diff options
author | Torne (Richard Coles) <torne@google.com> | 2013-10-22 16:41:35 +0100 |
---|---|---|
committer | Torne (Richard Coles) <torne@google.com> | 2013-10-22 16:41:35 +0100 |
commit | 8bcbed890bc3ce4d7a057a8f32cab53fa534672e (patch) | |
tree | 1390b6675d21328859f01f50203d9bde09105298 /chrome/browser | |
parent | 116fa16b45c9efe30e785b9fc32f09780ca23bec (diff) | |
download | chromium_org-8bcbed890bc3ce4d7a057a8f32cab53fa534672e.tar.gz |
Merge from Chromium at DEPS revision 230120
This commit was generated by merge_to_master.py.
Change-Id: I54bc06b7ee8a07092e74ce3b68c6893508349042
Diffstat (limited to 'chrome/browser')
1091 files changed, 29901 insertions, 14393 deletions
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index ec224ae8ab..7730526eb3 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc @@ -397,6 +397,15 @@ const Experiment kExperiments[] = { switches::kDisableAcceleratedOverflowScroll) }, { + "force-universal-accelerated-composited-scrolling", + IDS_FLAGS_FORCE_UNIVERSAL_ACCELERATED_OVERFLOW_SCROLL_MODE_NAME, + IDS_FLAGS_FORCE_UNIVERSAL_ACCELERATED_OVERFLOW_SCROLL_MODE_DESCRIPTION, + kOsAll, + ENABLE_DISABLE_VALUE_TYPE( + switches::kEnableUniversalAcceleratedOverflowScroll, + switches::kDisableUniversalAcceleratedOverflowScroll) + }, + { "present-with-GDI", IDS_FLAGS_PRESENT_WITH_GDI_NAME, IDS_FLAGS_PRESENT_WITH_GDI_DESCRIPTION, @@ -1499,7 +1508,9 @@ const Experiment kExperiments[] = { IDS_FLAGS_ENABLE_GOOGLE_NOW_INTEGRATION_NAME, IDS_FLAGS_ENABLE_GOOGLE_NOW_INTEGRATION_DESCRIPTION, kOsWin | kOsCrOS | kOsMac, - SINGLE_VALUE_TYPE(switches::kEnableGoogleNowIntegration) + ENABLE_DISABLE_VALUE_TYPE( + switches::kEnableGoogleNowIntegration, + switches::kDisableGoogleNowIntegration) }, #endif #if defined(OS_CHROMEOS) @@ -1643,6 +1654,13 @@ const Experiment kExperiments[] = { ENABLE_DISABLE_VALUE_TYPE(switches::kEnableStickyKeys, switches::kDisableStickyKeys) }, + { + "ash-enable-autoclick", + IDS_FLAGS_ENABLE_AUTOCLICK_NAME, + IDS_FLAGS_ENABLE_AUTOCLICK_DESCRIPTION, + kOsCrOS, + SINGLE_VALUE_TYPE(ash::switches::kAshEnableAutoclick) + }, #endif { "enable-web-midi", @@ -1659,6 +1677,13 @@ const Experiment kExperiments[] = { SINGLE_VALUE_TYPE(switches::kNewProfileManagement) }, { + "enable-inline-signin", + IDS_FLAGS_ENABLE_INLINE_SIGNIN_NAME, + IDS_FLAGS_ENABLE_INLINE_SIGNIN_DESCRIPTION, + kOsMac | kOsWin | kOsLinux, + SINGLE_VALUE_TYPE(switches::kEnableInlineSignin) + }, + { "enable-gaia-profile-info", IDS_FLAGS_ENABLE_GAIA_PROFILE_INFO_NAME, IDS_FLAGS_ENABLE_GAIA_PROFILE_INFO_DESCRIPTION, @@ -1667,10 +1692,10 @@ const Experiment kExperiments[] = { }, { "disable-app-launcher", - IDS_FLAGS_DISABLE_APP_LIST_NAME, - IDS_FLAGS_DISABLE_APP_LIST_DESCRIPTION, + IDS_FLAGS_RESET_APP_LIST_INSTALL_STATE_NAME, + IDS_FLAGS_RESET_APP_LIST_INSTALL_STATE_DESCRIPTION, kOsMac | kOsWin, - SINGLE_VALUE_TYPE(switches::kDisableAppList) + SINGLE_VALUE_TYPE(switches::kResetAppListInstallState) }, #if defined(ENABLE_APP_LIST) { @@ -1751,6 +1776,22 @@ const Experiment kExperiments[] = { SINGLE_VALUE_TYPE(cc::switches::kDisableCompositorTouchHitTesting), }, { + "enable-accelerated-scrollable-frames", + IDS_FLAGS_ENABLE_ACCELERATED_SCROLLABLE_FRAMES_NAME, + IDS_FLAGS_ENABLE_ACCELERATED_SCROLLABLE_FRAMES_DESCRIPTION, + kOsAll, + ENABLE_DISABLE_VALUE_TYPE(switches::kEnableAcceleratedScrollableFrames, + switches::kDisableAcceleratedScrollableFrames) + }, + { + "enable-composited-scrolling-for-frames", + IDS_FLAGS_ENABLE_COMPOSITED_SCROLLING_FOR_FRAMES_NAME, + IDS_FLAGS_ENABLE_COMPOSITED_SCROLLING_FOR_FRAMES_DESCRIPTION, + kOsAll, + ENABLE_DISABLE_VALUE_TYPE(switches::kEnableCompositedScrollingForFrames, + switches::kDisableCompositedScrollingForFrames) + }, + { "enable-streamlined-hosted-apps", IDS_FLAGS_ENABLE_STREAMLINED_HOSTED_APPS_NAME, IDS_FLAGS_ENABLE_STREAMLINED_HOSTED_APPS_DESCRIPTION, @@ -1758,6 +1799,13 @@ const Experiment kExperiments[] = { SINGLE_VALUE_TYPE(switches::kEnableStreamlinedHostedApps) }, { + "enable-ephemeral-apps", + IDS_FLAGS_ENABLE_EPHEMERAL_APPS_NAME, + IDS_FLAGS_ENABLE_EPHEMERAL_APPS_DESCRIPTION, + kOsWin | kOsCrOS, + SINGLE_VALUE_TYPE(switches::kEnableEphemeralApps) + }, + { "enable-service-worker", IDS_FLAGS_ENABLE_SERVICE_WORKER_NAME, IDS_FLAGS_ENABLE_SERVICE_WORKER_DESCRIPTION, diff --git a/chrome/browser/android/crash_dump_manager.cc b/chrome/browser/android/crash_dump_manager.cc deleted file mode 100644 index 5604f689af..0000000000 --- a/chrome/browser/android/crash_dump_manager.cc +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright (c) 2012 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/android/crash_dump_manager.h" - -#include "base/bind.h" -#include "base/file_util.h" -#include "base/format_macros.h" -#include "base/logging.h" -#include "base/path_service.h" -#include "base/posix/global_descriptors.h" -#include "base/process/process.h" -#include "base/rand_util.h" -#include "base/stl_util.h" -#include "base/strings/stringprintf.h" -#include "chrome/common/chrome_paths.h" -#include "chrome/common/descriptors_android.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/child_process_data.h" -#include "content/public/browser/file_descriptor_info.h" -#include "content/public/browser/notification_service.h" -#include "content/public/browser/notification_types.h" -#include "content/public/browser/render_process_host.h" - -using content::BrowserThread; - -// static -CrashDumpManager* CrashDumpManager::instance_ = NULL; - -// static -CrashDumpManager* CrashDumpManager::GetInstance() { - return instance_; -} - -CrashDumpManager::CrashDumpManager() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK(!instance_); - - instance_ = this; - - notification_registrar_.Add(this, - content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, - content::NotificationService::AllSources()); - notification_registrar_.Add(this, - content::NOTIFICATION_RENDERER_PROCESS_CLOSED, - content::NotificationService::AllSources()); - - BrowserChildProcessObserver::Add(this); -} - -CrashDumpManager::~CrashDumpManager() { - instance_ = NULL; - - BrowserChildProcessObserver::Remove(this); -} - -int CrashDumpManager::CreateMinidumpFile(int child_process_id) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER)); - base::FilePath minidump_path; - if (!file_util::CreateTemporaryFile(&minidump_path)) - return base::kInvalidPlatformFileValue; - - base::PlatformFileError error; - // We need read permission as the minidump is generated in several phases - // and needs to be read at some point. - int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ | - base::PLATFORM_FILE_WRITE; - base::PlatformFile minidump_file = - base::CreatePlatformFile(minidump_path, flags, NULL, &error); - if (minidump_file == base::kInvalidPlatformFileValue) { - LOG(ERROR) << "Failed to create temporary file, crash won't be reported."; - return base::kInvalidPlatformFileValue; - } - - { - base::AutoLock auto_lock(child_process_id_to_minidump_path_lock_); - DCHECK(!ContainsKey(child_process_id_to_minidump_path_, child_process_id)); - child_process_id_to_minidump_path_[child_process_id] = minidump_path; - } - return minidump_file; -} - -void CrashDumpManager::ProcessMinidump(const base::FilePath& minidump_path, - base::ProcessHandle pid) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - int64 file_size = 0; - int r = file_util::GetFileSize(minidump_path, &file_size); - DCHECK(r) << "Failed to retrieve size for minidump " - << minidump_path.value(); - - if (file_size == 0) { - // Empty minidump, this process did not crash. Just remove the file. - r = base::DeleteFile(minidump_path, false); - DCHECK(r) << "Failed to delete temporary minidump file " - << minidump_path.value(); - return; - } - - // We are dealing with a valid minidump. Copy it to the crash report - // directory from where Java code will upload it later on. - base::FilePath crash_dump_dir; - r = PathService::Get(chrome::DIR_CRASH_DUMPS, &crash_dump_dir); - if (!r) { - NOTREACHED() << "Failed to retrieve the crash dump directory."; - return; - } - - const uint64 rand = base::RandUint64(); - const std::string filename = - base::StringPrintf("chromium-renderer-minidump-%016" PRIx64 ".dmp%d", - rand, pid); - base::FilePath dest_path = crash_dump_dir.Append(filename); - r = base::Move(minidump_path, dest_path); - if (!r) { - LOG(ERROR) << "Failed to move crash dump from " << minidump_path.value() - << " to " << dest_path.value(); - base::DeleteFile(minidump_path, false); - return; - } - LOG(INFO) << "Crash minidump successfully generated: " << - crash_dump_dir.Append(filename).value(); -} - -void CrashDumpManager::BrowserChildProcessHostDisconnected( - const content::ChildProcessData& data) { - OnChildExit(data.id, data.handle); -} - -void CrashDumpManager::BrowserChildProcessCrashed( - const content::ChildProcessData& data) { - OnChildExit(data.id, data.handle); -} - -void CrashDumpManager::Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) { - switch (type) { - case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: - // NOTIFICATION_RENDERER_PROCESS_TERMINATED is sent when the renderer - // process is cleanly shutdown. However, we need to fallthrough so that - // we close the minidump_fd we kept open. - case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: { - content::RenderProcessHost* rph = - content::Source<content::RenderProcessHost>(source).ptr(); - OnChildExit(rph->GetID(), rph->GetHandle()); - break; - } - default: - NOTREACHED(); - return; - } -} - -void CrashDumpManager::OnChildExit(int child_process_id, - base::ProcessHandle pid) { - base::FilePath minidump_path; - { - base::AutoLock auto_lock(child_process_id_to_minidump_path_lock_); - ChildProcessIDToMinidumpPath::iterator iter = - child_process_id_to_minidump_path_.find(child_process_id); - if (iter == child_process_id_to_minidump_path_.end()) { - // We might get a NOTIFICATION_RENDERER_PROCESS_TERMINATED and a - // NOTIFICATION_RENDERER_PROCESS_CLOSED. - return; - } - minidump_path = iter->second; - child_process_id_to_minidump_path_.erase(iter); - } - BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, - base::Bind(&CrashDumpManager::ProcessMinidump, minidump_path, pid)); -} diff --git a/chrome/browser/android/crash_dump_manager.h b/chrome/browser/android/crash_dump_manager.h deleted file mode 100644 index 16c24a00bb..0000000000 --- a/chrome/browser/android/crash_dump_manager.h +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) 2012 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_ANDROID_CRASH_DUMP_MANAGER_H_ -#define CHROME_BROWSER_ANDROID_CRASH_DUMP_MANAGER_H_ - -#include <map> - -#include "base/files/file_path.h" -#include "base/platform_file.h" -#include "base/process/process.h" -#include "base/synchronization/lock.h" -#include "content/public/browser/browser_child_process_observer.h" -#include "content/public/browser/notification_observer.h" -#include "content/public/browser/notification_registrar.h" - -namespace content { -class RenderProcessHost; -} - -// This class manages the crash minidumps. -// On Android, because of process isolation, each renderer process runs with a -// different UID. As a result, we cannot generate the minidumps in the browser -// (as the browser process does not have access to some system files for the -// crashed process). So the minidump is generated in the renderer process. -// Since the isolated process cannot open files, we provide it on creation with -// a file descriptor where to write the minidump in the event of a crash. -// This class creates these file descriptors and associates them with render -// processes and take the appropriate action when the render process terminates. -class CrashDumpManager : public content::BrowserChildProcessObserver, - public content::NotificationObserver { - public: - // This object is a singleton created and owned by the - // ChromeBrowserMainPartsAndroid. - static CrashDumpManager* GetInstance(); - - virtual ~CrashDumpManager(); - - // Returns a file descriptor that should be used to generate a minidump for - // the process |child_process_id|. - int CreateMinidumpFile(int child_process_id); - - private: - friend class ChromeBrowserMainPartsAndroid; - - // Should be created on the UI thread. - CrashDumpManager(); - - typedef std::map<int, base::FilePath> ChildProcessIDToMinidumpPath; - - static void ProcessMinidump(const base::FilePath& minidump_path, - base::ProcessHandle pid); - - // content::BrowserChildProcessObserver implementation: - virtual void BrowserChildProcessHostDisconnected( - const content::ChildProcessData& data) OVERRIDE; - virtual void BrowserChildProcessCrashed( - const content::ChildProcessData& data) OVERRIDE; - - // NotificationObserver implementation: - virtual void Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) OVERRIDE; - - // Called on child process exit (including crash). - void OnChildExit(int child_process_id, base::ProcessHandle pid); - - content::NotificationRegistrar notification_registrar_; - - // This map should only be accessed with its lock aquired as it is accessed - // from the PROCESS_LAUNCHER and UI threads. - base::Lock child_process_id_to_minidump_path_lock_; - ChildProcessIDToMinidumpPath child_process_id_to_minidump_path_; - - static CrashDumpManager* instance_; - - DISALLOW_COPY_AND_ASSIGN(CrashDumpManager); -}; - -#endif // CHROME_BROWSER_ANDROID_CRASH_DUMP_MANAGER_H_ diff --git a/chrome/browser/android/dev_tools_server.cc b/chrome/browser/android/dev_tools_server.cc index 87014f8dc0..a716d0d82a 100644 --- a/chrome/browser/android/dev_tools_server.cc +++ b/chrome/browser/android/dev_tools_server.cc @@ -15,7 +15,9 @@ #include "base/compiler_specific.h" #include "base/logging.h" #include "base/strings/stringprintf.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" +#include "chrome/browser/android/tab_android.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/devtools/devtools_adb_bridge.h" #include "chrome/browser/history/top_sites.h" @@ -30,7 +32,6 @@ #include "content/public/browser/devtools_target.h" #include "content/public/browser/favicon_status.h" #include "content/public/browser/navigation_entry.h" -#include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_delegate.h" #include "content/public/common/content_switches.h" @@ -44,7 +45,6 @@ #include "webkit/common/user_agent/user_agent_util.h" using content::DevToolsAgentHost; -using content::RenderViewHost; using content::WebContents; namespace { @@ -56,9 +56,36 @@ const char kTetheringSocketName[] = "chrome_devtools_tethering_%d_%d"; const char kTargetTypePage[] = "page"; +bool FindTab(const std::string& str_id, + TabModel** model_result, + int* index_result) { + int id; + if (!base::StringToInt(str_id, &id)) + return false; + + for (TabModelList::const_iterator iter = TabModelList::begin(); + iter != TabModelList::end(); ++iter) { + TabModel* model = *iter; + for (int i = 0; i < model->GetTabCount(); ++i) { + TabAndroid* tab = model->GetTabAt(i); + if (id == tab->GetAndroidId()) { + *model_result = model; + *index_result = i; + return true; + } + } + } + return false; +} + + class Target : public content::DevToolsTarget { public: - explicit Target(WebContents* web_contents); + // Constructor for a tab with a valid WebContents. + Target(int id, WebContents* web_contents); + + // Constructor for a tab unloaded from memory. + Target(int id, const string16& title, const GURL& url); virtual std::string GetId() const OVERRIDE { return id_; } virtual std::string GetType() const OVERRIDE { return kTargetTypePage; } @@ -69,17 +96,12 @@ class Target : public content::DevToolsTarget { virtual base::TimeTicks GetLastActivityTime() const OVERRIDE { return last_activity_time_; } - virtual bool IsAttached() const OVERRIDE { - return agent_host_->IsAttached(); - } - virtual scoped_refptr<DevToolsAgentHost> GetAgentHost() const OVERRIDE { - return agent_host_; - } + virtual bool IsAttached() const OVERRIDE; + virtual scoped_refptr<DevToolsAgentHost> GetAgentHost() const OVERRIDE; virtual bool Activate() const OVERRIDE; virtual bool Close() const OVERRIDE; private: - scoped_refptr<DevToolsAgentHost> agent_host_; std::string id_; std::string title_; GURL url_; @@ -87,10 +109,8 @@ class Target : public content::DevToolsTarget { base::TimeTicks last_activity_time_; }; -Target::Target(WebContents* web_contents) { - agent_host_ = - DevToolsAgentHost::GetOrCreateFor(web_contents->GetRenderViewHost()); - id_ = agent_host_->GetId(); +Target::Target(int id, WebContents* web_contents) { + id_ = base::IntToString(id); title_ = UTF16ToUTF8(net::EscapeForHTML(web_contents->GetTitle())); url_ = web_contents->GetURL(); content::NavigationController& controller = web_contents->GetController(); @@ -100,22 +120,56 @@ Target::Target(WebContents* web_contents) { last_activity_time_ = web_contents->GetLastSelectedTime(); } -bool Target::Activate() const { - RenderViewHost* rvh = agent_host_->GetRenderViewHost(); - if (!rvh) +Target::Target(int id, const string16& title, const GURL& url) { + id_ = base::IntToString(id); + title_ = UTF16ToUTF8(net::EscapeForHTML(title)); + url_ = url; +} + +bool Target::IsAttached() const { + TabModel* model; + int index; + if (!FindTab(id_, &model, &index)) return false; - WebContents* web_contents = WebContents::FromRenderViewHost(rvh); + WebContents* web_contents = model->GetWebContentsAt(index); if (!web_contents) return false; - web_contents->GetDelegate()->ActivateContents(web_contents); + return DevToolsAgentHost::IsDebuggerAttached(web_contents); +} + +scoped_refptr<DevToolsAgentHost> Target::GetAgentHost() const { + TabModel* model; + int index; + if (!FindTab(id_, &model, &index)) + return NULL; + WebContents* web_contents = model->GetWebContentsAt(index); + if (!web_contents) { + // The tab has been pushed out of memory, pull it back. + TabAndroid* tab = model->GetTabAt(index); + tab->RestoreIfNeeded(); + web_contents = model->GetWebContentsAt(index); + if (!web_contents) + return NULL; + } + content::RenderViewHost* rvh = web_contents->GetRenderViewHost(); + return rvh ? DevToolsAgentHost::GetOrCreateFor(rvh) : NULL; +} + +bool Target::Activate() const { + TabModel* model; + int index; + if (!FindTab(id_, &model, &index)) + return false; + model->SetActiveIndex(index); return true; } bool Target::Close() const { - RenderViewHost* rvh = agent_host_->GetRenderViewHost(); - if (!rvh) + TabModel* model; + int index; + if (!FindTab(id_, &model, &index)) return false; - rvh->ClosePage(); + model->CloseTabAt(index); return true; } @@ -169,18 +223,32 @@ class DevToolsServerDelegate : public content::DevToolsHttpHandlerDelegate { tab_model->CreateTabForTesting(GURL(content::kAboutBlankURL)); if (!web_contents) return scoped_ptr<content::DevToolsTarget>(); - return scoped_ptr<content::DevToolsTarget>(new Target(web_contents)); + + for (int i = 0; i < tab_model->GetTabCount(); ++i) { + if (web_contents != tab_model->GetWebContentsAt(i)) + continue; + TabAndroid* tab = tab_model->GetTabAt(i); + return scoped_ptr<content::DevToolsTarget>( + new Target(tab->GetAndroidId(), web_contents)); + } + + return scoped_ptr<content::DevToolsTarget>(); } virtual void EnumerateTargets(TargetCallback callback) OVERRIDE { TargetList targets; - std::vector<RenderViewHost*> rvh_list = - DevToolsAgentHost::GetValidRenderViewHosts(); - for (std::vector<RenderViewHost*>::iterator it = rvh_list.begin(); - it != rvh_list.end(); ++it) { - WebContents* web_contents = WebContents::FromRenderViewHost(*it); - if (web_contents) - targets.push_back(new Target(web_contents)); + for (TabModelList::const_iterator iter = TabModelList::begin(); + iter != TabModelList::end(); ++iter) { + TabModel* model = *iter; + for (int i = 0; i < model->GetTabCount(); ++i) { + TabAndroid* tab = model->GetTabAt(i); + WebContents* web_contents = model->GetWebContentsAt(i); + targets.push_back( + web_contents ? + new Target(tab->GetAndroidId(), web_contents) : + new Target( + tab->GetAndroidId(), tab->GetTitle(), tab->GetURL())); + } } callback.Run(targets); } diff --git a/chrome/browser/android/tab_android.cc b/chrome/browser/android/tab_android.cc index dcf9a93789..c63c105bfb 100644 --- a/chrome/browser/android/tab_android.cc +++ b/chrome/browser/android/tab_android.cc @@ -135,7 +135,6 @@ TabAndroid* TabAndroid::GetNativeTab(JNIEnv* env, jobject obj) { TabAndroid::TabAndroid(JNIEnv* env, jobject obj) : weak_java_tab_(env, obj), session_tab_id_(), - android_tab_id_(-1), synced_tab_delegate_(new browser_sync::SyncedTabDelegateAndroid(this)) { Java_TabBase_setNativePtr(env, obj, reinterpret_cast<jint>(this)); } @@ -149,6 +148,40 @@ TabAndroid::~TabAndroid() { Java_TabBase_clearNativePtr(env, obj.obj()); } +int TabAndroid::GetAndroidId() const { + JNIEnv* env = base::android::AttachCurrentThread(); + ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env); + if (obj.is_null()) + return -1; + return Java_TabBase_getId(env, obj.obj()); +} + +string16 TabAndroid::GetTitle() const { + JNIEnv* env = base::android::AttachCurrentThread(); + ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env); + if (obj.is_null()) + return string16(); + return base::android::ConvertJavaStringToUTF16( + Java_TabBase_getTitle(env, obj.obj())); +} + +GURL TabAndroid::GetURL() const { + JNIEnv* env = base::android::AttachCurrentThread(); + ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env); + if (obj.is_null()) + return GURL::EmptyGURL(); + return GURL(base::android::ConvertJavaStringToUTF8( + Java_TabBase_getUrl(env, obj.obj()))); +} + +bool TabAndroid::RestoreIfNeeded() { + JNIEnv* env = base::android::AttachCurrentThread(); + ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env); + if (obj.is_null()) + return false; + return Java_TabBase_restoreIfNeeded(env, obj.obj()); +} + content::ContentViewCore* TabAndroid::GetContentViewCore() const { if (!web_contents()) return NULL; @@ -224,12 +257,9 @@ void TabAndroid::Observe(int type, void TabAndroid::InitWebContents(JNIEnv* env, jobject obj, - jint tab_id, jboolean incognito, jobject jcontent_view_core, jobject jweb_contents_delegate) { - android_tab_id_ = tab_id; - content::ContentViewCore* content_view_core = content::ContentViewCore::GetNativeContentViewCore(env, jcontent_view_core); diff --git a/chrome/browser/android/tab_android.h b/chrome/browser/android/tab_android.h index e3a6c29ff3..31e326637c 100644 --- a/chrome/browser/android/tab_android.h +++ b/chrome/browser/android/tab_android.h @@ -56,7 +56,16 @@ class TabAndroid : public CoreTabHelperDelegate, // Return specific id information regarding this TabAndroid. const SessionID& session_id() const { return session_tab_id_; } - int android_id() const { return android_tab_id_; } + int GetAndroidId() const; + + // Return the tab title. + string16 GetTitle() const; + + // Return the tab url. + GURL GetURL() const; + + // Restore the tab if it was unloaded from memory. + bool RestoreIfNeeded(); // Helper methods to make it easier to access objects from the associated // WebContents. Can return NULL. @@ -123,10 +132,10 @@ class TabAndroid : public CoreTabHelperDelegate, virtual void InitWebContents(JNIEnv* env, jobject obj, - jint tab_id, jboolean incognito, jobject jcontent_view_core, jobject jweb_contents_delegate); + virtual void DestroyWebContents(JNIEnv* env, jobject obj, jboolean delete_native); @@ -146,7 +155,6 @@ class TabAndroid : public CoreTabHelperDelegate, JavaObjectWeakGlobalRef weak_java_tab_; SessionID session_tab_id_; - int android_tab_id_; content::NotificationRegistrar notification_registrar_; diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm index 34736e6251..3895ceb51d 100644 --- a/chrome/browser/app_controller_mac.mm +++ b/chrome/browser/app_controller_mac.mm @@ -760,8 +760,9 @@ class AppControllerProfileObserver : public ProfileInfoCacheObserver { DownloadManager* download_manager = (download_service->HasCreatedDownloadManager() ? BrowserContext::GetDownloadManager(profiles[i]) : NULL); - if (download_manager && download_manager->InProgressCount() > 0) { - int downloadCount = download_manager->InProgressCount(); + if (download_manager && + download_manager->NonMaliciousInProgressCount() > 0) { + int downloadCount = download_manager->NonMaliciousInProgressCount(); if ([self userWillWaitForInProgressDownloads:downloadCount]) { // Create a new browser window (if necessary) and navigate to the // downloads page if the user chooses to wait. diff --git a/chrome/browser/apps/app_browsertest.cc b/chrome/browser/apps/app_browsertest.cc index e93de75ec4..b47ffd2868 100644 --- a/chrome/browser/apps/app_browsertest.cc +++ b/chrome/browser/apps/app_browsertest.cc @@ -51,8 +51,8 @@ #include "chrome/browser/chromeos/login/mock_user_manager.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_dbus_thread_manager.h" #include "chromeos/dbus/fake_power_manager_client.h" -#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" #endif using apps::ShellWindow; @@ -187,7 +187,9 @@ bool CopyTestDataAndSetCommandLineArg( return true; } +#if !defined(OS_CHROMEOS) const char kTestFilePath[] = "platform_apps/launch_files/test.txt"; +#endif } // namespace @@ -1219,7 +1221,8 @@ IN_PROC_BROWSER_TEST_F(PlatformAppIncognitoBrowserTest, IncognitoComponentApp) { ASSERT_TRUE(registry != NULL); registry->AddObserver(this); - OpenApplication(AppLaunchParams(incognito_profile, file_manager, 0)); + OpenApplication(AppLaunchParams( + incognito_profile, file_manager, 0, chrome::HOST_DESKTOP_TYPE_NATIVE)); while (!ContainsKey(opener_app_ids_, file_manager->id())) { content::RunAllPendingInMessageLoop(); @@ -1237,8 +1240,8 @@ class RestartDeviceTest : public PlatformAppBrowserTest { virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { PlatformAppBrowserTest::SetUpInProcessBrowserTestFixture(); - chromeos::MockDBusThreadManagerWithoutGMock* dbus_manager = - new chromeos::MockDBusThreadManagerWithoutGMock; + chromeos::FakeDBusThreadManager* dbus_manager = + new chromeos::FakeDBusThreadManager; chromeos::DBusThreadManager::InitializeForTesting(dbus_manager); power_manager_client_ = dbus_manager->fake_power_manager_client(); } diff --git a/chrome/browser/apps/app_browsertest_util.cc b/chrome/browser/apps/app_browsertest_util.cc index 6b75b5ce41..24a161cd81 100644 --- a/chrome/browser/apps/app_browsertest_util.cc +++ b/chrome/browser/apps/app_browsertest_util.cc @@ -16,6 +16,7 @@ #include "chrome/browser/ui/extensions/application_launch.h" #include "chrome/common/chrome_switches.h" #include "content/public/browser/notification_service.h" +#include "content/public/test/browser_test_utils.h" #include "content/public/test/test_utils.h" #include "extensions/common/switches.h" @@ -40,6 +41,21 @@ void PlatformAppBrowserTest::SetUpCommandLine(CommandLine* command_line) { command_line->AppendSwitchASCII(::switches::kEventPageSuspendingTime, "1"); } +// static +ShellWindow* PlatformAppBrowserTest::GetFirstShellWindowForBrowser( + Browser* browser) { + ShellWindowRegistry* app_registry = + ShellWindowRegistry::Get(browser->profile()); + const ShellWindowRegistry::ShellWindowList& shell_windows = + app_registry->shell_windows(); + + ShellWindowRegistry::const_iterator iter = shell_windows.begin(); + if (iter != shell_windows.end()) + return *iter; + + return NULL; +} + const Extension* PlatformAppBrowserTest::LoadAndLaunchPlatformApp( const char* name) { content::WindowedNotificationObserver app_loaded_observer( @@ -96,16 +112,7 @@ WebContents* PlatformAppBrowserTest::GetFirstShellWindowWebContents() { } ShellWindow* PlatformAppBrowserTest::GetFirstShellWindow() { - ShellWindowRegistry* app_registry = - ShellWindowRegistry::Get(browser()->profile()); - const ShellWindowRegistry::ShellWindowList& shell_windows = - app_registry->shell_windows(); - - ShellWindowRegistry::const_iterator iter = shell_windows.begin(); - if (iter != shell_windows.end()) - return *iter; - - return NULL; + return GetFirstShellWindowForBrowser(browser()); } size_t PlatformAppBrowserTest::RunGetWindowsFunctionForExtension( @@ -171,11 +178,10 @@ ShellWindow* PlatformAppBrowserTest::CreateShellWindowFromParams( } void PlatformAppBrowserTest::CloseShellWindow(ShellWindow* window) { - content::WindowedNotificationObserver destroyed_observer( - content::NOTIFICATION_WEB_CONTENTS_DESTROYED, - content::NotificationService::AllSources()); + content::WebContentsDestroyedWatcher destroyed_watcher( + window->web_contents()); window->GetBaseWindow()->Close(); - destroyed_observer.Wait(); + destroyed_watcher.Wait(); } void PlatformAppBrowserTest::CallAdjustBoundsToBeVisibleOnScreenForShellWindow( diff --git a/chrome/browser/apps/app_browsertest_util.h b/chrome/browser/apps/app_browsertest_util.h index 2b9d958f92..72e314d4ad 100644 --- a/chrome/browser/apps/app_browsertest_util.h +++ b/chrome/browser/apps/app_browsertest_util.h @@ -14,6 +14,7 @@ namespace content { class WebContents; } +class Browser; class CommandLine; namespace extensions { @@ -25,6 +26,9 @@ class PlatformAppBrowserTest : public ExtensionApiTest { virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE; + // Gets the first shell window that is found for a given browser. + static apps::ShellWindow* GetFirstShellWindowForBrowser(Browser* browser); + protected: // Runs the app named |name| out of the platform_apps subdirectory. Waits // until it is launched. diff --git a/chrome/browser/apps/web_view_browsertest.cc b/chrome/browser/apps/web_view_browsertest.cc index ae0db65808..09f23521ca 100644 --- a/chrome/browser/apps/web_view_browsertest.cc +++ b/chrome/browser/apps/web_view_browsertest.cc @@ -680,6 +680,13 @@ IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestChromeExtensionURL) { "web_view/shim"); } +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestInvalidChromeExtensionURL) { + TestHelper("testInvalidChromeExtensionURL", + "DoneShimTest.PASSED", + "DoneShimTest.FAILED", + "web_view/shim"); +} + IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestEventName) { TestHelper("testEventName", "DoneShimTest.PASSED", @@ -855,8 +862,17 @@ IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestWebRequestAPIGoogleProperty) { "web_view/shim"); } -IN_PROC_BROWSER_TEST_F(WebViewTest, - Shim_TestWebRequestListenerSurvivesReparenting) { +// This test is disabled due to being flaky. http://crbug.com/309451 +#if defined(OS_WIN) +#define MAYBE_Shim_TestWebRequestListenerSurvivesReparenting \ + DISABLED_Shim_TestWebRequestListenerSurvivesReparenting +#else +#define MAYBE_Shim_TestWebRequestListenerSurvivesReparenting \ + Shim_TestWebRequestListenerSurvivesReparenting +#endif +IN_PROC_BROWSER_TEST_F( + WebViewTest, + MAYBE_Shim_TestWebRequestListenerSurvivesReparenting) { TestHelper("testWebRequestListenerSurvivesReparenting", "DoneShimTest.PASSED", "DoneShimTest.FAILED", @@ -958,9 +974,8 @@ IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestRemoveWebviewOnExit) { ASSERT_TRUE(guest_loaded_listener.WaitUntilSatisfied()); - content::WindowedNotificationObserver observer( - content::NOTIFICATION_WEB_CONTENTS_DESTROYED, - content::Source<content::WebContents>(source->GetWebContents())); + content::WebContentsDestroyedWatcher destroyed_watcher( + source->GetWebContents()); // Tell the embedder to kill the guest. EXPECT_TRUE(content::ExecuteScript( @@ -968,7 +983,7 @@ IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestRemoveWebviewOnExit) { "removeWebviewOnExitDoCrash();")); // Wait until the guest WebContents is destroyed. - observer.Wait(); + destroyed_watcher.Wait(); } // Remove <webview> immediately after navigating it. diff --git a/chrome/browser/apps/web_view_interactive_browsertest.cc b/chrome/browser/apps/web_view_interactive_browsertest.cc index 1be9879148..e62c07ecab 100644 --- a/chrome/browser/apps/web_view_interactive_browsertest.cc +++ b/chrome/browser/apps/web_view_interactive_browsertest.cc @@ -370,8 +370,11 @@ class WebViewInteractiveTest void DragTestStep1() { // Move mouse to start of text. MoveMouseInsideWindow(gfx::Point(45, 8)); + MoveMouseInsideWindow(gfx::Point(45, 9)); SendMouseEvent(ui_controls::LEFT, ui_controls::DOWN); - MoveMouseInsideWindow(gfx::Point(76, 12)); + + MoveMouseInsideWindow(gfx::Point(74, 12)); + MoveMouseInsideWindow(gfx::Point(78, 12)); // Now wait a bit before moving mouse to initiate drag/drop. base::MessageLoop::current()->PostDelayedTask( @@ -400,18 +403,20 @@ class WebViewInteractiveTest // This is because of the nature of drag and drop code (esp. the // MessageLoop) in it. - // Now verify we got a drop and correct drop data. + // Now check if we got a drop and read the drop data. + embedder_web_contents_ = GetFirstShellWindowWebContents(); ExtensionTestMessageListener drop_listener("guest-got-drop", false); - EXPECT_TRUE(content::ExecuteScript(guest_web_contents_, - "window.pingEmbedder()")); + EXPECT_TRUE(content::ExecuteScript(embedder_web_contents_, + "window.checkIfGuestGotDrop()")); EXPECT_TRUE(drop_listener.WaitUntilSatisfied()); std::string last_drop_data; EXPECT_TRUE(content::ExecuteScriptAndExtractString( - embedder_web_contents_, - "window.domAutomationController.send(getLastDropData())", - &last_drop_data)); - EXPECT_EQ(last_drop_data, "Drop me"); + embedder_web_contents_, + "window.domAutomationController.send(getLastDropData())", + &last_drop_data)); + + last_drop_data_ = last_drop_data; } protected: @@ -422,6 +427,7 @@ class WebViewInteractiveTest bool first_click_; // Only used in drag/drop test. base::Closure quit_closure_; + std::string last_drop_data_; }; // ui_test_utils::SendMouseMoveSync doesn't seem to work on OS_MACOSX, and @@ -678,24 +684,48 @@ IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, DISABLED_PopupPositioningMoved) { // Drag and drop inside a webview is currently only enabled for linux and mac, // but the tests don't work on anything except chromeos for now. This is because // of simulating mouse drag code's dependency on platforms. -// -// Disabled because of flake on CrOS: http://crbug.com/161112 #if defined(OS_CHROMEOS) +// This test is flaky. See crbug.com/309032 IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, DISABLED_DragDropWithinWebView) { - SetupTest( - "web_view/dnd_within_webview", - "/extensions/platform_apps/web_view/dnd_within_webview/guest.html"); + ExtensionTestMessageListener guest_connected_listener("connected", false); + LoadAndLaunchPlatformApp("web_view/dnd_within_webview"); + ASSERT_TRUE(guest_connected_listener.WaitUntilSatisfied()); + ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(GetPlatformAppWindow())); + gfx::Rect offset; + embedder_web_contents_ = GetFirstShellWindowWebContents(); + embedder_web_contents_->GetView()->GetContainerBounds(&offset); + corner_ = gfx::Point(offset.x(), offset.y()); + + // In the drag drop test we add 20px padding to the page body because on + // windows if we get too close to the edge of the window the resize cursor + // appears and we start dragging the window edge. + corner_.Offset(20, 20); + // Flush any pending events to make sure we start with a clean slate. content::RunAllPendingInMessageLoop(); - base::RunLoop run_loop; - quit_closure_ = run_loop.QuitClosure(); - base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(&WebViewInteractiveTest::DragTestStep1, - base::Unretained(this))); - run_loop.Run(); + for (;;) { + base::RunLoop run_loop; + quit_closure_ = run_loop.QuitClosure(); + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&WebViewInteractiveTest::DragTestStep1, + base::Unretained(this))); + run_loop.Run(); + + if (last_drop_data_ == "Drop me") + break; + + LOG(INFO) << "Drag was cancelled in interactive_test, restarting drag"; + + // Reset state for next try. + ExtensionTestMessageListener reset_listener("resetStateReply", false); + EXPECT_TRUE(content::ExecuteScript(embedder_web_contents_, + "window.resetState()")); + ASSERT_TRUE(reset_listener.WaitUntilSatisfied()); + } + ASSERT_EQ("Drop me", last_drop_data_); } #endif // (defined(OS_CHROMEOS)) diff --git a/chrome/browser/autocomplete/autocomplete_result.cc b/chrome/browser/autocomplete/autocomplete_result.cc index 95bee29ba2..54ced3fa18 100644 --- a/chrome/browser/autocomplete/autocomplete_result.cc +++ b/chrome/browser/autocomplete/autocomplete_result.cc @@ -179,8 +179,18 @@ void AutocompleteResult::SortAndCull(const AutocompleteInput& input, matches_.resize(num_matches); default_match_ = matches_.begin(); - DCHECK((default_match_ == matches_.end()) || - default_match_->allowed_to_be_default_match); + + if (default_match_ != matches_.end()) { + DCHECK(default_match_->allowed_to_be_default_match); + // We shouldn't get query matches for URL inputs, or non-query matches + // for query inputs. + if (AutocompleteMatch::IsSearchType(default_match_->type)) { + DCHECK_NE(AutocompleteInput::URL, input.type()); + } else { + DCHECK_NE(AutocompleteInput::QUERY, input.type()); + DCHECK_NE(AutocompleteInput::FORCED_QUERY, input.type()); + } + } // Set the alternate nav URL. alternate_nav_url_ = (default_match_ == matches_.end()) ? diff --git a/chrome/browser/autocomplete/extension_app_provider.cc b/chrome/browser/autocomplete/extension_app_provider.cc index 468839d9d1..56f0fd095d 100644 --- a/chrome/browser/autocomplete/extension_app_provider.cc +++ b/chrome/browser/autocomplete/extension_app_provider.cc @@ -12,6 +12,7 @@ #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system_factory.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/history/history_service.h" #include "chrome/browser/history/history_service_factory.h" #include "chrome/browser/history/url_database.h" @@ -161,7 +162,7 @@ void ExtensionAppProvider::RefreshAppList() { // provider is currently only used in the app launcher. if (profile_->IsOffTheRecord() && - !extension_service->CanLoadInIncognito(app)) + !extension_util::CanLoadInIncognito(app, extension_service)) continue; GURL launch_url = app->is_platform_app() ? diff --git a/chrome/browser/autocomplete/history_provider_util.cc b/chrome/browser/autocomplete/history_provider_util.cc index b7c8d458cb..371d7b948c 100644 --- a/chrome/browser/autocomplete/history_provider_util.cc +++ b/chrome/browser/autocomplete/history_provider_util.cc @@ -12,7 +12,8 @@ HistoryMatch::HistoryMatch() : url_info(), input_location(string16::npos), match_in_scheme(false), - innermost_match(true) { + innermost_match(true), + promoted(false) { } HistoryMatch::HistoryMatch(const URLRow& url_info, @@ -22,7 +23,8 @@ HistoryMatch::HistoryMatch(const URLRow& url_info, : url_info(url_info), input_location(input_location), match_in_scheme(match_in_scheme), - innermost_match(innermost_match) { + innermost_match(innermost_match), + promoted(false) { } bool HistoryMatch::EqualsGURL(const HistoryMatch& h, const GURL& url) { diff --git a/chrome/browser/autocomplete/history_provider_util.h b/chrome/browser/autocomplete/history_provider_util.h index 866a477bc8..d6b04da534 100644 --- a/chrome/browser/autocomplete/history_provider_util.h +++ b/chrome/browser/autocomplete/history_provider_util.h @@ -50,6 +50,10 @@ struct HistoryMatch { // "x", no scheme in our prefix list (or "www.") begins with x, so all // matches are, vacuously, "innermost matches". bool innermost_match; + + // When sorting, all promoted matches should appear before all non-promoted + // matches, regardless of other properties of the match. + bool promoted; }; typedef std::deque<HistoryMatch> HistoryMatches; diff --git a/chrome/browser/autocomplete/history_url_provider.cc b/chrome/browser/autocomplete/history_url_provider.cc index a29f827111..05e00176d9 100644 --- a/chrome/browser/autocomplete/history_url_provider.cc +++ b/chrome/browser/autocomplete/history_url_provider.cc @@ -48,7 +48,9 @@ namespace { // // It's OK to call this function with both |create_if_necessary| and // |promote| false, in which case we'll do nothing. -void CreateOrPromoteMatch(const history::URLRow& info, +// +// Returns whether the match exists regardless if it was promoted/created. +bool CreateOrPromoteMatch(const history::URLRow& info, size_t input_location, bool match_in_scheme, history::HistoryMatches* matches, @@ -61,12 +63,12 @@ void CreateOrPromoteMatch(const history::URLRow& info, // Rotate it to the front if the caller wishes. if (promote) std::rotate(matches->begin(), i, i + 1); - return; + return true; } } if (!create_if_necessary) - return; + return false; // No entry, so create one. history::HistoryMatch match(info, input_location, match_in_scheme, true); @@ -74,6 +76,8 @@ void CreateOrPromoteMatch(const history::URLRow& info, matches->push_front(match); else matches->push_back(match); + + return true; } // Given the user's |input| and a |match| created from it, reduce the match's @@ -105,6 +109,10 @@ GURL ConvertToHostOnly(const history::HistoryMatch& match, // Acts like the > operator for URLInfo classes. bool CompareHistoryMatch(const history::HistoryMatch& a, const history::HistoryMatch& b) { + // A promoted match is better than non-promoted. + if (a.promoted != b.promoted) + return a.promoted; + // A URL that has been typed at all is better than one that has never been // typed. (Note "!"s on each side) if (!a.url_info.typed_count() != !b.url_info.typed_count()) @@ -132,6 +140,41 @@ bool CompareHistoryMatch(const history::HistoryMatch& a, return a.url_info.last_visit() > b.url_info.last_visit(); } +// Sorts and dedups the given list of matches. +void SortAndDedupMatches(history::HistoryMatches* matches) { + // Sort by quality, best first. + std::sort(matches->begin(), matches->end(), &CompareHistoryMatch); + + // Remove duplicate matches (caused by the search string appearing in one of + // the prefixes as well as after it). Consider the following scenario: + // + // User has visited "http://http.com" once and "http://htaccess.com" twice. + // User types "http". The autocomplete search with prefix "http://" returns + // the first host, while the search with prefix "" returns both hosts. Now + // we sort them into rank order: + // http://http.com (innermost_match) + // http://htaccess.com (!innermost_match, url_info.visit_count == 2) + // http://http.com (!innermost_match, url_info.visit_count == 1) + // + // The above scenario tells us we can't use std::unique(), since our + // duplicates are not always sequential. It also tells us we should remove + // the lower-quality duplicate(s), since otherwise the returned results won't + // be ordered correctly. This is easy to do: we just always remove the later + // element of a duplicate pair. + // Be careful! Because the vector contents may change as we remove elements, + // we use an index instead of an iterator in the outer loop, and don't + // precalculate the ending position. + for (size_t i = 0; i < matches->size(); ++i) { + for (history::HistoryMatches::iterator j(matches->begin() + i + 1); + j != matches->end(); ) { + if ((*matches)[i].url_info.url() == j->url_info.url()) + j = matches->erase(j); + else + ++j; + } + } +} + // Extracts typed_count, visit_count, and last_visited time from the // URLRow and puts them in the additional info field of the |match| // for display in about:omnibox. @@ -490,8 +533,8 @@ void HistoryURLProvider::DoAutocomplete(history::HistoryBackend* backend, } // Create sorted list of suggestions. - CullPoorMatches(&history_matches, params); - SortMatches(&history_matches); + CullPoorMatches(*params, &history_matches); + SortAndDedupMatches(&history_matches); PromoteOrCreateShorterSuggestion(db, *params, have_what_you_typed_match, what_you_typed_match, &history_matches); @@ -512,7 +555,7 @@ void HistoryURLProvider::DoAutocomplete(history::HistoryBackend* backend, params->matches.push_back(what_you_typed_match); } else if (params->prevent_inline_autocomplete || history_matches.empty() || - !PromoteMatchForInlineAutocomplete(params, history_matches.front())) { + !PromoteMatchForInlineAutocomplete(history_matches.front(), params)) { // Failed to promote any URLs for inline autocompletion. Use the What You // Typed match, if we have it. first_match = 0; @@ -555,7 +598,7 @@ void HistoryURLProvider::DoAutocomplete(history::HistoryBackend* backend, // less than the previous match. relevance = (relevance > 0) ? (relevance - 1) : CalculateRelevance(NORMAL, history_matches.size() - 1 - i); - AutocompleteMatch ac_match = HistoryMatchToACMatch(params, match, + AutocompleteMatch ac_match = HistoryMatchToACMatch(*params, match, NORMAL, relevance); params->matches.push_back(ac_match); } @@ -809,17 +852,19 @@ bool HistoryURLProvider::CanFindIntranetURL( } bool HistoryURLProvider::PromoteMatchForInlineAutocomplete( - HistoryURLProviderParams* params, - const history::HistoryMatch& match) { - // Promote the first match if it's been typed at least n times, where n == 1 - // for "simple" (host-only) URLs and n == 2 for others. We set a higher bar - // for these long URLs because it's less likely that users will want to visit - // them again. Even though we don't increment the typed_count for pasted-in - // URLs, if the user manually edits the URL or types some long thing in by - // hand, we wouldn't want to immediately start autocompleting it. - if (!match.url_info.typed_count() || - ((match.url_info.typed_count() == 1) && - !match.IsHostOnly())) + const history::HistoryMatch& match, + HistoryURLProviderParams* params) { + // Promote the first match if it's been marked for promotion or typed at least + // n times, where n == 1 for "simple" (host-only) URLs and n == 2 for others. + // We set a higher bar for these long URLs because it's less likely that users + // will want to visit them again. Even though we don't increment the + // typed_count for pasted-in URLs, if the user manually edits the URL or types + // some long thing in by hand, we wouldn't want to immediately start + // autocompleting it. + if (!match.promoted && + (!match.url_info.typed_count() || + ((match.url_info.typed_count() == 1) && + !match.IsHostOnly()))) return false; // In the case where the user has typed "foo.com" and visited (but not typed) @@ -830,7 +875,7 @@ bool HistoryURLProvider::PromoteMatchForInlineAutocomplete( // future pass from suggesting the exact input as a better match. if (params) { params->dont_suggest_exact_input = true; - params->matches.push_back(HistoryMatchToACMatch(params, match, + params->matches.push_back(HistoryMatchToACMatch(*params, match, INLINE_AUTOCOMPLETE, CalculateRelevance(INLINE_AUTOCOMPLETE, 0))); } return true; @@ -907,69 +952,23 @@ void HistoryURLProvider::PromoteOrCreateShorterSuggestion( // Promote or add the desired URL to the list of matches. bool ensure_can_inline = - promote && PromoteMatchForInlineAutocomplete(NULL, match); - CreateOrPromoteMatch(info, match.input_location, match.match_in_scheme, - matches, create_shorter_match_, promote); - if (ensure_can_inline) { - // If |match| was inline-autocompletable and we're promoting something to - // replace it, make sure the promoted item is also inline-autocompletable. - // Setting the typed_count to 2 is sufficient to guarantee this (and is safe - // because by this point all sorting has already happened and the only thing - // checking the typed_count will be PromoteMatchForInlineAutocomplete()). - // - // We have to do this here rather than changing |info| before calling - // EnsureMatchPresent() because if EnsureMatchPresent() merely moves an - // existing match to the front, it will ignore the typed_count in |info|. - // But we set |ensure_can_inline| above because |match| is a reference and - // thus checking it here would examine the wrong match. - matches->front().url_info.set_typed_count(2); - } -} - -void HistoryURLProvider::SortMatches(history::HistoryMatches* matches) const { - // Sort by quality, best first. - std::sort(matches->begin(), matches->end(), &CompareHistoryMatch); - - // Remove duplicate matches (caused by the search string appearing in one of - // the prefixes as well as after it). Consider the following scenario: - // - // User has visited "http://http.com" once and "http://htaccess.com" twice. - // User types "http". The autocomplete search with prefix "http://" returns - // the first host, while the search with prefix "" returns both hosts. Now - // we sort them into rank order: - // http://http.com (innermost_match) - // http://htaccess.com (!innermost_match, url_info.visit_count == 2) - // http://http.com (!innermost_match, url_info.visit_count == 1) - // - // The above scenario tells us we can't use std::unique(), since our - // duplicates are not always sequential. It also tells us we should remove - // the lower-quality duplicate(s), since otherwise the returned results won't - // be ordered correctly. This is easy to do: we just always remove the later - // element of a duplicate pair. - // Be careful! Because the vector contents may change as we remove elements, - // we use an index instead of an iterator in the outer loop, and don't - // precalculate the ending position. - for (size_t i = 0; i < matches->size(); ++i) { - for (history::HistoryMatches::iterator j(matches->begin() + i + 1); - j != matches->end(); ) { - if ((*matches)[i].url_info.url() == j->url_info.url()) - j = matches->erase(j); - else - ++j; - } - } + promote && PromoteMatchForInlineAutocomplete(match, NULL); + ensure_can_inline &= CreateOrPromoteMatch(info, match.input_location, + match.match_in_scheme, matches, create_shorter_match_, promote); + if (ensure_can_inline) + matches->front().promoted = true; } void HistoryURLProvider::CullPoorMatches( - history::HistoryMatches* matches, - HistoryURLProviderParams* params) const { + const HistoryURLProviderParams& params, + history::HistoryMatches* matches) const { const base::Time& threshold(history::AutocompleteAgeThreshold()); for (history::HistoryMatches::iterator i(matches->begin()); i != matches->end(); ) { if (RowQualifiesAsSignificant(i->url_info, threshold) && - !(params->default_search_provider && - params->default_search_provider->IsSearchURLUsingTermsData( - i->url_info.url(), *params->search_terms_data.get()))) { + !(params.default_search_provider && + params.default_search_provider->IsSearchURLUsingTermsData( + i->url_info.url(), *params.search_terms_data.get()))) { ++i; } else { i = matches->erase(i); @@ -1038,7 +1037,7 @@ size_t HistoryURLProvider::RemoveSubsequentMatchesOf( } AutocompleteMatch HistoryURLProvider::HistoryMatchToACMatch( - HistoryURLProviderParams* params, + const HistoryURLProviderParams& params, const history::HistoryMatch& history_match, MatchType match_type, int relevance) { @@ -1049,18 +1048,18 @@ AutocompleteMatch HistoryURLProvider::HistoryMatchToACMatch( match.destination_url = info.url(); DCHECK(match.destination_url.is_valid()); size_t inline_autocomplete_offset = - history_match.input_location + params->input.text().length(); + history_match.input_location + params.input.text().length(); std::string languages = (match_type == WHAT_YOU_TYPED) ? - std::string() : params->languages; + std::string() : params.languages; const net::FormatUrlTypes format_types = net::kFormatUrlOmitAll & - ~((params->trim_http && !history_match.match_in_scheme) ? + ~((params.trim_http && !history_match.match_in_scheme) ? 0 : net::kFormatUrlOmitHTTP); match.fill_into_edit = AutocompleteInput::FormattedStringWithEquivalentMeaning(info.url(), net::FormatUrl(info.url(), languages, format_types, net::UnescapeRule::SPACES, NULL, NULL, &inline_autocomplete_offset)); - if (!params->prevent_inline_autocomplete && + if (!params.prevent_inline_autocomplete && (inline_autocomplete_offset != string16::npos)) { DCHECK(inline_autocomplete_offset <= match.fill_into_edit.length()); match.inline_autocompletion = @@ -1068,7 +1067,7 @@ AutocompleteMatch HistoryURLProvider::HistoryMatchToACMatch( } // The latter part of the test effectively asks "is the inline completion // empty?" (i.e., is this match effectively the what-you-typed match?). - match.allowed_to_be_default_match = !params->prevent_inline_autocomplete || + match.allowed_to_be_default_match = !params.prevent_inline_autocomplete || ((inline_autocomplete_offset != string16::npos) && (inline_autocomplete_offset >= match.fill_into_edit.length())); @@ -1088,7 +1087,7 @@ AutocompleteMatch HistoryURLProvider::HistoryMatchToACMatch( &match.contents_class); } match.description = info.title(); - AutocompleteMatch::ClassifyMatchInString(params->input.text(), + AutocompleteMatch::ClassifyMatchInString(params.input.text(), info.title(), ACMatchClassification::NONE, &match.description_class); diff --git a/chrome/browser/autocomplete/history_url_provider.h b/chrome/browser/autocomplete/history_url_provider.h index a46884e531..0ae0030a68 100644 --- a/chrome/browser/autocomplete/history_url_provider.h +++ b/chrome/browser/autocomplete/history_url_provider.h @@ -254,8 +254,8 @@ class HistoryURLProvider : public HistoryProvider { // Determines if |match| is suitable for inline autocomplete. If so, and if // |params| is non-NULL, promotes the match. Returns whether |match| is // suitable for inline autocomplete. - bool PromoteMatchForInlineAutocomplete(HistoryURLProviderParams* params, - const history::HistoryMatch& match); + bool PromoteMatchForInlineAutocomplete(const history::HistoryMatch& match, + HistoryURLProviderParams* params); // Sees if a shorter version of the best match should be created, and if so // places it at the front of |matches|. This can suggest history URLs that @@ -270,17 +270,14 @@ class HistoryURLProvider : public HistoryProvider { const AutocompleteMatch& what_you_typed_match, history::HistoryMatches* matches); - // Sorts the given list of matches. - void SortMatches(history::HistoryMatches* matches) const; - // Removes results that have been rarely typed or visited, and not any time // recently. The exact parameters for this heuristic can be found in the // function body. Also culls results corresponding to queries from the default // search engine. These are low-quality, difficult-to-understand matches for // users, and the SearchProvider should surface past queries in a better way // anyway. - void CullPoorMatches(history::HistoryMatches* matches, - HistoryURLProviderParams* params) const; + void CullPoorMatches(const HistoryURLProviderParams& params, + history::HistoryMatches* matches) const; // Removes results that redirect to each other, leaving at most |max_results| // results. @@ -302,7 +299,7 @@ class HistoryURLProvider : public HistoryProvider { // Converts a line from the database into an autocomplete match for display. AutocompleteMatch HistoryMatchToACMatch( - HistoryURLProviderParams* params, + const HistoryURLProviderParams& params, const history::HistoryMatch& history_match, MatchType match_type, int relevance); diff --git a/chrome/browser/autocomplete/keyword_provider.cc b/chrome/browser/autocomplete/keyword_provider.cc index d07e27f789..50020fcc90 100644 --- a/chrome/browser/autocomplete/keyword_provider.cc +++ b/chrome/browser/autocomplete/keyword_provider.cc @@ -16,6 +16,7 @@ #include "chrome/browser/extensions/api/omnibox/omnibox_api.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/search_engines/template_url.h" #include "chrome/browser/search_engines/template_url_service.h" @@ -204,7 +205,8 @@ string16 KeywordProvider::GetKeywordForText(const string16& text) const { GetExtensionById(template_url->GetExtensionId(), false); if (!extension || (profile_->IsOffTheRecord() && - !extension_service->IsIncognitoEnabled(extension->id()))) + !extension_util::IsIncognitoEnabled(extension->id(), + extension_service))) return string16(); } @@ -281,7 +283,8 @@ void KeywordProvider::Start(const AutocompleteInput& input, service->GetExtensionById(template_url->GetExtensionId(), false); bool enabled = extension && (!profile_->IsOffTheRecord() || - service->IsIncognitoEnabled(extension->id())); + extension_util::IsIncognitoEnabled(extension->id(), + service)); if (!enabled) { i = matches.erase(i); continue; diff --git a/chrome/browser/automation/automation_event_observers.h b/chrome/browser/automation/automation_event_observers.h index 73460ec67f..03c2af5b13 100644 --- a/chrome/browser/automation/automation_event_observers.h +++ b/chrome/browser/automation/automation_event_observers.h @@ -89,8 +89,8 @@ class LoginEventObserver // chromeos::LoginStatusConsumer: virtual void OnLoginFailure(const chromeos::LoginFailure& error) OVERRIDE; - virtual void OnLoginSuccess(const chromeos::UserContext& user_context, - bool pending_requests, bool using_oauth) OVERRIDE; + virtual void OnLoginSuccess( + const chromeos::UserContext& user_context) OVERRIDE; private: base::WeakPtr<AutomationProvider> automation_; diff --git a/chrome/browser/automation/automation_event_observers_chromeos.cc b/chrome/browser/automation/automation_event_observers_chromeos.cc index f2a33bfb68..21eb366c25 100644 --- a/chrome/browser/automation/automation_event_observers_chromeos.cc +++ b/chrome/browser/automation/automation_event_observers_chromeos.cc @@ -27,9 +27,7 @@ void LoginEventObserver::OnLoginFailure(const chromeos::LoginFailure& error) { } void LoginEventObserver::OnLoginSuccess( - const chromeos::UserContext& user_context, - bool pending_requests, - bool using_oauth) { + const chromeos::UserContext& user_context) { // Profile changes after login. Ensure AutomationProvider refers to // the correct one. if (automation_) { diff --git a/chrome/browser/automation/automation_provider_observers.h b/chrome/browser/automation/automation_provider_observers.h index 198389e5e8..b6639ece18 100644 --- a/chrome/browser/automation/automation_provider_observers.h +++ b/chrome/browser/automation/automation_provider_observers.h @@ -671,10 +671,7 @@ class LoginObserver : public chromeos::LoginStatusConsumer { virtual void OnLoginFailure(const chromeos::LoginFailure& error); - virtual void OnLoginSuccess( - const chromeos::UserContext& user_context, - bool pending_requests, - bool using_oauth); + virtual void OnLoginSuccess(const chromeos::UserContext& user_context); private: chromeos::ExistingUserController* controller_; @@ -760,10 +757,7 @@ class ScreenUnlockObserver : public ScreenLockUnlockObserver, virtual void OnLoginFailure(const chromeos::LoginFailure& error); - virtual void OnLoginSuccess( - const chromeos::UserContext& user_context, - bool pending_requests, - bool using_oauth) {} + virtual void OnLoginSuccess(const chromeos::UserContext& user_context) {} private: DISALLOW_COPY_AND_ASSIGN(ScreenUnlockObserver); diff --git a/chrome/browser/automation/automation_provider_observers_chromeos.cc b/chrome/browser/automation/automation_provider_observers_chromeos.cc index 49f430800e..7ca37982b3 100644 --- a/chrome/browser/automation/automation_provider_observers_chromeos.cc +++ b/chrome/browser/automation/automation_provider_observers_chromeos.cc @@ -73,10 +73,7 @@ void LoginObserver::OnLoginFailure(const chromeos::LoginFailure& error) { delete this; } -void LoginObserver::OnLoginSuccess( - const chromeos::UserContext& user_context, - bool pending_requests, - bool using_oauth) { +void LoginObserver::OnLoginSuccess(const chromeos::UserContext& user_context) { controller_->set_login_status_consumer(NULL); AutomationJSONReply(automation_.get(), reply_message_.release()) .SendSuccess(NULL); diff --git a/chrome/browser/automation/testing_automation_provider.cc b/chrome/browser/automation/testing_automation_provider.cc index ce44e8f043..a15f5d3fc9 100644 --- a/chrome/browser/automation/testing_automation_provider.cc +++ b/chrome/browser/automation/testing_automation_provider.cc @@ -58,6 +58,7 @@ #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/extensions/extension_tab_util.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/unpacked_installer.h" #include "chrome/browser/extensions/updater/extension_updater.h" #include "chrome/browser/history/history_service_factory.h" @@ -3576,7 +3577,7 @@ void TestingAutomationProvider::GetExtensionsInfo(DictionaryValue* args, Manifest::IsUnpackedLocation(location)); extension_value->SetBoolean("is_enabled", service->IsExtensionEnabled(id)); extension_value->SetBoolean("allowed_in_incognito", - service->IsIncognitoEnabled(id)); + extension_util::IsIncognitoEnabled(id, service)); extension_value->SetBoolean( "has_page_action", extension_action_manager->GetPageAction(*extension) != NULL); @@ -3684,7 +3685,8 @@ void TestingAutomationProvider::SetExtensionStateById( AutomationJSONReply(this, reply_message).SendSuccess(NULL); } - service->SetIsIncognitoEnabled(extension->id(), allow_in_incognito); + extension_util::SetIsIncognitoEnabled( + extension->id(), service, allow_in_incognito); } // See TriggerPageActionById() in chrome/test/pyautolib/pyauto.py diff --git a/chrome/browser/background/background_mode_manager.cc b/chrome/browser/background/background_mode_manager.cc index 7fd24c08fb..b1ffcd17cb 100644 --- a/chrome/browser/background/background_mode_manager.cc +++ b/chrome/browser/background/background_mode_manager.cc @@ -262,7 +262,7 @@ void BackgroundModeManager::LaunchBackgroundApplication( OpenApplication(AppLaunchParams(profile, extension, NEW_FOREGROUND_TAB)); } -bool BackgroundModeManager::IsBackgroundModeActiveForTest() { +bool BackgroundModeManager::IsBackgroundModeActive() { return in_background_mode_; } diff --git a/chrome/browser/background/background_mode_manager.h b/chrome/browser/background/background_mode_manager.h index 118d38693a..0494f5a2ab 100644 --- a/chrome/browser/background/background_mode_manager.h +++ b/chrome/browser/background/background_mode_manager.h @@ -60,6 +60,9 @@ class BackgroundModeManager static void LaunchBackgroundApplication(Profile* profile, const extensions::Extension* extension); + // Returns true if background mode is active. + virtual bool IsBackgroundModeActive(); + // For testing purposes. int NumberOfBackgroundModeData(); @@ -246,9 +249,6 @@ class BackgroundModeManager // (virtual to allow overriding in tests). virtual bool IsBackgroundModePrefEnabled() const; - // Returns true if background mode is active. Used only by tests. - bool IsBackgroundModeActiveForTest(); - // Turns off background mode if it's currently enabled. void DisableBackgroundMode(); diff --git a/chrome/browser/bookmarks/bookmark_model.cc b/chrome/browser/bookmarks/bookmark_model.cc index f118e55cbf..9e4156e6af 100644 --- a/chrome/browser/bookmarks/bookmark_model.cc +++ b/chrome/browser/bookmarks/bookmark_model.cc @@ -496,14 +496,29 @@ void BookmarkModel::SetURL(const BookmarkNode* node, const GURL& url) { void BookmarkModel::SetNodeMetaInfo(const BookmarkNode* node, const std::string& key, const std::string& value) { + // TODO(noyau): Right now the notification is send even if the meta info + // doesn't change. Checking first with the current API will decode the meta + // info twice, a non optimal solution. + FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, + OnWillChangeBookmarkMetaInfo(this, node)); + if (AsMutable(node)->SetMetaInfo(key, value) && store_.get()) store_->ScheduleSave(); + + FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, + BookmarkMetaInfoChanged(this, node)); } void BookmarkModel::DeleteNodeMetaInfo(const BookmarkNode* node, const std::string& key) { + FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, + OnWillChangeBookmarkMetaInfo(this, node)); + if (AsMutable(node)->DeleteMetaInfo(key) && store_.get()) store_->ScheduleSave(); + + FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, + BookmarkMetaInfoChanged(this, node)); } void BookmarkModel::SetDateAdded(const BookmarkNode* node, diff --git a/chrome/browser/bookmarks/bookmark_model_observer.h b/chrome/browser/bookmarks/bookmark_model_observer.h index 31bd2e7197..aea1a0ad2e 100644 --- a/chrome/browser/bookmarks/bookmark_model_observer.h +++ b/chrome/browser/bookmarks/bookmark_model_observer.h @@ -57,6 +57,14 @@ class BookmarkModelObserver { virtual void BookmarkNodeChanged(BookmarkModel* model, const BookmarkNode* node) = 0; + // Invoked before the metainfo of a node is changed. + virtual void OnWillChangeBookmarkMetaInfo(BookmarkModel* model, + const BookmarkNode* node) {} + + // Invoked when the metainfo on a node changes. + virtual void BookmarkMetaInfoChanged(BookmarkModel* model, + const BookmarkNode* node) {} + // Invoked when a favicon has been loaded or changed. virtual void BookmarkNodeFaviconChanged(BookmarkModel* model, const BookmarkNode* node) = 0; diff --git a/chrome/browser/bookmarks/bookmark_tag_model.cc b/chrome/browser/bookmarks/bookmark_tag_model.cc new file mode 100644 index 0000000000..9a7dec2cb6 --- /dev/null +++ b/chrome/browser/bookmarks/bookmark_tag_model.cc @@ -0,0 +1,553 @@ +// Copyright 2013 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/bookmarks/bookmark_tag_model.h" + +#include "base/auto_reset.h" +#include "base/json/json_string_value_serializer.h" +#include "base/observer_list.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/bookmarks/bookmark_model_factory.h" +#include "chrome/browser/bookmarks/bookmark_tag_model_observer.h" +#include "ui/base/models/tree_node_iterator.h" + +namespace { +// The key used to store the tag list in the metainfo of a bookmark. +const char* TAG_KEY = "TAG_KEY"; + +// Comparator to sort tags by usage. +struct TagComparator { + TagComparator(std::map<BookmarkTag, unsigned int>& tags) : tags_(tags) { + } + ~TagComparator() {} + + bool operator()(const BookmarkTag& a, const BookmarkTag& b) { + return (tags_[a] < tags_[b]); + } + + std::map<BookmarkTag, unsigned int>& tags_; +}; + +// The tags are currently stored in the BookmarkNode's metaInfo in JSON +// format. This function extracts the info from there and returns it in +// digestible format. +// If the Bookmark was never tagged before it is implicitely tagged with the +// title of all its ancestors in the BookmarkModel. +std::set<BookmarkTag> ExtractTagsFromBookmark(const BookmarkNode* bookmark) { + // This is awful BTW. Metainfo is itself an encoded JSON, and here we decode + // another layer. + + // Retrieve the encodedData from the bookmark. If there is no encoded data + // at all returns the name of all the ancestors as separate tags. + std::string encoded; + if (!bookmark->GetMetaInfo(TAG_KEY, &encoded)) { + std::set<BookmarkTag> tags; + const BookmarkNode* folder = bookmark->parent(); + while (folder && folder->type() == BookmarkNode::FOLDER) { + BookmarkTag trimmed_tag = CollapseWhitespace(folder->GetTitle(), true); + if (!trimmed_tag.empty()) + tags.insert(trimmed_tag); + folder = folder->parent(); + } + return tags; + } + + // Decode into a base::Value. If the data is not encoded properly as a list + // return an empty result. + JSONStringValueSerializer serializer(&encoded); + int error_code = 0; + std::string error_message; + scoped_ptr<base::Value> result(serializer.Deserialize(&error_code, + &error_message)); + + if (error_code || !result->IsType(base::Value::TYPE_LIST)) + return std::set<BookmarkTag>(); + + base::ListValue* list = NULL; + if (!result->GetAsList(&list) || list->empty()) + return std::set<BookmarkTag>(); + + // Build the set. + std::set<BookmarkTag> return_value; + + for (base::ListValue::iterator it = list->begin(); + it != list->end(); ++it) { + base::Value* item = *it; + BookmarkTag tag; + if (!item->GetAsString(&tag)) + continue; + return_value.insert(tag); + } + return return_value; +} +} // namespace + +BookmarkTagModel::BookmarkTagModel(BookmarkModel* bookmark_model) + : bookmark_model_(bookmark_model), + loaded_(false), + observers_(ObserverList<BookmarkTagModelObserver>::NOTIFY_EXISTING_ONLY), + inhibit_change_notifications_(false) { + bookmark_model_->AddObserver(this); + if (bookmark_model_->loaded()) + Load(); +} + +BookmarkTagModel::~BookmarkTagModel() { + if (bookmark_model_) + bookmark_model_->RemoveObserver(this); +} + +// BookmarkModel forwarding. + +void BookmarkTagModel::AddObserver(BookmarkTagModelObserver* observer) { + observers_.AddObserver(observer); +} + +void BookmarkTagModel::RemoveObserver(BookmarkTagModelObserver* observer) { + observers_.RemoveObserver(observer); +} + +void BookmarkTagModel::BeginExtensiveChanges() { + DCHECK(bookmark_model_); + bookmark_model_->BeginExtensiveChanges(); +} + +void BookmarkTagModel::EndExtensiveChanges() { + DCHECK(bookmark_model_); + bookmark_model_->EndExtensiveChanges(); +} + +bool BookmarkTagModel::IsDoingExtensiveChanges() const { + DCHECK(bookmark_model_); + return bookmark_model_->IsDoingExtensiveChanges(); +} + +void BookmarkTagModel::Remove(const BookmarkNode* bookmark) { + DCHECK(bookmark_model_); + DCHECK(loaded_); + const BookmarkNode* parent = bookmark->parent(); + bookmark_model_->Remove(parent, parent->GetIndexOf(bookmark)); +} + +void BookmarkTagModel::RemoveAll() { + DCHECK(bookmark_model_); + DCHECK(loaded_); + bookmark_model_->RemoveAll(); +} + +const gfx::Image& BookmarkTagModel::GetFavicon(const BookmarkNode* bookmark) { + DCHECK(bookmark_model_); + DCHECK(loaded_); + return bookmark_model_->GetFavicon(bookmark); +} + +void BookmarkTagModel::SetTitle(const BookmarkNode* bookmark, + const string16& title) { + DCHECK(bookmark_model_); + DCHECK(loaded_); + bookmark_model_->SetTitle(bookmark, title); +} + +void BookmarkTagModel::SetURL(const BookmarkNode* bookmark, const GURL& url) { + DCHECK(bookmark_model_); + DCHECK(loaded_); + bookmark_model_->SetURL(bookmark, url); +} + +void BookmarkTagModel::SetDateAdded(const BookmarkNode* bookmark, + base::Time date_added) { + DCHECK(bookmark_model_); + DCHECK(loaded_); + bookmark_model_->SetDateAdded(bookmark, date_added); +} + +const BookmarkNode* + BookmarkTagModel::GetMostRecentlyAddedBookmarkForURL(const GURL& url) { + DCHECK(bookmark_model_); + DCHECK(loaded_); + return bookmark_model_->GetMostRecentlyAddedNodeForURL(url); +} + +// Tags specific code. + +const BookmarkNode* BookmarkTagModel::AddURL( + const string16& title, + const GURL& url, + const std::set<BookmarkTag>& tags) { + DCHECK(bookmark_model_); + DCHECK(loaded_); + + const BookmarkNode* bookmark; + { + base::AutoReset<bool> inhibitor(&inhibit_change_notifications_, true); + const BookmarkNode* parent = bookmark_model_->GetParentForNewNodes(); + bookmark = bookmark_model_->AddURL(parent, 0, title, url); + AddTagsToBookmark(tags, bookmark); + } + FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, + BookmarkNodeAdded(this, bookmark)); + + return bookmark; +} + +std::set<BookmarkTag> BookmarkTagModel::GetTagsForBookmark( + const BookmarkNode* bookmark) { + DCHECK(loaded_); + return bookmark_to_tags_[bookmark]; +} + +void BookmarkTagModel::AddTagsToBookmark( + const std::set<BookmarkTag>& tags, + const BookmarkNode* bookmark) { + DCHECK(bookmark_model_); + std::set<BookmarkTag> all_tags(GetTagsForBookmark(bookmark)); + for (std::set<BookmarkTag>::const_iterator it = tags.begin(); + it != tags.end(); ++it) { + BookmarkTag trimmed_tag = CollapseWhitespace(*it, true); + if (trimmed_tag.empty()) + continue; + all_tags.insert(trimmed_tag); + } + SetTagsOnBookmark(all_tags, bookmark); +} + +void BookmarkTagModel::AddTagsToBookmarks( + const std::set<BookmarkTag>& tags, + const std::set<const BookmarkNode*>& bookmarks) { + for (std::set<const BookmarkNode*>::const_iterator it = bookmarks.begin(); + it != bookmarks.end(); ++it) { + AddTagsToBookmark(tags, *it); + } +} + +void BookmarkTagModel::RemoveTagsFromBookmark( + const std::set<BookmarkTag>& tags, + const BookmarkNode* bookmark) { + std::set<BookmarkTag> all_tags(GetTagsForBookmark(bookmark)); + for (std::set<BookmarkTag>::const_iterator it = tags.begin(); + it != tags.end(); ++it) { + all_tags.erase(*it); + } + SetTagsOnBookmark(all_tags, bookmark); +} + +void BookmarkTagModel::RemoveTagsFromBookmarks( + const std::set<BookmarkTag>& tags, + const std::set<const BookmarkNode*>& bookmarks){ + for (std::set<const BookmarkNode*>::const_iterator it = bookmarks.begin(); + it != bookmarks.end(); ++it) { + RemoveTagsFromBookmark(tags, *it); + } +} + +std::set<const BookmarkNode*> BookmarkTagModel::BookmarksForTags( + const std::set<BookmarkTag>& tags) { + DCHECK(loaded_); + // Count for each tags how many times a bookmark appeared. + std::map<const BookmarkNode*, size_t> bookmark_counts; + for (std::set<BookmarkTag>::const_iterator it = tags.begin(); + it != tags.end(); ++it) { + const std::set<const BookmarkNode*>& subset(tag_to_bookmarks_[*it]); + for (std::set<const BookmarkNode*>::const_iterator tag_it = subset.begin(); + tag_it != subset.end(); ++tag_it) { + bookmark_counts[*tag_it] += 1; + } + } + // Keep only the bookmarks that appeared in all the tags. + std::set<const BookmarkNode*> common_bookmarks; + for (std::map<const BookmarkNode*, size_t>::iterator it = + bookmark_counts.begin(); it != bookmark_counts.end(); ++it) { + if (it->second == tags.size()) + common_bookmarks.insert(it->first); + } + return common_bookmarks; +} + +std::set<const BookmarkNode*> BookmarkTagModel::BookmarksForTag( + const BookmarkTag& tag) { + DCHECK(!tag.empty()); + return tag_to_bookmarks_[tag]; +} + +std::vector<BookmarkTag> BookmarkTagModel::TagsRelatedToTag( + const BookmarkTag& tag) { + DCHECK(loaded_); + std::map<BookmarkTag, unsigned int> tags; + + if (tag.empty()) { + // Returns all the tags. + for (std::map<const BookmarkTag, std::set<const BookmarkNode*> >::iterator + it = tag_to_bookmarks_.begin(); it != tag_to_bookmarks_.end(); ++it) { + tags[it->first] = it->second.size(); + } + } else { + std::set<const BookmarkNode*> bookmarks(BookmarksForTag(tag)); + + for (std::set<const BookmarkNode*>::iterator it = bookmarks.begin(); + it != bookmarks.end(); ++it) { + const std::set<BookmarkTag>& subset(bookmark_to_tags_[*it]); + for (std::set<BookmarkTag>::const_iterator tag_it = subset.begin(); + tag_it != subset.end(); ++tag_it) { + tags[*tag_it] += 1; + } + } + tags.erase(tag); // A tag is not related to itself. + } + + std::vector<BookmarkTag> sorted_tags; + for (std::map<BookmarkTag, unsigned int>::iterator it = tags.begin(); + it != tags.end(); ++it) { + sorted_tags.push_back(it->first); + } + std::sort(sorted_tags.begin(), sorted_tags.end(), TagComparator(tags)); + return sorted_tags; +} + +// BookmarkModelObserver methods. + +void BookmarkTagModel::Loaded(BookmarkModel* model, bool ids_reassigned) { + Load(); +} + +void BookmarkTagModel::BookmarkModelBeingDeleted(BookmarkModel* model) { + DCHECK(bookmark_model_); + FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, + BookmarkTagModelBeingDeleted(this)); + bookmark_model_ = NULL; +} + +void BookmarkTagModel::BookmarkNodeMoved(BookmarkModel* model, + const BookmarkNode* old_parent, + int old_index, + const BookmarkNode* new_parent, + int new_index) { + DCHECK(loaded_); + const BookmarkNode* bookmark = new_parent->GetChild(new_index); + if (bookmark->is_folder()) { + ReloadDescendants(bookmark); + } else if (bookmark->is_url()) { + std::string encoded; + if (!bookmark->GetMetaInfo(TAG_KEY, &encoded)) { + // The bookmark moved and the system currently use its ancestors name as a + // poor approximation for tags. + FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, + OnWillChangeBookmarkTags(this, bookmark)); + RemoveBookmark(bookmark); + LoadBookmark(bookmark); + FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, + BookmarkTagsChanged(this, bookmark)); + } + } +} + +void BookmarkTagModel::BookmarkNodeAdded(BookmarkModel* model, + const BookmarkNode* parent, + int index) { + DCHECK(loaded_); + const BookmarkNode* bookmark = parent->GetChild(index); + if (!bookmark->is_url()) + return; + LoadBookmark(bookmark); + + if (!inhibit_change_notifications_) + FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, + BookmarkNodeAdded(this, bookmark)); +} + +void BookmarkTagModel::OnWillRemoveBookmarks(BookmarkModel* model, + const BookmarkNode* parent, + int old_index, + const BookmarkNode* node) { + DCHECK(loaded_); + if (!node->is_url()) + return; + RemoveBookmark(node); + FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, + OnWillRemoveBookmarks(this, node)); +} + +void BookmarkTagModel::BookmarkNodeRemoved(BookmarkModel* model, + const BookmarkNode* parent, + int old_index, + const BookmarkNode* node) { + DCHECK(loaded_); + if (!node->is_url()) + return; + FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, + BookmarkNodeRemoved(this, node)); +} + +void BookmarkTagModel::OnWillChangeBookmarkNode(BookmarkModel* model, + const BookmarkNode* node) { + DCHECK(loaded_); + if (!node->is_url() || inhibit_change_notifications_) + return; + FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, + OnWillChangeBookmarkNode(this, node)); +} + +void BookmarkTagModel::BookmarkNodeChanged(BookmarkModel* model, + const BookmarkNode* node) { + DCHECK(loaded_); + if (node->is_folder()) { + // A folder title changed. This may change the tags on all the descendants + // still using the default tag list of all ancestors. + ReloadDescendants(node); + } else if (node->is_url()) { + if (!inhibit_change_notifications_) + FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, + BookmarkNodeChanged(this, node)); + } +} + +void BookmarkTagModel::OnWillChangeBookmarkMetaInfo(BookmarkModel* model, + const BookmarkNode* node) { + DCHECK(loaded_); + if (!node->is_url() || inhibit_change_notifications_) + return; + FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, + OnWillChangeBookmarkTags(this, node)); +} + +void BookmarkTagModel::BookmarkMetaInfoChanged(BookmarkModel* model, + const BookmarkNode* node) { + DCHECK(loaded_); + if (!node->is_url()) + return; + RemoveBookmark(node); + LoadBookmark(node); + if (!inhibit_change_notifications_) + FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, + BookmarkTagsChanged(this, node)); +} + +void BookmarkTagModel::BookmarkNodeFaviconChanged(BookmarkModel* model, + const BookmarkNode* node) { + DCHECK(loaded_); + if (!node->is_url()) + return; + FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, + BookmarkNodeFaviconChanged(this, node)); +} + +void BookmarkTagModel::OnWillReorderBookmarkNode(BookmarkModel* model, + const BookmarkNode* node) { + // This model doesn't care. +} + +void BookmarkTagModel::BookmarkNodeChildrenReordered(BookmarkModel* model, + const BookmarkNode* node) { + // This model doesn't care. +} + +void BookmarkTagModel::ExtensiveBookmarkChangesBeginning(BookmarkModel* model) { + DCHECK(loaded_); + FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, + ExtensiveBookmarkChangesBeginning(this)); +} + +void BookmarkTagModel::ExtensiveBookmarkChangesEnded(BookmarkModel* model) { + DCHECK(loaded_); + FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, + ExtensiveBookmarkChangesEnded(this)); +} + +void BookmarkTagModel::OnWillRemoveAllBookmarks(BookmarkModel* model) { + DCHECK(loaded_); + FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, + OnWillRemoveAllBookmarks(this)); +} + +void BookmarkTagModel::BookmarkAllNodesRemoved(BookmarkModel* model){ + DCHECK(loaded_); + tag_to_bookmarks_.clear(); + bookmark_to_tags_.clear(); + FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, + BookmarkAllNodesRemoved(this)); +} + +// Private methods. + +void BookmarkTagModel::SetTagsOnBookmark(const std::set<BookmarkTag>& tags, + const BookmarkNode* bookmark) { + DCHECK(bookmark_model_); + DCHECK(loaded_); + + // Build a ListValue. + std::vector<BookmarkTag> tag_vector(tags.begin(), tags.end()); + base::ListValue list; + list.AppendStrings(tag_vector); + + // Encodes it. + std::string encoded; + JSONStringValueSerializer serializer(&encoded); + + // Pushes it in the bookmark's metainfo. Even if the tag list is empty the + // empty list must be put on the node to avoid reverting to the tag list + // derived from the hierarchy. + // The internal caches of the BookmarkTagModel are updated when the + // notification from the BookmarkModel is received. + serializer.Serialize(list); + bookmark_model_->SetNodeMetaInfo(bookmark, TAG_KEY, encoded); +} + +void BookmarkTagModel::Load() { + DCHECK(bookmark_model_); + DCHECK(!loaded_); + ui::TreeNodeIterator<const BookmarkNode> iterator( + bookmark_model_->root_node()); + while (iterator.has_next()) { + const BookmarkNode* bookmark = iterator.Next(); + if (bookmark->is_url()) + LoadBookmark(bookmark); + } + loaded_ = true; + FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, + Loaded(this)); +} + +void BookmarkTagModel::ReloadDescendants(const BookmarkNode* folder) { + DCHECK(folder->is_folder()); + ExtensiveChanges scoped(ExtensiveChanges(this)); + ui::TreeNodeIterator<const BookmarkNode> iterator(folder); + while (iterator.has_next()) { + const BookmarkNode* bookmark = iterator.Next(); + if (bookmark->is_url()) { + FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, + OnWillChangeBookmarkTags(this, bookmark)); + RemoveBookmark(bookmark); + LoadBookmark(bookmark); + FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, + BookmarkTagsChanged(this, bookmark)); + } + } +} + +void BookmarkTagModel::LoadBookmark(const BookmarkNode* bookmark) { + DCHECK(bookmark_model_); + DCHECK(bookmark->is_url()); + std::set<BookmarkTag> tags(ExtractTagsFromBookmark(bookmark)); + + bookmark_to_tags_[bookmark] = tags; + for (std::set<BookmarkTag>::iterator it = tags.begin(); + it != tags.end(); ++it) { + tag_to_bookmarks_[*it].insert(bookmark); + } +} + +void BookmarkTagModel::RemoveBookmark(const BookmarkNode* bookmark) { + DCHECK(bookmark_model_); + DCHECK(bookmark->is_url()); + std::set<BookmarkTag> tags(bookmark_to_tags_[bookmark]); + bookmark_to_tags_.erase(bookmark); + + for (std::set<BookmarkTag>::iterator it = tags.begin(); + it != tags.end(); ++it) { + tag_to_bookmarks_[*it].erase(bookmark); + // Remove the tags no longer used. + if (!tag_to_bookmarks_[*it].size()) + tag_to_bookmarks_.erase(*it); + } +} diff --git a/chrome/browser/bookmarks/bookmark_tag_model.h b/chrome/browser/bookmarks/bookmark_tag_model.h new file mode 100644 index 0000000000..b30e565cc3 --- /dev/null +++ b/chrome/browser/bookmarks/bookmark_tag_model.h @@ -0,0 +1,214 @@ +// Copyright 2013 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_BOOKMARKS_BOOKMARK_TAG_MODEL_H_ +#define CHROME_BROWSER_BOOKMARKS_BOOKMARK_TAG_MODEL_H_ + +#include "chrome/browser/bookmarks/bookmark_model.h" +#include "chrome/browser/bookmarks/bookmark_model_observer.h" + +class BookmarkTagModelObserver; + +typedef string16 BookmarkTag; + +// BookmarTagModel provides a way to access and manipulate bookmarks in a +// non-hierarchical way. BookmarkTagModel view the bookmarks as a flat list, +// and each one can be marked with a collection of tags (tags are simply +// strings). +// +// BookmarkTagModel converts on demand the data from an existing BookmarkModel +// to its view of the world by considering all the titles of all the ancestors +// as tags. This view is frozen on an individual bookmarks when the +// BookmarkTagModel performs a change on the tags of this bookmarks. +// +// The Bookmark's meta info is used for storage. +// +// An observer may be attached to a BookmarkTagModel to observe relevant events. +// +// TODO(noyau): The meta info data is not preserved by copy/paste or drag and +// drop, this means that bookmarks will loose their tag information if +// manipulated that way. +// +class BookmarkTagModel : public BookmarkModelObserver { + public: + explicit BookmarkTagModel(BookmarkModel* bookmark_model); + virtual ~BookmarkTagModel(); + + // Returns true if the model finished loading. + bool loaded() const { return loaded_; } + + // Add and remove observers on this object. + void AddObserver(BookmarkTagModelObserver* observer); + void RemoveObserver(BookmarkTagModelObserver* observer); + + // Brackets an extensive set of changes, such as during import or sync, so + // observers can delay any expensive UI updates until it's finished. + class ExtensiveChanges { + public: + friend class BookmarkTagModel; + explicit ExtensiveChanges(BookmarkTagModel* model) : model_(model) { + model_->BeginExtensiveChanges(); + } + private: + ~ExtensiveChanges() { + model_->EndExtensiveChanges(); + } + + BookmarkTagModel* model_; + }; + + // Returns true if this bookmark model is currently in a mode where extensive + // changes might happen, such as for import and sync. This is helpful for + // observers that are created after the mode has started, and want to check + // state during their own initializer. + bool IsDoingExtensiveChanges() const; + + // Removes the given |BookmarkNode|. Observers are notified immediately. + void Remove(const BookmarkNode* bookmark); + + // Removes all the bookmark nodes. Observers are only notified when all nodes + // have been removed. There is no notification for individual node removals. + void RemoveAll(); + + // Returns the favicon for |node|. If the favicon has not yet been + // loaded it is loaded and the observer of the model notified when done. + const gfx::Image& GetFavicon(const BookmarkNode* bookmark); + + // Sets the title of |node|. + void SetTitle(const BookmarkNode* bookmark, const string16& title); + + // Sets the URL of |node|. + void SetURL(const BookmarkNode* bookmark, const GURL& url); + + // Sets the date added time of |node|. + void SetDateAdded(const BookmarkNode* bookmark, base::Time date_added); + + // Returns the most recently added bookmark for the |url|. Returns NULL if + // |url| is not bookmarked. + const BookmarkNode* GetMostRecentlyAddedBookmarkForURL(const GURL& url); + + // Creates a new bookmark. + const BookmarkNode* AddURL(const string16& title, + const GURL& url, + const std::set<BookmarkTag>& tags); + + // Adds the |tags| to the tag list of the |bookmark|. + void AddTagsToBookmark(const std::set<BookmarkTag>& tags, + const BookmarkNode* bookmark); + + // Same but to a whole collection of |bookmarks|. + void AddTagsToBookmarks(const std::set<BookmarkTag>& tags, + const std::set<const BookmarkNode*>& bookmarks); + + // Remove the |tags| from the tag list of the |bookmark|. If the bookmark + // is not tagged with one or more of the tags, these are ignored. + void RemoveTagsFromBookmark(const std::set<BookmarkTag>& tags, + const BookmarkNode* bookmark); + + // Same but to a whole collection of |bookmarks|. + void RemoveTagsFromBookmarks(const std::set<BookmarkTag>& tags, + const std::set<const BookmarkNode*>& bookmarks); + + // Returns all the tags set on a specific |bookmark|. + std::set<BookmarkTag> GetTagsForBookmark(const BookmarkNode* bookmark); + + // Returns the bookmarks marked with all the given |tags|. + std::set<const BookmarkNode*> BookmarksForTags( + const std::set<BookmarkTag>& tags); + + // Returns the bookmarks marked with the given |tag|. + std::set<const BookmarkNode*> BookmarksForTag(const BookmarkTag& tag); + + // Returns all tags related to the parent |tag|. If |tag| is null this method + // will returns and sort all tags in the system. A related tag is a tag used + // on one or more of the bookmarks tagged with |tag|. The returned tags are + // ordered from the most common to the rarest one. + std::vector<BookmarkTag> TagsRelatedToTag(const BookmarkTag& tag); + + // All the BookmarkModelObserver methods. See there for details. + virtual void Loaded(BookmarkModel* model, bool ids_reassigned) OVERRIDE; + virtual void BookmarkModelBeingDeleted(BookmarkModel* model) OVERRIDE; + virtual void BookmarkNodeMoved(BookmarkModel* model, + const BookmarkNode* old_parent, + int old_index, + const BookmarkNode* new_parent, + int new_index) OVERRIDE; + virtual void BookmarkNodeAdded(BookmarkModel* model, + const BookmarkNode* parent, + int index) OVERRIDE; + virtual void OnWillRemoveBookmarks(BookmarkModel* model, + const BookmarkNode* parent, + int old_index, + const BookmarkNode* node) OVERRIDE; + virtual void BookmarkNodeRemoved(BookmarkModel* model, + const BookmarkNode* parent, + int old_index, + const BookmarkNode* node) OVERRIDE; + virtual void OnWillChangeBookmarkNode(BookmarkModel* model, + const BookmarkNode* node) OVERRIDE; + virtual void BookmarkNodeChanged(BookmarkModel* model, + const BookmarkNode* node) OVERRIDE; + virtual void OnWillChangeBookmarkMetaInfo(BookmarkModel* model, + const BookmarkNode* node) OVERRIDE; + virtual void BookmarkMetaInfoChanged(BookmarkModel* model, + const BookmarkNode* node) OVERRIDE; + virtual void BookmarkNodeFaviconChanged(BookmarkModel* model, + const BookmarkNode* node) OVERRIDE; + virtual void OnWillReorderBookmarkNode(BookmarkModel* model, + const BookmarkNode* node) OVERRIDE; + virtual void BookmarkNodeChildrenReordered(BookmarkModel* model, + const BookmarkNode* node) OVERRIDE; + virtual void ExtensiveBookmarkChangesBeginning(BookmarkModel* model) OVERRIDE; + virtual void ExtensiveBookmarkChangesEnded(BookmarkModel* model) OVERRIDE; + virtual void OnWillRemoveAllBookmarks(BookmarkModel* model) OVERRIDE; + virtual void BookmarkAllNodesRemoved(BookmarkModel* model) OVERRIDE; + + private: + // Notifies the observers that an extensive set of changes is about to happen, + // such as during import or sync, so they can delay any expensive UI updates + // until it's finished. + void BeginExtensiveChanges(); + void EndExtensiveChanges(); + + // Encode the tags in a format suitable for the BookmarkNode's metaInfo and + // set or replace the value. + void SetTagsOnBookmark(const std::set<BookmarkTag>& tags, + const BookmarkNode* bookmark); + + // Build the caches of tag to bookmarks and bookmarks to tag for faster + // access to the data. Load() is called from the constructor if possible, or + // as soon as possible after that. + void Load(); + + // Discard tag information for all descendants of a given folder node and + // rebuild the cache for them. + void ReloadDescendants(const BookmarkNode* folder); + + // Clear the local cache of all mentions of |bookmark|. + void RemoveBookmark(const BookmarkNode* bookmark); + + // Extract the tags from |bookmark| and insert it in the local cache. + void LoadBookmark(const BookmarkNode* bookmark); + + // The model from where the data is permanently stored. + BookmarkModel* bookmark_model_; + + // True if the model is fully loaded. + bool loaded_; + + // The observers. + ObserverList<BookmarkTagModelObserver> observers_; + + // Local cache for quick access. + std::map<const BookmarkTag, std::set<const BookmarkNode*> > tag_to_bookmarks_; + std::map<const BookmarkNode*, std::set<BookmarkTag> > bookmark_to_tags_; + + // Set to true during the creation of a new bookmark in order to send only the + // proper notification. + bool inhibit_change_notifications_; + + DISALLOW_COPY_AND_ASSIGN(BookmarkTagModel); +}; + +#endif // CHROME_BROWSER_BOOKMARKS_BOOKMARK_TAG_MODEL_H_ diff --git a/chrome/browser/bookmarks/bookmark_tag_model_observer.h b/chrome/browser/bookmarks/bookmark_tag_model_observer.h new file mode 100644 index 0000000000..4c1df21e85 --- /dev/null +++ b/chrome/browser/bookmarks/bookmark_tag_model_observer.h @@ -0,0 +1,76 @@ +// Copyright 2013 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_BOOKMARKS_BOOKMARK_TAG_MODEL_OBSERVER_H_ +#define CHROME_BROWSER_BOOKMARKS_BOOKMARK_TAG_MODEL_OBSERVER_H_ + +class BookmarkTagModel; +class BookmarkNode; + +// Observer for the BookmarkTagModel. +class BookmarkTagModelObserver { + public: + // Invoked when the model has finished loading. + virtual void Loaded(BookmarkTagModel* model) = 0; + + // Invoked from the destructor of the BookmarkTagModel. + virtual void BookmarkTagModelBeingDeleted(BookmarkTagModel* model) {} + + // Invoked when a node has been added. + virtual void BookmarkNodeAdded(BookmarkTagModel* model, + const BookmarkNode* bookmark) = 0; + + // Invoked before a node is removed. + // |node| is the node to be removed. + virtual void OnWillRemoveBookmarks(BookmarkTagModel* model, + const BookmarkNode* bookmark) {} + + // Invoked when a node has been removed, |node| is the node that was removed. + virtual void BookmarkNodeRemoved(BookmarkTagModel* model, + const BookmarkNode* bookmark) = 0; + + // Invoked before the title or url of a node is changed. + virtual void OnWillChangeBookmarkNode(BookmarkTagModel* model, + const BookmarkNode* bookmark) {} + + // Invoked when the title or url of a node changes. + virtual void BookmarkNodeChanged(BookmarkTagModel* model, + const BookmarkNode* bookmark) = 0; + + // Invoked before changing the tags of a node. + virtual void OnWillChangeBookmarkTags(BookmarkTagModel* model, + const BookmarkNode* bookmark) {} + + // Invoked when tags are changed on a bookmark. + virtual void BookmarkTagsChanged(BookmarkTagModel* model, + const BookmarkNode* bookmark) = 0; + + // Invoked when a favicon has been loaded or changed. + virtual void BookmarkNodeFaviconChanged(BookmarkTagModel* model, + const BookmarkNode* node) = 0; + + // Invoked before an extensive set of model changes is about to begin. + // This tells UI intensive observers to wait until the updates finish to + // update themselves. + // These methods should only be used for imports and sync. + // Observers should still respond to BookmarkNodeRemoved immediately, + // to avoid holding onto stale node pointers. + virtual void ExtensiveBookmarkChangesBeginning(BookmarkTagModel* model) {} + + // Invoked after an extensive set of model changes has ended. + // This tells observers to update themselves if they were waiting for the + // update to finish. + virtual void ExtensiveBookmarkChangesEnded(BookmarkTagModel* model) {} + + // Invoked before all non-permanent bookmark nodes are removed. + virtual void OnWillRemoveAllBookmarks(BookmarkTagModel* model) {} + + // Invoked when all non-permanent bookmark nodes have been removed. + virtual void BookmarkAllNodesRemoved(BookmarkTagModel* model) = 0; + + protected: + virtual ~BookmarkTagModelObserver() {} +}; + +#endif // CHROME_BROWSER_BOOKMARKS_BOOKMARK_TAG_MODEL_OBSERVER_H_ diff --git a/chrome/browser/bookmarks/bookmark_tag_model_unittest.cc b/chrome/browser/bookmarks/bookmark_tag_model_unittest.cc new file mode 100644 index 0000000000..fa5cc98f84 --- /dev/null +++ b/chrome/browser/bookmarks/bookmark_tag_model_unittest.cc @@ -0,0 +1,566 @@ +// Copyright 2013 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/bookmarks/bookmark_tag_model.h" + +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/bookmarks/bookmark_tag_model_observer.h" +#include "testing/gtest/include/gtest/gtest.h" + + +namespace { + +static struct { + const std::string input_tag; + const std::string expected_tag; +} whitespace_test_cases[] = { + // Newlines. + {"foo\nbar", "foo bar"}, + {"foo\n\nbar", "foo bar"}, + {"foo\n\n\nbar", "foo bar"}, + {"foo\r\nbar", "foo bar"}, + {"foo\r\n\r\nbar", "foo bar"}, + {"\nfoo\nbar\n", "foo bar"}, + // Spaces. + {"foo bar", "foo bar"}, + {" foo bar ", "foo bar"}, + {" foo bar ", "foo bar"}, + // Tabs. + {"\tfoo\tbar\t", "foo bar"}, + {"\tfoo bar\t", "foo bar"}, + // Mixed cases. + {"\tfoo\nbar\t", "foo bar"}, + {"\tfoo\r\nbar\t", "foo bar"}, + {" foo\tbar\n", "foo bar"}, + {"\t foo \t bar \t", "foo bar"}, + {"\n foo\r\n\tbar\n \t", "foo bar"}, +}; + +enum ObserverCounts { + OBSERVER_COUNTS_ADD = 0, + OBSERVER_COUNTS_BEFORE_REMOVE, + OBSERVER_COUNTS_REMOVE, + OBSERVER_COUNTS_BEFORE_CHANGE, + OBSERVER_COUNTS_CHANGE, + OBSERVER_COUNTS_BEFORE_TAG_CHANGE, + OBSERVER_COUNTS_TAG_CHANGE, + OBSERVER_COUNTS_FAVICON_CHANGE, + OBSERVER_COUNTS_EXTENSIVE_CHANGE_BEGIN, + OBSERVER_COUNTS_EXTENSIVE_CHANGE_END, + OBSERVER_COUNTS_BEFORE_REMOVE_ALL, + OBSERVER_COUNTS_REMOVE_ALL, + OBSERVER_COUNTS_TOTAL_NUMBER_OF_COUNTS +}; + +const std::string count_name[] = { + "OBSERVER_COUNTS_ADD", + "OBSERVER_COUNTS_BEFORE_REMOVE", + "OBSERVER_COUNTS_REMOVE", + "OBSERVER_COUNTS_BEFORE_CHANGE", + "OBSERVER_COUNTS_CHANGE", + "OBSERVER_COUNTS_BEFORE_TAG_CHANGE", + "OBSERVER_COUNTS_TAG_CHANGE", + "OBSERVER_COUNTS_FAVICON_CHANGE", + "OBSERVER_COUNTS_EXTENSIVE_CHANGE_BEGIN", + "OBSERVER_COUNTS_EXTENSIVE_CHANGE_END", + "OBSERVER_COUNTS_BEFORE_REMOVE_ALL", + "OBSERVER_COUNTS_REMOVE_ALL" +}; + + +class BookmarkTagModelTest + : public testing::Test, public BookmarkTagModelObserver { + public: + struct ObserverDetails { + ObserverDetails() : bookmark_(NULL) {} + + void Set(const BookmarkNode* bookmark, + const std::set<BookmarkTag>& tags) { + bookmark_ = bookmark; + tags_ = tags; + } + + void ExpectEquals(const BookmarkNode* bookmark, + const std::set<BookmarkTag>& tags) { + EXPECT_EQ(bookmark_, bookmark); + EXPECT_EQ(tags_, tags); + } + + private: + const BookmarkNode* bookmark_; + std::set<BookmarkTag> tags_; + }; + + BookmarkTagModelTest() : model_(NULL), + tag_model_(new BookmarkTagModel(&model_)) { + tag_model_->AddObserver(this); + ClearCounts(); + } + + virtual ~BookmarkTagModelTest() { + } + + // BookmarkTagModelObserver methods. + + virtual void Loaded(BookmarkTagModel* model) OVERRIDE { + // We never load from the db, so that this should never get invoked. + NOTREACHED(); + } + + // Invoked when a node has been added. + virtual void BookmarkNodeAdded(BookmarkTagModel* model, + const BookmarkNode* bookmark) OVERRIDE { + ++counts_[OBSERVER_COUNTS_ADD]; + observer_details_.Set(bookmark, model->GetTagsForBookmark(bookmark)); + } + + // Invoked before a node is removed. + // |node| is the node to be removed. + virtual void OnWillRemoveBookmarks(BookmarkTagModel* model, + const BookmarkNode* bookmark) OVERRIDE { + ++counts_[OBSERVER_COUNTS_BEFORE_REMOVE]; + } + + // Invoked when a node has been removed, the item may still be starred though. + // |node| is the node that was removed. + virtual void BookmarkNodeRemoved(BookmarkTagModel* model, + const BookmarkNode* bookmark) OVERRIDE { + ++counts_[OBSERVER_COUNTS_REMOVE]; + } + + // Invoked before the title or url of a node is changed. + virtual void OnWillChangeBookmarkNode(BookmarkTagModel* model, + const BookmarkNode* bookmark) OVERRIDE { + ++counts_[OBSERVER_COUNTS_BEFORE_CHANGE]; + } + + // Invoked when the title or url of a node changes. + virtual void BookmarkNodeChanged(BookmarkTagModel* model, + const BookmarkNode* bookmark) OVERRIDE { + ++counts_[OBSERVER_COUNTS_CHANGE]; + observer_details_.Set(bookmark, model->GetTagsForBookmark(bookmark)); + } + + virtual void OnWillChangeBookmarkTags(BookmarkTagModel* model, + const BookmarkNode* bookmark) OVERRIDE { + ++counts_[OBSERVER_COUNTS_BEFORE_TAG_CHANGE]; + } + + virtual void BookmarkTagsChanged(BookmarkTagModel* model, + const BookmarkNode* bookmark) OVERRIDE { + ++counts_[OBSERVER_COUNTS_TAG_CHANGE]; + observer_details_.Set(bookmark, model->GetTagsForBookmark(bookmark)); + } + + virtual void BookmarkNodeFaviconChanged(BookmarkTagModel* model, + const BookmarkNode* node) OVERRIDE { + ++counts_[OBSERVER_COUNTS_FAVICON_CHANGE]; + } + + virtual void ExtensiveBookmarkChangesBeginning(BookmarkTagModel* model) + OVERRIDE { + ++counts_[OBSERVER_COUNTS_EXTENSIVE_CHANGE_BEGIN]; + } + + virtual void ExtensiveBookmarkChangesEnded(BookmarkTagModel* model) OVERRIDE { + ++counts_[OBSERVER_COUNTS_EXTENSIVE_CHANGE_END]; + } + + virtual void OnWillRemoveAllBookmarks(BookmarkTagModel* model) OVERRIDE { + ++counts_[OBSERVER_COUNTS_BEFORE_REMOVE_ALL]; + } + + virtual void BookmarkAllNodesRemoved(BookmarkTagModel* model) OVERRIDE { + ++counts_[OBSERVER_COUNTS_REMOVE_ALL]; + } + + void ClearCounts() { + for (unsigned int i = 0; i < OBSERVER_COUNTS_TOTAL_NUMBER_OF_COUNTS; ++i) + counts_[i] = 0; + } + + void AssertAndClearObserverCount(ObserverCounts count, int expected) { + ASSERT_EQ(expected, counts_[count]) << count_name[count]; + counts_[count] = 0; + } + + void AssertAllCountsClear() { + for (unsigned int i = 0; i < OBSERVER_COUNTS_TOTAL_NUMBER_OF_COUNTS; ++i) + ASSERT_EQ(0, counts_[i]) << count_name[i]; + } + + const BookmarkNode* AddURLWithTags( + const std::string& name, + const std::set<BookmarkTag>& tags) { + const string16 title(ASCIIToUTF16(name)); + const GURL url("http://" + name + ".com"); + + return tag_model_->AddURL(title, url, tags); + } + + protected: + BookmarkModel model_; + scoped_ptr<BookmarkTagModel> tag_model_; + ObserverDetails observer_details_; + + private: + int counts_[OBSERVER_COUNTS_TOTAL_NUMBER_OF_COUNTS]; + + DISALLOW_COPY_AND_ASSIGN(BookmarkTagModelTest); +}; + +TEST_F(BookmarkTagModelTest, InitialState) { + std::vector<BookmarkTag> tags(tag_model_->TagsRelatedToTag(base::string16())); + EXPECT_EQ(0UL, tags.size()); +} + +TEST_F(BookmarkTagModelTest, AddURL) { + std::set<BookmarkTag> tags; + tags.insert(ASCIIToUTF16("bar")); + tags.insert(ASCIIToUTF16("baz")); + + AddURLWithTags("orly", tags); + const BookmarkNode* new_node = AddURLWithTags("foo", tags); + AssertAndClearObserverCount(OBSERVER_COUNTS_ADD, 2); + AssertAllCountsClear(); + + observer_details_.ExpectEquals(new_node, tags); + + EXPECT_EQ(2UL, tag_model_->BookmarksForTag(ASCIIToUTF16("bar")).size()); + EXPECT_EQ(2UL, tag_model_->BookmarksForTag(ASCIIToUTF16("baz")).size()); + EXPECT_EQ(tags, tag_model_->GetTagsForBookmark(new_node)); + + std::vector<BookmarkTag> alltags( + tag_model_->TagsRelatedToTag(base::string16())); + EXPECT_EQ(2UL, alltags.size()); +} + +TEST_F(BookmarkTagModelTest, RelatedTo) { + std::set<BookmarkTag> tags; + tags.insert(ASCIIToUTF16("bar")); + tags.insert(ASCIIToUTF16("baz")); + + AddURLWithTags("orly", tags); + AddURLWithTags("foo", tags); + AssertAndClearObserverCount(OBSERVER_COUNTS_ADD, 2); + AssertAllCountsClear(); + + std::vector<BookmarkTag> bartags(tag_model_->TagsRelatedToTag( + ASCIIToUTF16("bar"))); + EXPECT_EQ(1UL, bartags.size()); + std::vector<BookmarkTag> baztags(tag_model_->TagsRelatedToTag( + ASCIIToUTF16("baz"))); + EXPECT_EQ(1UL, baztags.size()); +} + +TEST_F(BookmarkTagModelTest, AddURLWithWhitespaceTitle) { + std::set<BookmarkTag> tags; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(whitespace_test_cases); ++i) { + const BookmarkNode* new_node = AddURLWithTags( + whitespace_test_cases[i].input_tag, tags); + + EXPECT_EQ(ASCIIToUTF16(whitespace_test_cases[i].expected_tag), + new_node->GetTitle()); + EXPECT_EQ(BookmarkNode::URL, new_node->type()); + } +} + +TEST_F(BookmarkTagModelTest, CheckTagsWithWhitespace) { + std::set<BookmarkTag> tags; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(whitespace_test_cases); ++i) + tags.insert(ASCIIToUTF16(whitespace_test_cases[i].input_tag)); + + AddURLWithTags("foo", tags); + + std::vector<BookmarkTag> alltags(tag_model_->TagsRelatedToTag( + base::string16())); + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(whitespace_test_cases); ++i) { + EXPECT_EQ(0UL, tag_model_->BookmarksForTag( + ASCIIToUTF16(whitespace_test_cases[i].input_tag)).size()); + EXPECT_EQ(1UL, tag_model_->BookmarksForTag( + ASCIIToUTF16(whitespace_test_cases[i].expected_tag)).size()); + } +} + +TEST_F(BookmarkTagModelTest, RemoveURL) { + std::set<BookmarkTag> tags; + tags.insert(ASCIIToUTF16("bar")); + tags.insert(ASCIIToUTF16("baz")); + + const BookmarkNode* new_node = AddURLWithTags("foo", tags); + const BookmarkNode* new_node2 = AddURLWithTags("flou", tags); + ClearCounts(); + + tag_model_->Remove(new_node); + tag_model_->Remove(new_node2); + AssertAndClearObserverCount(OBSERVER_COUNTS_BEFORE_REMOVE, 2); + AssertAndClearObserverCount(OBSERVER_COUNTS_REMOVE, 2); + AssertAllCountsClear(); + + std::vector<BookmarkTag> alltags(tag_model_->TagsRelatedToTag( + base::string16())); + EXPECT_EQ(0UL, alltags.size()); +} + +TEST_F(BookmarkTagModelTest, AddTagToBookmarks) { + std::set<BookmarkTag> tags; + tags.insert(ASCIIToUTF16("bar")); + tags.insert(ASCIIToUTF16("baz")); + + std::set<const BookmarkNode*> bookmarks; + bookmarks.insert(AddURLWithTags("foo", tags)); + bookmarks.insert(AddURLWithTags("flou", tags)); + ClearCounts(); + + std::set<BookmarkTag> new_tags; + new_tags.insert(ASCIIToUTF16("new_bar")); + new_tags.insert(ASCIIToUTF16("new_baz")); + + tag_model_->AddTagsToBookmarks(new_tags, bookmarks); + AssertAndClearObserverCount(OBSERVER_COUNTS_BEFORE_TAG_CHANGE, 2); + AssertAndClearObserverCount(OBSERVER_COUNTS_TAG_CHANGE, 2); + AssertAllCountsClear(); + + std::vector<BookmarkTag> alltags(tag_model_->TagsRelatedToTag( + base::string16())); + EXPECT_EQ(4UL, alltags.size()); +} + +TEST_F(BookmarkTagModelTest, AddTagToBookmark) { + std::set<BookmarkTag> tags; + tags.insert(ASCIIToUTF16("bar")); + tags.insert(ASCIIToUTF16("baz")); + + const BookmarkNode* bookmark = AddURLWithTags("foo", tags); + ClearCounts(); + + std::set<BookmarkTag> new_tags; + new_tags.insert(ASCIIToUTF16("new_bar")); + new_tags.insert(ASCIIToUTF16("new_baz")); + + tag_model_->AddTagsToBookmark(new_tags, bookmark); + AssertAndClearObserverCount(OBSERVER_COUNTS_BEFORE_TAG_CHANGE, 1); + AssertAndClearObserverCount(OBSERVER_COUNTS_TAG_CHANGE, 1); + AssertAllCountsClear(); + + std::vector<BookmarkTag> alltags(tag_model_->TagsRelatedToTag( + base::string16())); + EXPECT_EQ(4UL, alltags.size()); +} + +TEST_F(BookmarkTagModelTest, RemoveTagFromBookmarks) { + std::set<BookmarkTag> tags; + tags.insert(ASCIIToUTF16("bar")); + tags.insert(ASCIIToUTF16("baz")); + + std::set<const BookmarkNode*> bookmarks; + + bookmarks.insert(AddURLWithTags("foo", tags)); + bookmarks.insert(AddURLWithTags("flou", tags)); + ClearCounts(); + + tag_model_->RemoveTagsFromBookmarks(tags, bookmarks); + AssertAndClearObserverCount(OBSERVER_COUNTS_BEFORE_TAG_CHANGE, 2); + AssertAndClearObserverCount(OBSERVER_COUNTS_TAG_CHANGE, 2); + AssertAllCountsClear(); + + std::vector<BookmarkTag> alltags(tag_model_->TagsRelatedToTag( + base::string16())); + EXPECT_EQ(0UL, alltags.size()); +} + +TEST_F(BookmarkTagModelTest, RemoveTagFromBookmark) { + std::set<BookmarkTag> tags; + tags.insert(ASCIIToUTF16("bar")); + tags.insert(ASCIIToUTF16("baz")); + + const BookmarkNode* bookmark = AddURLWithTags("foo", tags); + ClearCounts(); + + tag_model_->RemoveTagsFromBookmark(tags, bookmark); + AssertAndClearObserverCount(OBSERVER_COUNTS_BEFORE_TAG_CHANGE, 1); + AssertAndClearObserverCount(OBSERVER_COUNTS_TAG_CHANGE, 1); + AssertAllCountsClear(); + + std::vector<BookmarkTag> alltags(tag_model_->TagsRelatedToTag( + base::string16())); + EXPECT_EQ(0UL, alltags.size()); +} + +TEST_F(BookmarkTagModelTest, RemoveAll) { + std::set<BookmarkTag> tags; + tags.insert(ASCIIToUTF16("bar")); + tags.insert(ASCIIToUTF16("baz")); + + AddURLWithTags("foo", tags); + ClearCounts(); + + model_.RemoveAll(); + AssertAndClearObserverCount(OBSERVER_COUNTS_EXTENSIVE_CHANGE_BEGIN, 1); + AssertAndClearObserverCount(OBSERVER_COUNTS_BEFORE_REMOVE_ALL, 1); + AssertAndClearObserverCount(OBSERVER_COUNTS_REMOVE_ALL, 1); + AssertAndClearObserverCount(OBSERVER_COUNTS_EXTENSIVE_CHANGE_END, 1); + AssertAllCountsClear(); + + std::vector<BookmarkTag> alltags(tag_model_->TagsRelatedToTag( + base::string16())); + EXPECT_EQ(0UL, alltags.size()); +} + +TEST_F(BookmarkTagModelTest, DuplicateFolders) { + const BookmarkNode* left = model_.AddFolder(model_.bookmark_bar_node(), 0, + ASCIIToUTF16("left")); + const BookmarkNode* right = model_.AddFolder(model_.bookmark_bar_node(), 0, + ASCIIToUTF16("right")); + const BookmarkNode* left_handed = model_.AddFolder(left, 0, + ASCIIToUTF16("handed")); + const BookmarkNode* right_handed = model_.AddFolder(right, 0, + ASCIIToUTF16("handed")); + model_.AddURL( + left_handed, 0, ASCIIToUTF16("red"), GURL("http://random.com")); + model_.AddURL( + right_handed, 0, ASCIIToUTF16("der"), GURL("http://random.com")); + + EXPECT_EQ(2UL, tag_model_->BookmarksForTag(ASCIIToUTF16("handed")).size()); + EXPECT_EQ(1UL, tag_model_->BookmarksForTag(ASCIIToUTF16("left")).size()); + EXPECT_EQ(1UL, tag_model_->BookmarksForTag(ASCIIToUTF16("right")).size()); + std::set<BookmarkTag> tags; + tags.insert(ASCIIToUTF16("left")); + tags.insert(ASCIIToUTF16("handed")); + EXPECT_EQ(1UL, tag_model_->BookmarksForTags(tags).size()); +} + +class PreloadedBookmarkTagModelTest : public BookmarkTagModelTest { + public: + PreloadedBookmarkTagModelTest() : BookmarkTagModelTest() { + PopulateUnderlyingModel(); + } + + void PopulateUnderlyingModel() { + ClearCounts(); + top_node_ = model_.AddURL(model_.bookmark_bar_node(), 0, + ASCIIToUTF16("Tagless"), GURL("http://example.com")); + folder_1_ = model_.AddFolder(model_.bookmark_bar_node(), 0, + ASCIIToUTF16("folder1")); + one_tag_ = model_.AddURL(folder_1_, 0, ASCIIToUTF16("OneTag"), + GURL("http://random.com")); + folder_2_ = model_.AddFolder(folder_1_, 0, ASCIIToUTF16("folder2")); + two_tags_ = model_.AddURL(folder_2_, 0, ASCIIToUTF16("TwoTags"), + GURL("http://moveit.com")); + AssertAndClearObserverCount(OBSERVER_COUNTS_ADD, 3); + AssertAllCountsClear(); + } + + void AssertModelMatches() { + EXPECT_EQ(2UL, tag_model_->BookmarksForTag(ASCIIToUTF16("folder1")).size()); + EXPECT_EQ(1UL, tag_model_->BookmarksForTag(ASCIIToUTF16("folder2")).size()); + + std::set<BookmarkTag> tags; + tags.insert(ASCIIToUTF16("folder1")); + EXPECT_EQ(tags, tag_model_->GetTagsForBookmark(one_tag_)); + + tags.insert(ASCIIToUTF16("folder2")); + const BookmarkNode* two_tags = tag_model_-> + GetMostRecentlyAddedBookmarkForURL(GURL("http://moveit.com")); + EXPECT_EQ(tags, tag_model_->GetTagsForBookmark(two_tags)); + + std::vector<BookmarkTag> alltags(tag_model_->TagsRelatedToTag( + base::string16())); + EXPECT_EQ(2UL, alltags.size()); + } + + protected: + const BookmarkNode* folder_1_; + const BookmarkNode* folder_2_; + const BookmarkNode* top_node_; + const BookmarkNode* one_tag_; + const BookmarkNode* two_tags_; + + DISALLOW_COPY_AND_ASSIGN(PreloadedBookmarkTagModelTest); +}; + +TEST_F(PreloadedBookmarkTagModelTest, InitialState) { + AssertAllCountsClear(); + AssertModelMatches(); +} + +TEST_F(PreloadedBookmarkTagModelTest, FromExistingState) { + tag_model_.reset(new BookmarkTagModel(&model_)); + tag_model_->AddObserver(this); + AssertAllCountsClear(); + AssertModelMatches(); +} + +TEST_F(PreloadedBookmarkTagModelTest, BookmarkChange) { + AssertAllCountsClear(); + tag_model_->SetTitle(top_node_, ASCIIToUTF16("newname")); + AssertAndClearObserverCount(OBSERVER_COUNTS_BEFORE_CHANGE, 1); + AssertAndClearObserverCount(OBSERVER_COUNTS_CHANGE, 1); + AssertAllCountsClear(); +} + +TEST_F(PreloadedBookmarkTagModelTest, UnchangedBookmarkMove) { + AssertAllCountsClear(); + model_.Move(top_node_, folder_2_, 0); + AssertAndClearObserverCount(OBSERVER_COUNTS_BEFORE_TAG_CHANGE, 1); + AssertAndClearObserverCount(OBSERVER_COUNTS_TAG_CHANGE, 1); + AssertAllCountsClear(); +} + +TEST_F(PreloadedBookmarkTagModelTest, ChangedBookmarkMove) { + std::set<BookmarkTag> tags; + tags.insert(ASCIIToUTF16("bar")); + + AssertAllCountsClear(); + tag_model_->AddTagsToBookmark(tags, top_node_); + AssertAndClearObserverCount(OBSERVER_COUNTS_BEFORE_TAG_CHANGE, 1); + AssertAndClearObserverCount(OBSERVER_COUNTS_TAG_CHANGE, 1); + AssertAllCountsClear(); + + model_.Move(top_node_, folder_2_, 0); + AssertAllCountsClear(); +} + +TEST_F(PreloadedBookmarkTagModelTest, DuplicateBookmark) { + EXPECT_EQ(2UL, tag_model_->BookmarksForTag(folder_1_->GetTitle()).size()); + model_.AddURL(folder_1_, 0, one_tag_->GetTitle(), one_tag_->url()); + EXPECT_EQ(3UL, tag_model_->BookmarksForTag(folder_1_->GetTitle()).size()); +} + +TEST_F(PreloadedBookmarkTagModelTest, NamelessFolders) { + const BookmarkNode* folder = model_.AddFolder(model_.bookmark_bar_node(), 0, + ASCIIToUTF16("")); + model_.AddURL(folder, 0, ASCIIToUTF16("StillNotag"), + GURL("http://random.com")); + AssertModelMatches(); +} + +TEST_F(PreloadedBookmarkTagModelTest, FolderNameChange) { + EXPECT_EQ(2UL, tag_model_->BookmarksForTag(folder_1_->GetTitle()).size()); + model_.SetTitle(folder_1_, ASCIIToUTF16("Bummer")); + AssertAndClearObserverCount(OBSERVER_COUNTS_EXTENSIVE_CHANGE_BEGIN, 1); + AssertAndClearObserverCount(OBSERVER_COUNTS_EXTENSIVE_CHANGE_END, 1); + AssertAndClearObserverCount(OBSERVER_COUNTS_BEFORE_TAG_CHANGE, 2); + AssertAndClearObserverCount(OBSERVER_COUNTS_TAG_CHANGE, 2); + AssertAllCountsClear(); + + EXPECT_EQ(2UL, tag_model_->BookmarksForTag(folder_1_->GetTitle()).size()); +} + +TEST_F(PreloadedBookmarkTagModelTest, FolderMoved) { + EXPECT_EQ(2UL, tag_model_->BookmarksForTag(folder_1_->GetTitle()).size()); + model_.Move(folder_2_, model_.bookmark_bar_node(), 0); + AssertAndClearObserverCount(OBSERVER_COUNTS_EXTENSIVE_CHANGE_BEGIN, 1); + AssertAndClearObserverCount(OBSERVER_COUNTS_EXTENSIVE_CHANGE_END, 1); + AssertAndClearObserverCount(OBSERVER_COUNTS_BEFORE_TAG_CHANGE, 1); + AssertAndClearObserverCount(OBSERVER_COUNTS_TAG_CHANGE, 1); + AssertAllCountsClear(); + + EXPECT_EQ(1UL, tag_model_->BookmarksForTag(folder_1_->GetTitle()).size()); +} + +} // namespace diff --git a/chrome/browser/browser_encoding_browsertest.cc b/chrome/browser/browser_encoding_browsertest.cc index beca6a06af..1f7a289b16 100644 --- a/chrome/browser/browser_encoding_browsertest.cc +++ b/chrome/browser/browser_encoding_browsertest.cc @@ -221,7 +221,7 @@ IN_PROC_BROWSER_TEST_F(BrowserEncodingTest, TestOverrideEncoding) { // http://crbug.com/2927 for more details. // // This test fails frequently on the win_rel trybot. See http://crbug.com/122053 -#if defined(OS_WIN) || defined(OS_MACOSX) +#if defined(OS_WIN) #define MAYBE_TestEncodingAutoDetect DISABLED_TestEncodingAutoDetect #else #define MAYBE_TestEncodingAutoDetect TestEncodingAutoDetect diff --git a/chrome/browser/browser_keyevents_browsertest.cc b/chrome/browser/browser_keyevents_browsertest.cc index 77f10c4783..0a2580839f 100644 --- a/chrome/browser/browser_keyevents_browsertest.cc +++ b/chrome/browser/browser_keyevents_browsertest.cc @@ -723,10 +723,8 @@ IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, MAYBE_ReservedAccelerators) { // Reserved accelerators can't be suppressed. ASSERT_NO_FATAL_FAILURE(SuppressAllEvents(1, true)); - content::WindowedNotificationObserver wait_for_tab_closed( - content::NOTIFICATION_WEB_CONTENTS_DESTROYED, - content::Source<content::WebContents>( - browser()->tab_strip_model()->GetWebContentsAt(1))); + content::WebContentsDestroyedWatcher destroyed_watcher( + browser()->tab_strip_model()->GetWebContentsAt(1)); // Press Ctrl/Cmd+W, which will close the tab. #if defined(OS_MACOSX) @@ -737,7 +735,7 @@ IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, MAYBE_ReservedAccelerators) { browser(), ui::VKEY_W, true, false, false, false)); #endif - ASSERT_NO_FATAL_FAILURE(wait_for_tab_closed.Wait()); + ASSERT_NO_FATAL_FAILURE(destroyed_watcher.Wait()); EXPECT_EQ(1, browser()->tab_strip_model()->count()); } diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc index 6825a2e2ec..51abfe83a5 100644 --- a/chrome/browser/browser_process_impl.cc +++ b/chrome/browser/browser_process_impl.cc @@ -373,9 +373,12 @@ void BrowserProcessImpl::EndSession() { MetricsService* metrics = g_browser_process->metrics_service(); if (metrics && local_state()) { metrics->RecordStartOfSessionEnd(); - +#if !defined(OS_CHROMEOS) // MetricsService lazily writes to prefs, force it to write now. + // On ChromeOS, chrome gets killed when hangs, so no need to + // commit prefs::kStabilitySessionEndCompleted change immediately. local_state()->CommitPendingWrite(); +#endif } // http://crbug.com/125207 diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd index 346fdb3d12..e45cef652c 100644 --- a/chrome/browser/browser_resources.grd +++ b/chrome/browser/browser_resources.grd @@ -70,6 +70,7 @@ </if> <structure name="IDR_READER_OUT_OF_DATE_HTML" file="resources\reader_out_of_date.html" flattenhtml="true" type="chrome_html" /> <structure name="IDR_SSL_ROAD_BLOCK_HTML" file="resources\ssl\roadblock.html" flattenhtml="true" type="chrome_html" /> + <structure name="IDR_SSL_BLOCKING_HTML" file="resources\ssl\blocking.html" flattenhtml="true" type="chrome_html" /> <structure name="IDR_SAFE_BROWSING_MALWARE_BLOCK_V2" file="resources\safe_browsing\malware_block_v2.html" flattenhtml="true" type="chrome_html" /> </structures> <includes> diff --git a/chrome/browser/captive_portal/captive_portal_tab_helper.cc b/chrome/browser/captive_portal/captive_portal_tab_helper.cc index 46c9af173a..d28f275395 100644 --- a/chrome/browser/captive_portal/captive_portal_tab_helper.cc +++ b/chrome/browser/captive_portal/captive_portal_tab_helper.cc @@ -98,6 +98,7 @@ void CaptivePortalTabHelper::DidStartProvisionalLoadForFrame( void CaptivePortalTabHelper::DidCommitProvisionalLoadForFrame( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& url, content::PageTransition transition_type, @@ -127,6 +128,7 @@ void CaptivePortalTabHelper::DidCommitProvisionalLoadForFrame( void CaptivePortalTabHelper::DidFailProvisionalLoad( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& validated_url, int error_code, diff --git a/chrome/browser/captive_portal/captive_portal_tab_helper.h b/chrome/browser/captive_portal/captive_portal_tab_helper.h index dec16d2a64..5e0a989df3 100644 --- a/chrome/browser/captive_portal/captive_portal_tab_helper.h +++ b/chrome/browser/captive_portal/captive_portal_tab_helper.h @@ -79,6 +79,7 @@ class CaptivePortalTabHelper virtual void DidCommitProvisionalLoadForFrame( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& url, content::PageTransition transition_type, @@ -86,6 +87,7 @@ class CaptivePortalTabHelper virtual void DidFailProvisionalLoad( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& validated_url, int error_code, diff --git a/chrome/browser/captive_portal/captive_portal_tab_helper_unittest.cc b/chrome/browser/captive_portal/captive_portal_tab_helper_unittest.cc index 39c081a051..c4c156d9df 100644 --- a/chrome/browser/captive_portal/captive_portal_tab_helper_unittest.cc +++ b/chrome/browser/captive_portal/captive_portal_tab_helper_unittest.cc @@ -91,7 +91,8 @@ class CaptivePortalTabHelperTest : public ChromeRenderViewHostTestHarness { EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::OK)).Times(1); tab_helper().DidCommitProvisionalLoadForFrame( - 1, true, url, content::PAGE_TRANSITION_LINK, render_view_host); + 1, string16(), true, url, content::PAGE_TRANSITION_LINK, + render_view_host); } // Simulates a connection timeout while requesting |url|. @@ -102,7 +103,8 @@ class CaptivePortalTabHelperTest : public ChromeRenderViewHostTestHarness { 1, -1, true, url, false, false, render_view_host); tab_helper().DidFailProvisionalLoad( - 1, true, url, net::ERR_TIMED_OUT, string16(), render_view_host); + 1, string16(), true, url, net::ERR_TIMED_OUT, string16(), + render_view_host); // Provisional load starts for the error page. tab_helper().DidStartProvisionalLoadForFrame( @@ -110,7 +112,7 @@ class CaptivePortalTabHelperTest : public ChromeRenderViewHostTestHarness { EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::ERR_TIMED_OUT)).Times(1); tab_helper().DidCommitProvisionalLoadForFrame( - 1, true, GURL(kErrorPageUrl), content::PAGE_TRANSITION_LINK, + 1, string16(), true, GURL(kErrorPageUrl), content::PAGE_TRANSITION_LINK, render_view_host); } @@ -125,7 +127,8 @@ class CaptivePortalTabHelperTest : public ChromeRenderViewHostTestHarness { EXPECT_CALL(mock_reloader(), OnAbort()).Times(1); if (navigation_type == kSameProcess) { tab_helper().DidFailProvisionalLoad( - 1, true, url, net::ERR_ABORTED, string16(), render_view_host); + 1, string16(), true, url, net::ERR_ABORTED, string16(), + render_view_host); } else { // For interrupted provisional cross-process navigations, the // RenderViewHost is destroyed without sending a DidFailProvisionalLoad @@ -147,7 +150,8 @@ class CaptivePortalTabHelperTest : public ChromeRenderViewHostTestHarness { 1, -1, true, url, false, false, render_view_host); tab_helper().DidFailProvisionalLoad( - 1, true, url, net::ERR_TIMED_OUT, string16(), render_view_host); + 1, string16(), true, url, net::ERR_TIMED_OUT, string16(), + render_view_host); // Start event for the error page. tab_helper().DidStartProvisionalLoadForFrame( @@ -156,7 +160,8 @@ class CaptivePortalTabHelperTest : public ChromeRenderViewHostTestHarness { EXPECT_CALL(mock_reloader(), OnAbort()).Times(1); if (navigation_type == kSameProcess) { tab_helper().DidFailProvisionalLoad( - 1, true, url, net::ERR_ABORTED, string16(), render_view_host); + 1, string16(), true, url, net::ERR_ABORTED, string16(), + render_view_host); } else { // For interrupted provisional cross-process navigations, the // RenderViewHost is destroyed without sending a DidFailProvisionalLoad @@ -244,7 +249,7 @@ TEST_F(CaptivePortalTabHelperTest, HttpTimeoutLinkDoctor) { EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::OK)).Times(1); tab_helper().DidCommitProvisionalLoadForFrame( - 1, true, GURL(kErrorPageUrl), content::PAGE_TRANSITION_LINK, + 1, string16(), true, GURL(kErrorPageUrl), content::PAGE_TRANSITION_LINK, render_view_host1()); tab_helper().DidStopLoading(render_view_host1()); } @@ -327,12 +332,12 @@ TEST_F(CaptivePortalTabHelperTest, UnexpectedProvisionalLoad) { // The cross-process navigation fails. tab_helper().DidFailProvisionalLoad( - 1, true, cross_process_url, net::ERR_FAILED, string16(), + 1, string16(), true, cross_process_url, net::ERR_FAILED, string16(), render_view_host2()); // The same-site navigation finally is aborted. tab_helper().DidFailProvisionalLoad( - 1, true, same_site_url, net::ERR_ABORTED, string16(), + 1, string16(), true, same_site_url, net::ERR_ABORTED, string16(), render_view_host1()); // The provisional load starts for the error page for the cross-process @@ -342,7 +347,7 @@ TEST_F(CaptivePortalTabHelperTest, UnexpectedProvisionalLoad) { EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::ERR_FAILED)).Times(1); tab_helper().DidCommitProvisionalLoadForFrame( - 1, true, GURL(kErrorPageUrl), content::PAGE_TRANSITION_TYPED, + 1, string16(), true, GURL(kErrorPageUrl), content::PAGE_TRANSITION_TYPED, render_view_host2()); } @@ -369,7 +374,7 @@ TEST_F(CaptivePortalTabHelperTest, UnexpectedCommit) { // The cross-process navigation fails. tab_helper().DidFailProvisionalLoad( - 1, true, cross_process_url, net::ERR_FAILED, string16(), + 1, string16(), true, cross_process_url, net::ERR_FAILED, string16(), render_view_host2()); // The same-site navigation succeeds. @@ -378,7 +383,7 @@ TEST_F(CaptivePortalTabHelperTest, UnexpectedCommit) { OnLoadStart(same_site_url.SchemeIsSecure())).Times(1); EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::OK)).Times(1); tab_helper().DidCommitProvisionalLoadForFrame( - 1, true, same_site_url, content::PAGE_TRANSITION_LINK, + 1, string16(), true, same_site_url, content::PAGE_TRANSITION_LINK, render_view_host1()); } @@ -390,23 +395,27 @@ TEST_F(CaptivePortalTabHelperTest, HttpsSubframe) { tab_helper().DidStartProvisionalLoadForFrame( 1, -1, false, url, false, false, render_view_host1()); tab_helper().DidCommitProvisionalLoadForFrame( - 1, false, url, content::PAGE_TRANSITION_LINK, render_view_host1()); + 1, string16(), false, url, content::PAGE_TRANSITION_LINK, + render_view_host1()); // Timeout. tab_helper().DidStartProvisionalLoadForFrame( 2, -1, false, url, false, false, render_view_host1()); tab_helper().DidFailProvisionalLoad( - 2, false, url, net::ERR_TIMED_OUT, string16(), render_view_host1()); + 2, string16(), false, url, net::ERR_TIMED_OUT, string16(), + render_view_host1()); tab_helper().DidStartProvisionalLoadForFrame( 2, -1, false, url, true, false, render_view_host1()); tab_helper().DidFailProvisionalLoad( - 2, false, url, net::ERR_ABORTED, string16(), render_view_host1()); + 2, string16(), false, url, net::ERR_ABORTED, string16(), + render_view_host1()); // Abort. tab_helper().DidStartProvisionalLoadForFrame( 3, -1, false, url, false, false, render_view_host1()); tab_helper().DidFailProvisionalLoad( - 3, false, url, net::ERR_ABORTED, string16(), render_view_host1()); + 3, string16(), false, url, net::ERR_ABORTED, string16(), + render_view_host1()); } // Simulates a subframe erroring out at the same time as a provisional load, @@ -428,10 +437,10 @@ TEST_F(CaptivePortalTabHelperTest, HttpsSubframeParallelError) { // Loads return errors. tab_helper().DidFailProvisionalLoad( - frame_id, true, url, net::ERR_UNEXPECTED, string16(), + frame_id, string16(), true, url, net::ERR_UNEXPECTED, string16(), render_view_host1()); tab_helper().DidFailProvisionalLoad( - subframe_id, false, url, net::ERR_TIMED_OUT, string16(), + subframe_id, string16(), false, url, net::ERR_TIMED_OUT, string16(), render_view_host1()); // Provisional load starts for the error pages. @@ -442,11 +451,11 @@ TEST_F(CaptivePortalTabHelperTest, HttpsSubframeParallelError) { // Error page load finishes. tab_helper().DidCommitProvisionalLoadForFrame( - subframe_id, false, url, content::PAGE_TRANSITION_AUTO_SUBFRAME, - render_view_host1()); + subframe_id, string16(), false, url, + content::PAGE_TRANSITION_AUTO_SUBFRAME, render_view_host1()); EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::ERR_UNEXPECTED)).Times(1); tab_helper().DidCommitProvisionalLoadForFrame( - frame_id, true, url, content::PAGE_TRANSITION_LINK, + frame_id, string16(), true, url, content::PAGE_TRANSITION_LINK, render_view_host1()); } @@ -463,7 +472,7 @@ TEST_F(CaptivePortalTabHelperTest, HttpToHttpsRedirectTimeout) { render_view_host1()->GetProcess()->GetID()); tab_helper().DidFailProvisionalLoad( - 1, true, https_url, net::ERR_TIMED_OUT, string16(), + 1, string16(), true, https_url, net::ERR_TIMED_OUT, string16(), render_view_host1()); // Provisional load starts for the error page. @@ -472,7 +481,7 @@ TEST_F(CaptivePortalTabHelperTest, HttpToHttpsRedirectTimeout) { EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::ERR_TIMED_OUT)).Times(1); tab_helper().DidCommitProvisionalLoadForFrame( - 1, true, GURL(kErrorPageUrl), content::PAGE_TRANSITION_LINK, + 1, string16(), true, GURL(kErrorPageUrl), content::PAGE_TRANSITION_LINK, render_view_host1()); } @@ -491,7 +500,7 @@ TEST_F(CaptivePortalTabHelperTest, HttpsToHttpRedirect) { EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::OK)).Times(1); tab_helper().DidCommitProvisionalLoadForFrame( - 1, true, http_url, content::PAGE_TRANSITION_LINK, + 1, string16(), true, http_url, content::PAGE_TRANSITION_LINK, render_view_host1()); } @@ -509,7 +518,7 @@ TEST_F(CaptivePortalTabHelperTest, HttpToHttpRedirect) { EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::OK)).Times(1); tab_helper().DidCommitProvisionalLoadForFrame( - 1, true, http_url, content::PAGE_TRANSITION_LINK, + 1, string16(), true, http_url, content::PAGE_TRANSITION_LINK, render_view_host1()); } @@ -526,7 +535,7 @@ TEST_F(CaptivePortalTabHelperTest, SubframeRedirect) { EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::OK)).Times(1); tab_helper().DidCommitProvisionalLoadForFrame( - 1, true, GURL(kErrorPageUrl), content::PAGE_TRANSITION_LINK, + 1, string16(), true, GURL(kErrorPageUrl), content::PAGE_TRANSITION_LINK, render_view_host1()); } @@ -544,7 +553,7 @@ TEST_F(CaptivePortalTabHelperTest, OtherRenderViewHostRedirect) { render_view_host2()->GetProcess()->GetID()); tab_helper().DidFailProvisionalLoad( - 1, true, https_url, net::ERR_TIMED_OUT, string16(), + 1, string16(), true, https_url, net::ERR_TIMED_OUT, string16(), render_view_host1()); // Provisional load starts for the error page. @@ -553,7 +562,7 @@ TEST_F(CaptivePortalTabHelperTest, OtherRenderViewHostRedirect) { EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::ERR_TIMED_OUT)).Times(1); tab_helper().DidCommitProvisionalLoadForFrame( - 1, true, GURL(kErrorPageUrl), content::PAGE_TRANSITION_LINK, + 1, string16(), true, GURL(kErrorPageUrl), content::PAGE_TRANSITION_LINK, render_view_host1()); } diff --git a/chrome/browser/certificate_viewer.h b/chrome/browser/certificate_viewer.h index 940ab15b91..9b31e02213 100644 --- a/chrome/browser/certificate_viewer.h +++ b/chrome/browser/certificate_viewer.h @@ -12,10 +12,8 @@ class WebContents; } namespace net { - class X509Certificate; - -} // namespace net +} // Opens a certificate viewer under |parent| to display the certificate from // the |CertStore| with id |cert_id|. diff --git a/chrome/browser/chrome_browser_field_trials.cc b/chrome/browser/chrome_browser_field_trials.cc index 19a69f63f8..c22f495020 100644 --- a/chrome/browser/chrome_browser_field_trials.cc +++ b/chrome/browser/chrome_browser_field_trials.cc @@ -59,6 +59,7 @@ void ChromeBrowserFieldTrials::InstantiateDynamicTrials() { base::FieldTrialList::FindValue("Test0PercentDefault"); // The following trials are used from renderer process. // Mark here so they will be sync-ed. + base::FieldTrialList::FindValue("CLD1VsCLD2"); base::FieldTrialList::FindValue("DateExtensionEnabled"); base::FieldTrialList::FindValue("MouseEventPreconnect"); // Activate the autocomplete dynamic field trials. diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc index 47e8c9e7e4..0e14596fc4 100644 --- a/chrome/browser/chrome_browser_main.cc +++ b/chrome/browser/chrome_browser_main.cc @@ -53,7 +53,7 @@ #include "chrome/browser/defaults.h" #include "chrome/browser/extensions/extension_protocols.h" #include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/extensions/message_handler.h" +#include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/extensions/startup_helper.h" #include "chrome/browser/first_run/first_run.h" #include "chrome/browser/first_run/upgrade_util.h" @@ -71,6 +71,7 @@ #include "chrome/browser/metrics/tracking_synchronizer.h" #include "chrome/browser/metrics/variations/variations_http_header_provider.h" #include "chrome/browser/metrics/variations/variations_service.h" +#include "chrome/browser/nacl_host/nacl_browser.h" #include "chrome/browser/nacl_host/nacl_browser_delegate_impl.h" #include "chrome/browser/nacl_host/nacl_process_host.h" #include "chrome/browser/net/chrome_net_log.h" @@ -91,7 +92,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profiles_state.h" -#include "chrome/browser/renderer_host/chrome_render_view_host_observer.h" #include "chrome/browser/service/service_process_control.h" #include "chrome/browser/shell_integration.h" #include "chrome/browser/three_d_api_observer.h" @@ -381,6 +381,12 @@ void RegisterComponentsForUpdate(const CommandLine& command_line) { RegisterRecoveryComponent(cus, g_browser_process->local_state()); RegisterPepperFlashComponent(cus); RegisterSwiftShaderComponent(cus); +#endif + + g_browser_process->pnacl_component_installer()->RegisterPnaclComponent( + cus, command_line); + +#if !defined(OS_CHROMEOS) RegisterWidevineCdmComponent(cus); // CRLSetFetcher attempts to load a CRL set from either the local disk or @@ -389,9 +395,6 @@ void RegisterComponentsForUpdate(const CommandLine& command_line) { g_browser_process->crl_set_fetcher()->StartInitialLoad(cus); #endif - g_browser_process->pnacl_component_installer()->RegisterPnaclComponent( - cus, command_line); - cus->Start(); } @@ -500,17 +503,6 @@ class LoadCompleteListener : public content::NotificationObserver { DISALLOW_COPY_AND_ASSIGN(LoadCompleteListener); }; -void RenderViewHostCreated(content::RenderViewHost* render_view_host) { - content::SiteInstance* site_instance = render_view_host->GetSiteInstance(); - Profile* profile = Profile::FromBrowserContext( - site_instance->GetBrowserContext()); - - new ChromeRenderViewHostObserver(render_view_host, - profile->GetNetworkPredictor()); - new extensions::MessageHandler(render_view_host); -} - - } // namespace namespace chrome_browser { @@ -536,7 +528,6 @@ ChromeBrowserMainParts::ChromeBrowserMainParts( shutdown_watcher_(new ShutdownWatcherHelper()), startup_timer_(new performance_monitor::StartupTimer()), browser_field_trials_(parameters.command_line), - rvh_callback_(base::Bind(&RenderViewHostCreated)), translate_manager_(NULL), profile_(NULL), run_message_loop_(true), @@ -552,13 +543,9 @@ ChromeBrowserMainParts::ChromeBrowserMainParts( // a ChromeNetworkDelegate attached that selectively allows cookies again. if (!disable_enforcing_cookie_policies_for_tests_) net::URLRequest::SetDefaultCookiePolicyToBlock(); - - content::RenderViewHost::AddCreatedCallback(rvh_callback_); } ChromeBrowserMainParts::~ChromeBrowserMainParts() { - content::RenderViewHost::RemoveCreatedCallback(rvh_callback_); - for (int i = static_cast<int>(chrome_extra_parts_.size())-1; i >= 0; --i) delete chrome_extra_parts_[i]; chrome_extra_parts_.clear(); @@ -1274,6 +1261,14 @@ int ChromeBrowserMainParts::PreMainMessageLoopRunImpl() { content::WebUIControllerFactory::RegisterFactory( ChromeWebUIControllerFactory::GetInstance()); + // NaClBrowserDelegateImpl is accessed inside PostProfileInit(). + // So make sure to create it before that. +#if !defined(DISABLE_NACL) + NaClBrowserDelegateImpl* delegate = new NaClBrowserDelegateImpl( + extensions::ExtensionSystem::Get(profile_)->info_map()); + NaClBrowser::SetDelegate(delegate); +#endif + // TODO(stevenjb): Move WIN and MACOSX specific code to appropriate Parts. // (requires supporting early exit). PostProfileInit(); @@ -1440,7 +1435,11 @@ int ChromeBrowserMainParts::PreMainMessageLoopRunImpl() { parsed_command_line().GetSwitchValuePath( switches::kPnaclDir)); } - NaClProcessHost::EarlyStartup(new NaClBrowserDelegateImpl); + + content::BrowserThread::PostTask( + content::BrowserThread::IO, + FROM_HERE, + base::Bind(NaClProcessHost::EarlyStartup)); #endif // Make sure initial prefs are recorded diff --git a/chrome/browser/chrome_browser_main.h b/chrome/browser/chrome_browser_main.h index 22c4526bd9..cf5cd7aee3 100644 --- a/chrome/browser/chrome_browser_main.h +++ b/chrome/browser/chrome_browser_main.h @@ -16,7 +16,6 @@ #include "chrome/browser/task_profiler/auto_tracking.h" #include "chrome/browser/ui/startup/startup_browser_creator.h" #include "content/public/browser/browser_main_parts.h" -#include "content/public/browser/render_view_host.h" #include "content/public/common/main_function_params.h" class ActiveTabTracker; @@ -154,8 +153,6 @@ class ChromeBrowserMainParts : public content::BrowserMainParts { ChromeBrowserFieldTrials browser_field_trials_; - content::RenderViewHost::CreatedCallback rvh_callback_; - // Vector of additional ChromeBrowserMainExtraParts. // Parts are deleted in the inverse order they are added. std::vector<ChromeBrowserMainExtraParts*> chrome_extra_parts_; diff --git a/chrome/browser/chrome_browser_main_android.cc b/chrome/browser/chrome_browser_main_android.cc index bf96e89768..0acfb7ed5c 100644 --- a/chrome/browser/chrome_browser_main_android.cc +++ b/chrome/browser/chrome_browser_main_android.cc @@ -8,9 +8,10 @@ #include "base/debug/trace_event.h" #include "base/path_service.h" #include "cc/base/switches.h" -#include "chrome/app/breakpad_linux.h" -#include "chrome/browser/android/crash_dump_manager.h" +#include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" +#include "components/breakpad/app/breakpad_linux.h" +#include "components/breakpad/browser/crash_dump_manager_android.h" #include "content/public/browser/android/compositor.h" #include "content/public/common/main_function_params.h" #include "net/android/network_change_notifier_factory_android.h" @@ -43,8 +44,10 @@ void ChromeBrowserMainPartsAndroid::PreProfileInit() { switches::kEnableCrashReporterForTesting); if (breakpad_enabled) { - InitCrashReporter(); - crash_dump_manager_.reset(new CrashDumpManager()); + breakpad::InitCrashReporter(); + base::FilePath crash_dump_dir; + PathService::Get(chrome::DIR_CRASH_DUMPS, &crash_dump_dir); + crash_dump_manager_.reset(new breakpad::CrashDumpManager(crash_dump_dir)); } ChromeBrowserMainParts::PreProfileInit(); diff --git a/chrome/browser/chrome_browser_main_android.h b/chrome/browser/chrome_browser_main_android.h index b498db5113..a7f31a9327 100644 --- a/chrome/browser/chrome_browser_main_android.h +++ b/chrome/browser/chrome_browser_main_android.h @@ -7,7 +7,9 @@ #include "chrome/browser/chrome_browser_main.h" +namespace breakpad { class CrashDumpManager; +} class ChromeBrowserMainPartsAndroid : public ChromeBrowserMainParts { public: @@ -24,7 +26,7 @@ class ChromeBrowserMainPartsAndroid : public ChromeBrowserMainParts { private: scoped_ptr<base::MessageLoop> main_message_loop_; - scoped_ptr<CrashDumpManager> crash_dump_manager_; + scoped_ptr<breakpad::CrashDumpManager> crash_dump_manager_; DISALLOW_COPY_AND_ASSIGN(ChromeBrowserMainPartsAndroid); }; diff --git a/chrome/browser/chrome_browser_main_linux.cc b/chrome/browser/chrome_browser_main_linux.cc index 5e0cf2a0a6..835afa4ac1 100644 --- a/chrome/browser/chrome_browser_main_linux.cc +++ b/chrome/browser/chrome_browser_main_linux.cc @@ -9,12 +9,12 @@ #include "base/command_line.h" #include "base/linux_util.h" #include "base/prefs/pref_service.h" -#include "chrome/app/breakpad_linux.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/metrics/metrics_service.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/env_vars.h" #include "chrome/common/pref_names.h" +#include "components/breakpad/app/breakpad_linux.h" #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/settings/cros_settings.h" @@ -122,7 +122,7 @@ void ChromeBrowserMainPartsLinux::PreProfileInit() { #endif if (IsCrashReportingEnabled(local_state())) - InitCrashReporter(); + breakpad::InitCrashReporter(); ChromeBrowserMainPartsPosix::PreProfileInit(); } @@ -131,5 +131,5 @@ void ChromeBrowserMainPartsLinux::PostProfileInit() { ChromeBrowserMainPartsPosix::PostProfileInit(); g_browser_process->metrics_service()->RecordBreakpadRegistration( - IsCrashReporterEnabled()); + breakpad::IsCrashReporterEnabled()); } diff --git a/chrome/browser/chrome_browser_main_mac.mm b/chrome/browser/chrome_browser_main_mac.mm index 98b225e020..1cea6afb96 100644 --- a/chrome/browser/chrome_browser_main_mac.mm +++ b/chrome/browser/chrome_browser_main_mac.mm @@ -23,7 +23,7 @@ #include "chrome/browser/metrics/metrics_service.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" -#include "components/breakpad/breakpad_mac.h" +#include "components/breakpad/app/breakpad_mac.h" #include "content/public/common/main_function_params.h" #include "content/public/common/result_codes.h" #include "ui/base/l10n/l10n_util_mac.h" diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index 49fe6b1d5b..e4347f8edd 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc @@ -48,6 +48,8 @@ #include "chrome/browser/guestview/webview/webview_guest.h" #include "chrome/browser/media/media_capture_devices_dispatcher.h" #include "chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.h" +#include "chrome/browser/nacl_host/nacl_browser.h" +#include "chrome/browser/nacl_host/nacl_browser_delegate_impl.h" #include "chrome/browser/nacl_host/nacl_host_message_filter.h" #include "chrome/browser/nacl_host/nacl_process_host.h" #include "chrome/browser/net/chrome_net_log.h" @@ -91,6 +93,7 @@ #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/env_vars.h" #include "chrome/common/extensions/background_info.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_process_policy.h" @@ -149,7 +152,7 @@ #elif defined(OS_MACOSX) #include "chrome/browser/chrome_browser_main_mac.h" #include "chrome/browser/spellchecker/spellcheck_message_filter_mac.h" -#include "components/breakpad/breakpad_mac.h" +#include "components/breakpad/app/breakpad_mac.h" #elif defined(OS_CHROMEOS) #include "chrome/browser/chromeos/chrome_browser_main_chromeos.h" #include "chrome/browser/chromeos/drive/file_system_backend_delegate.h" @@ -161,19 +164,20 @@ #elif defined(OS_LINUX) #include "chrome/browser/chrome_browser_main_linux.h" #elif defined(OS_ANDROID) -#include "chrome/browser/android/crash_dump_manager.h" #include "chrome/browser/android/webapps/single_tab_mode_tab_helper.h" #include "chrome/browser/chrome_browser_main_android.h" #include "chrome/browser/media/encrypted_media_message_filter_android.h" #include "chrome/common/descriptors_android.h" +#include "components/breakpad/browser/crash_dump_manager_android.h" #elif defined(OS_POSIX) #include "chrome/browser/chrome_browser_main_posix.h" #endif #if defined(OS_POSIX) && !defined(OS_MACOSX) +#include "base/debug/leak_annotations.h" #include "base/linux_util.h" -#include "chrome/app/breakpad_linux.h" -#include "chrome/browser/crash_handler_host_linux.h" +#include "components/breakpad/app/breakpad_linux.h" +#include "components/breakpad/browser/crash_handler_host_linux.h" #endif #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) @@ -182,6 +186,7 @@ #if defined(OS_ANDROID) #include "ui/base/ui_base_paths.h" +#include "ui/gfx/android/device_display_info.h" #endif #if defined(USE_NSS) @@ -250,10 +255,6 @@ using extensions::Extension; using extensions::Manifest; using message_center::NotifierId; -#if defined(OS_MACOSX) -using breakpad::IsCrashReporterEnabled; -#endif - namespace { // Cached version of the locale so we can return the locale on the I/O @@ -479,6 +480,8 @@ bool CertMatchesFilter(const net::X509Certificate& cert, void FillFontFamilyMap(const PrefService* prefs, const char* map_name, webkit_glue::ScriptFontFamilyMap* map) { + // TODO: Get rid of the brute-force scan over possible (font family / script) + // combinations - see http://crbug.com/308095. for (size_t i = 0; i < prefs::kWebKitScriptsForFontFamilyMapsLength; ++i) { const char* script = prefs::kWebKitScriptsForFontFamilyMaps[i]; std::string pref_name = base::StringPrintf("%s.%s", map_name, script); @@ -489,27 +492,58 @@ void FillFontFamilyMap(const PrefService* prefs, } #if defined(OS_POSIX) && !defined(OS_MACOSX) +breakpad::CrashHandlerHostLinux* CreateCrashHandlerHost( + const std::string& process_type) { + base::FilePath dumps_path; + PathService::Get(chrome::DIR_CRASH_DUMPS, &dumps_path); + { + ANNOTATE_SCOPED_MEMORY_LEAK; + breakpad::CrashHandlerHostLinux* crash_handler = + new breakpad::CrashHandlerHostLinux( + process_type, dumps_path, getenv(env_vars::kHeadless) == NULL); + crash_handler->StartUploaderThread(); + return crash_handler; + } +} + int GetCrashSignalFD(const CommandLine& command_line) { if (command_line.HasSwitch(switches::kExtensionProcess)) { - ExtensionCrashHandlerHostLinux* crash_handler = - ExtensionCrashHandlerHostLinux::GetInstance(); + static breakpad::CrashHandlerHostLinux* crash_handler = NULL; + if (!crash_handler) + crash_handler = CreateCrashHandlerHost("extension"); return crash_handler->GetDeathSignalSocket(); } std::string process_type = command_line.GetSwitchValueASCII(switches::kProcessType); - if (process_type == switches::kRendererProcess) - return RendererCrashHandlerHostLinux::GetInstance()->GetDeathSignalSocket(); + if (process_type == switches::kRendererProcess) { + static breakpad::CrashHandlerHostLinux* crash_handler = NULL; + if (!crash_handler) + crash_handler = CreateCrashHandlerHost(process_type); + return crash_handler->GetDeathSignalSocket(); + } - if (process_type == switches::kPluginProcess) - return PluginCrashHandlerHostLinux::GetInstance()->GetDeathSignalSocket(); + if (process_type == switches::kPluginProcess) { + static breakpad::CrashHandlerHostLinux* crash_handler = NULL; + if (!crash_handler) + crash_handler = CreateCrashHandlerHost(process_type); + return crash_handler->GetDeathSignalSocket(); + } - if (process_type == switches::kPpapiPluginProcess) - return PpapiCrashHandlerHostLinux::GetInstance()->GetDeathSignalSocket(); + if (process_type == switches::kPpapiPluginProcess) { + static breakpad::CrashHandlerHostLinux* crash_handler = NULL; + if (!crash_handler) + crash_handler = CreateCrashHandlerHost(process_type); + return crash_handler->GetDeathSignalSocket(); + } - if (process_type == switches::kGpuProcess) - return GpuCrashHandlerHostLinux::GetInstance()->GetDeathSignalSocket(); + if (process_type == switches::kGpuProcess) { + static breakpad::CrashHandlerHostLinux* crash_handler = NULL; + if (!crash_handler) + crash_handler = CreateCrashHandlerHost(process_type); + return crash_handler->GetDeathSignalSocket(); + } return -1; } @@ -549,6 +583,7 @@ void HandleBlockedPopupOnUIThread(const BlockedWindowParams& params) { } #if defined(OS_ANDROID) + void HandleSingleTabModeBlockOnUIThread(const BlockedWindowParams& params) { WebContents* web_contents = tab_util::GetWebContentsByID(params.render_process_id(), @@ -558,6 +593,33 @@ void HandleSingleTabModeBlockOnUIThread(const BlockedWindowParams& params) { SingleTabModeTabHelper::FromWebContents(web_contents)->HandleOpenUrl(params); } + +float GetFontScaleMultiplier(const PrefService* prefs) { + if (prefs->GetBoolean(prefs::kWebKitFontScaleFactorQuirk)) { + // The value of kWebKitFontScaleFactor passed by Chrome for Android already + // includes the multiplier. + return 1.0f; + } + + static const float kMinFSM = 1.05f; + static const int kWidthForMinFSM = 320; + static const float kMaxFSM = 1.3f; + static const int kWidthForMaxFSM = 800; + + gfx::DeviceDisplayInfo info; + int minWidth = info.GetSmallestDIPWidth(); + + if (minWidth <= kWidthForMinFSM) + return kMinFSM; + if (minWidth >= kWidthForMaxFSM) + return kMaxFSM; + + // The font scale multiplier varies linearly between kMinFSM and kMaxFSM. + float ratio = static_cast<float>(minWidth - kWidthForMinFSM) / + (kWidthForMaxFSM - kWidthForMinFSM); + return ratio * (kMaxFSM - kMinFSM) + kMinFSM; +} + #endif // defined(OS_ANDROID) } // namespace @@ -863,17 +925,15 @@ void ChromeContentBrowserClient::RenderProcessHostCreated( host->AddFilter(new TtsMessageFilter(id, profile)); #if defined(ENABLE_WEBRTC) WebRtcLoggingHandlerHost* webrtc_logging_handler_host = - new WebRtcLoggingHandlerHost(); + new WebRtcLoggingHandlerHost(profile); host->AddFilter(webrtc_logging_handler_host); host->SetUserData(host, new base::UserDataAdapter<WebRtcLoggingHandlerHost>( webrtc_logging_handler_host)); #endif #if !defined(DISABLE_NACL) - ExtensionInfoMap* extension_info_map = - extensions::ExtensionSystem::Get(profile)->info_map(); host->AddFilter(new NaClHostMessageFilter( id, profile->IsOffTheRecord(), - profile->GetPath(), extension_info_map, + profile->GetPath(), context)); #endif #if defined(OS_ANDROID) @@ -1333,7 +1393,7 @@ std::string ChromeContentBrowserClient::GetCanonicalEncodingNameByAliasName( void ChromeContentBrowserClient::AppendExtraCommandLineSwitches( CommandLine* command_line, int child_process_id) { #if defined(OS_POSIX) - if (IsCrashReporterEnabled()) { + if (breakpad::IsCrashReporterEnabled()) { std::string enable_crash_reporter; GoogleUpdateSettings::GetMetricsId(&enable_crash_reporter); #if !defined(OS_MACOSX) @@ -1424,6 +1484,7 @@ void ChromeContentBrowserClient::AppendExtraCommandLineSwitches( autofill::switches::kEnableExperimentalFormFilling, autofill::switches::kEnableInteractiveAutocomplete, autofill::switches::kEnablePasswordGeneration, + autofill::switches::kNoAutofillNecessaryForPasswordGeneration, extensions::switches::kAllowLegacyExtensionManifests, extensions::switches::kAllowScriptingGallery, extensions::switches::kEnableExperimentalExtensionApis, @@ -1444,7 +1505,6 @@ void ChromeContentBrowserClient::AppendExtraCommandLineSwitches( switches::kEnableAdviewSrcAttribute, switches::kEnableAppWindowControls, switches::kEnableBenchmarking, - switches::kEnableIPCFuzzing, switches::kEnableNaCl, switches::kEnableNetBenchmarking, switches::kEnableWatchdog, @@ -2090,6 +2150,9 @@ void ChromeContentBrowserClient::OverrideWebkitPrefs( rvh->GetProcess()->GetBrowserContext()); PrefService* prefs = profile->GetPrefs(); + // Fill per-script font preferences. These are not registered on Android + // - http://crbug.com/308033. +#if !defined(OS_ANDROID) FillFontFamilyMap(prefs, prefs::kWebKitStandardFontFamilyMap, &web_prefs->standard_font_family_map); FillFontFamilyMap(prefs, prefs::kWebKitFixedFontFamilyMap, @@ -2104,6 +2167,7 @@ void ChromeContentBrowserClient::OverrideWebkitPrefs( &web_prefs->fantasy_font_family_map); FillFontFamilyMap(prefs, prefs::kWebKitPictographFontFamilyMap, &web_prefs->pictograph_font_family_map); +#endif web_prefs->default_font_size = prefs->GetInteger(prefs::kWebKitDefaultFontSize); @@ -2157,8 +2221,9 @@ void ChromeContentBrowserClient::OverrideWebkitPrefs( web_prefs->allow_running_insecure_content = prefs->GetBoolean(prefs::kWebKitAllowRunningInsecureContent); #if defined(OS_ANDROID) - web_prefs->font_scale_factor = - static_cast<float>(prefs->GetDouble(prefs::kWebKitFontScaleFactor)); + web_prefs->text_autosizing_font_scale_factor = + static_cast<float>(prefs->GetDouble(prefs::kWebKitFontScaleFactor)) * + GetFontScaleMultiplier(prefs); web_prefs->force_enable_zoom = prefs->GetBoolean(prefs::kWebKitForceEnableZoom); #endif @@ -2493,8 +2558,9 @@ void ChromeContentBrowserClient::GetAdditionalMappedFilesForChildProcess( mappings->push_back(FileDescriptorInfo(kAndroidUIResourcesPakDescriptor, FileDescriptor(f, true))); - if (IsCrashReporterEnabled()) { - f = CrashDumpManager::GetInstance()->CreateMinidumpFile(child_process_id); + if (breakpad::IsCrashReporterEnabled()) { + f = breakpad::CrashDumpManager::GetInstance()->CreateMinidumpFile( + child_process_id); if (f == base::kInvalidPlatformFileValue) { LOG(ERROR) << "Failed to create file for minidump, crash reporting will " "be disabled for this process."; diff --git a/chrome/browser/chromeos/accessibility/sticky_keys_browsertest.cc b/chrome/browser/chromeos/accessibility/sticky_keys_browsertest.cc new file mode 100644 index 0000000000..8743980e43 --- /dev/null +++ b/chrome/browser/chromeos/accessibility/sticky_keys_browsertest.cc @@ -0,0 +1,144 @@ +// Copyright 2013 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 "ash/shell.h" +#include "ash/system/tray/system_tray.h" +#include "base/command_line.h" +#include "base/prefs/pref_service.h" +#include "chrome/browser/chromeos/accessibility/accessibility_manager.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/view_ids.h" +#include "chrome/common/pref_names.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/interactive_test_utils.h" +#include "ui/aura/root_window.h" +#include "ui/events/keycodes/keyboard_codes.h" +#include "ui/gfx/native_widget_types.h" + +namespace chromeos { + +class StickyKeysBrowserTest : public InProcessBrowserTest { + protected: + StickyKeysBrowserTest() {} + virtual ~StickyKeysBrowserTest() {} + + void EnableStickyKeys() { + AccessibilityManager::Get()->EnableStickyKeys(true); + } + + void DisableStickyKeys() { + AccessibilityManager::Get()->EnableStickyKeys(false); + } + + ash::SystemTray* GetSystemTray() { + return ash::Shell::GetInstance()->GetPrimarySystemTray(); + } + + void SendKeyPress(ui::KeyboardCode key) { + gfx::NativeWindow root_window = + ash::Shell::GetInstance()->GetPrimaryRootWindow(); + ASSERT_TRUE( + ui_test_utils::SendKeyPressToWindowSync(root_window, + key, + false, // control + false, // shift + false, // alt + false)); // command + } + + content::NotificationRegistrar registrar_; + + DISALLOW_COPY_AND_ASSIGN(StickyKeysBrowserTest); +}; + +IN_PROC_BROWSER_TEST_F(StickyKeysBrowserTest, OpenTrayMenu) { + EnableStickyKeys(); + + // Open system tray bubble with shortcut. + SendKeyPress(ui::VKEY_MENU); // alt key. + SendKeyPress(ui::VKEY_SHIFT); + SendKeyPress(ui::VKEY_S); + EXPECT_TRUE(GetSystemTray()->HasSystemBubble()); + + // Hide system bubble. + GetSystemTray()->CloseSystemBubble(); + EXPECT_FALSE(GetSystemTray()->HasSystemBubble()); + + // Pressing S again should not reopen the bubble. + SendKeyPress(ui::VKEY_S); + EXPECT_FALSE(GetSystemTray()->HasSystemBubble()); + + // With sticky keys disabled, we will fail to perform the shortcut. + DisableStickyKeys(); + SendKeyPress(ui::VKEY_MENU); // alt key. + SendKeyPress(ui::VKEY_SHIFT); + SendKeyPress(ui::VKEY_S); + EXPECT_FALSE(GetSystemTray()->HasSystemBubble()); +} + +IN_PROC_BROWSER_TEST_F(StickyKeysBrowserTest, OpenNewTabs) { + // Lock the modifier key. + EnableStickyKeys(); + SendKeyPress(ui::VKEY_CONTROL); + SendKeyPress(ui::VKEY_CONTROL); + + // In the locked state, pressing 't' should open a new tab each time. + TabStripModel* tab_strip_model = browser()->tab_strip_model(); + int tab_count = 1; + for (; tab_count < 5; ++tab_count) { + EXPECT_EQ(tab_count, tab_strip_model->count()); + SendKeyPress(ui::VKEY_T); + } + + // Unlock the modifier key and shortcut should no longer activate. + SendKeyPress(ui::VKEY_CONTROL); + SendKeyPress(ui::VKEY_T); + EXPECT_EQ(tab_count, tab_strip_model->count()); + + // Shortcut should not work after disabling sticky keys. + DisableStickyKeys(); + SendKeyPress(ui::VKEY_CONTROL); + SendKeyPress(ui::VKEY_CONTROL); + SendKeyPress(ui::VKEY_T); + EXPECT_EQ(tab_count, tab_strip_model->count()); +} + +// TODO(tengs): Enable this test once sticky keys has been fixed to +// support mouse events (crbug.com/308659). +IN_PROC_BROWSER_TEST_F(StickyKeysBrowserTest, DISABLED_CtrlClickHomeButton) { + // Show home page button. + browser()->profile()->GetPrefs()->SetBoolean(prefs::kShowHomeButton, true); + TabStripModel* tab_strip_model = browser()->tab_strip_model(); + int tab_count = 1; + EXPECT_EQ(tab_count, tab_strip_model->count()); + + // Test sticky keys with modified mouse click action. + EnableStickyKeys(); + SendKeyPress(ui::VKEY_CONTROL); + ui_test_utils::ClickOnView(browser(), VIEW_ID_HOME_BUTTON); + EXPECT_EQ(++tab_count, tab_strip_model->count()); + ui_test_utils::ClickOnView(browser(), VIEW_ID_HOME_BUTTON); + EXPECT_EQ(tab_count, tab_strip_model->count()); + + // Test locked modifier key with mouse click. + SendKeyPress(ui::VKEY_CONTROL); + SendKeyPress(ui::VKEY_CONTROL); + for (; tab_count < 5; ++tab_count) { + ui_test_utils::ClickOnView(browser(), VIEW_ID_HOME_BUTTON); + EXPECT_EQ(tab_count, tab_strip_model->count()); + } + SendKeyPress(ui::VKEY_CONTROL); + ui_test_utils::ClickOnView(browser(), VIEW_ID_HOME_BUTTON); + EXPECT_EQ(tab_count, tab_strip_model->count()); + + // Test disabling sticky keys prevent modified mouse click. + DisableStickyKeys(); + SendKeyPress(ui::VKEY_CONTROL); + ui_test_utils::ClickOnView(browser(), VIEW_ID_HOME_BUTTON); + EXPECT_EQ(tab_count, tab_strip_model->count()); +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/app_mode/kiosk_profile_loader.cc b/chrome/browser/chromeos/app_mode/kiosk_profile_loader.cc index 2cb1a73354..a354ed0767 100644 --- a/chrome/browser/chromeos/app_mode/kiosk_profile_loader.cc +++ b/chrome/browser/chromeos/app_mode/kiosk_profile_loader.cc @@ -180,17 +180,13 @@ void KioskProfileLoader::ReportLaunchResult(KioskAppLaunchError::Error error) { } } -void KioskProfileLoader::OnLoginSuccess( - const UserContext& user_context, - bool pending_requests, - bool using_oauth) { +void KioskProfileLoader::OnLoginSuccess(const UserContext& user_context) { // LoginPerformer will delete itself. login_performer_->set_delegate(NULL); ignore_result(login_performer_.release()); LoginUtils::Get()->PrepareProfile(user_context, std::string(), // display email - false, // using_oauth false, // has_cookies false, // has_active_session this); diff --git a/chrome/browser/chromeos/app_mode/kiosk_profile_loader.h b/chrome/browser/chromeos/app_mode/kiosk_profile_loader.h index 46d47eb761..9f28103f5e 100644 --- a/chrome/browser/chromeos/app_mode/kiosk_profile_loader.h +++ b/chrome/browser/chromeos/app_mode/kiosk_profile_loader.h @@ -51,10 +51,7 @@ class KioskProfileLoader : public LoginPerformer::Delegate, void ReportLaunchResult(KioskAppLaunchError::Error error); // LoginPerformer::Delegate overrides - virtual void OnLoginSuccess( - const UserContext& user_context, - bool pending_requests, - bool using_oauth) OVERRIDE; + virtual void OnLoginSuccess(const UserContext& user_context) OVERRIDE; virtual void OnLoginFailure(const LoginFailure& error) OVERRIDE; virtual void WhiteListCheckFailed(const std::string& email) OVERRIDE; virtual void PolicyLoadFailed() OVERRIDE; diff --git a/chrome/browser/chromeos/app_mode/startup_app_launcher.cc b/chrome/browser/chromeos/app_mode/startup_app_launcher.cc index 01fd0f5fc8..da60c3c1ef 100644 --- a/chrome/browser/chromeos/app_mode/startup_app_launcher.cc +++ b/chrome/browser/chromeos/app_mode/startup_app_launcher.cc @@ -27,7 +27,7 @@ #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/manifest_handlers/kiosk_mode_info.h" -#include "chromeos/cryptohome/cryptohome_library.h" +#include "chromeos/cryptohome/system_salt_getter.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h" #include "google_apis/gaia/gaia_auth_consumer.h" @@ -284,7 +284,7 @@ void StartupAppLauncher::OnReadyToLaunch() { // Defer app launch until system salt is loaded to make sure that identity // api works with the enterprise kiosk app. // TODO(xiyuan): Use async GetSystemSalt after merging to M31. - const std::string system_salt = CryptohomeLibrary::Get()->GetSystemSaltSync(); + const std::string system_salt = SystemSaltGetter::Get()->GetSystemSaltSync(); if (system_salt.empty()) { const int64 kRequestSystemSaltDelayMs = 500; BrowserThread::PostDelayedTask( diff --git a/chrome/browser/chromeos/attestation/OWNERS b/chrome/browser/chromeos/attestation/OWNERS index cd1c574925..a48744dcca 100644 --- a/chrome/browser/chromeos/attestation/OWNERS +++ b/chrome/browser/chromeos/attestation/OWNERS @@ -1,2 +1,3 @@ mnissler@chromium.org pastarmovj@chromium.org +bartfab@chromium.org diff --git a/chrome/browser/chromeos/attestation/attestation_policy_observer.cc b/chrome/browser/chromeos/attestation/attestation_policy_observer.cc index 2734706100..9aab2644c2 100644 --- a/chrome/browser/chromeos/attestation/attestation_policy_observer.cc +++ b/chrome/browser/chromeos/attestation/attestation_policy_observer.cc @@ -27,8 +27,6 @@ namespace { -const char kEnterpriseMachineKey[] = "attest-ent-machine"; - // The number of days before a certificate expires during which it is // considered 'expiring soon' and replacement is initiated. The Chrome OS CA // issues certificates with an expiry of at least two years. This value has @@ -172,6 +170,7 @@ void AttestationPolicyObserver::Start() { weak_factory_.GetWeakPtr()); cryptohome_client_->TpmAttestationDoesKeyExist( KEY_DEVICE, + std::string(), // Not used. kEnterpriseMachineKey, base::Bind(DBusBoolRedirectCallback, on_does_exist, @@ -200,6 +199,7 @@ void AttestationPolicyObserver::GetNewCertificate() { void AttestationPolicyObserver::GetExistingCertificate() { cryptohome_client_->TpmAttestationGetCertificate( KEY_DEVICE, + std::string(), // Not used. kEnterpriseMachineKey, base::Bind(DBusStringCallback, base::Bind(&AttestationPolicyObserver::CheckCertificateExpiry, @@ -257,6 +257,7 @@ void AttestationPolicyObserver::GetKeyPayload( base::Callback<void(const std::string&)> callback) { cryptohome_client_->TpmAttestationGetKeyPayload( KEY_DEVICE, + std::string(), // Not used. kEnterpriseMachineKey, base::Bind(DBusStringCallback, callback, @@ -285,6 +286,7 @@ void AttestationPolicyObserver::MarkAsUploaded(const std::string& key_payload) { } cryptohome_client_->TpmAttestationSetKeyPayload( KEY_DEVICE, + std::string(), // Not used. kEnterpriseMachineKey, new_payload, base::Bind(DBusBoolRedirectCallback, diff --git a/chrome/browser/chromeos/attestation/attestation_policy_observer_unittest.cc b/chrome/browser/chromeos/attestation/attestation_policy_observer_unittest.cc index 8e592dc4d7..6338ca9c12 100644 --- a/chrome/browser/chromeos/attestation/attestation_policy_observer_unittest.cc +++ b/chrome/browser/chromeos/attestation/attestation_policy_observer_unittest.cc @@ -148,20 +148,20 @@ class AttestationPolicyObserverTest : public ::testing::Test { bool key_exists = (mock_options & MOCK_KEY_EXISTS); // Setup expected key / cert queries. if (key_exists) { - EXPECT_CALL(cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _)) - .WillRepeatedly(WithArgs<2>(Invoke(DBusCallbackTrue))); - EXPECT_CALL(cryptohome_client_, TpmAttestationGetCertificate(_, _, _)) - .WillRepeatedly(WithArgs<2>(Invoke(FakeDBusData(certificate)))); + EXPECT_CALL(cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _, _)) + .WillRepeatedly(WithArgs<3>(Invoke(DBusCallbackTrue))); + EXPECT_CALL(cryptohome_client_, TpmAttestationGetCertificate(_, _, _, _)) + .WillRepeatedly(WithArgs<3>(Invoke(FakeDBusData(certificate)))); } else { - EXPECT_CALL(cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _)) - .WillRepeatedly(WithArgs<2>(Invoke(DBusCallbackFalse))); + EXPECT_CALL(cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _, _)) + .WillRepeatedly(WithArgs<3>(Invoke(DBusCallbackFalse))); } // Setup expected key payload queries. bool key_uploaded = (mock_options & MOCK_KEY_UPLOADED); std::string payload = CreatePayload(); - EXPECT_CALL(cryptohome_client_, TpmAttestationGetKeyPayload(_, _, _)) - .WillRepeatedly(WithArgs<2>(Invoke( + EXPECT_CALL(cryptohome_client_, TpmAttestationGetKeyPayload(_, _, _, _)) + .WillRepeatedly(WithArgs<3>(Invoke( FakeDBusData(key_uploaded ? payload : "")))); // Setup expected key uploads. Use WillOnce() so StrictMock will trigger an @@ -175,8 +175,8 @@ class AttestationPolicyObserverTest : public ::testing::Test { UploadCertificate(new_key ? "fake_cert" : certificate, _)) .WillOnce(WithArgs<1>(Invoke(StatusCallbackSuccess))); EXPECT_CALL(cryptohome_client_, - TpmAttestationSetKeyPayload(_, _, payload, _)) - .WillOnce(WithArgs<3>(Invoke(DBusCallbackTrue))); + TpmAttestationSetKeyPayload(_, _, _, payload, _)) + .WillOnce(WithArgs<4>(Invoke(DBusCallbackTrue))); } // Setup expected key generations. Again use WillOnce(). Key generation is @@ -297,9 +297,9 @@ TEST_F(AttestationPolicyObserverTest, IgnoreUnknownCertFormat) { TEST_F(AttestationPolicyObserverTest, DBusFailureRetry) { SetupMocks(MOCK_NEW_KEY, ""); // Simulate a DBus failure. - EXPECT_CALL(cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _)) - .WillOnce(WithArgs<2>(Invoke(DBusCallbackError))) - .WillRepeatedly(WithArgs<2>(Invoke(DBusCallbackFalse))); + EXPECT_CALL(cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _, _)) + .WillOnce(WithArgs<3>(Invoke(DBusCallbackError))) + .WillRepeatedly(WithArgs<3>(Invoke(DBusCallbackFalse))); Run(); } diff --git a/chrome/browser/chromeos/attestation/platform_verification_flow.cc b/chrome/browser/chromeos/attestation/platform_verification_flow.cc index ab2a53b151..d6ef3ace1e 100644 --- a/chrome/browser/chromeos/attestation/platform_verification_flow.cc +++ b/chrome/browser/chromeos/attestation/platform_verification_flow.cc @@ -10,26 +10,24 @@ #include "chrome/browser/chromeos/attestation/attestation_ca_client.h" #include "chrome/browser/chromeos/attestation/attestation_signed_data.pb.h" #include "chrome/browser/chromeos/attestation/platform_verification_dialog.h" +#include "chrome/browser/chromeos/login/user.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/prefs/scoped_user_pref_update.h" +#include "chrome/browser/profiles/profile.h" #include "chrome/common/pref_names.h" #include "chromeos/attestation/attestation_flow.h" #include "chromeos/cryptohome/async_method_caller.h" #include "chromeos/dbus/cryptohome_client.h" #include "chromeos/dbus/dbus_thread_manager.h" -#include "chromeos/system/statistics_provider.h" #include "components/user_prefs/pref_registry_syncable.h" #include "components/user_prefs/user_prefs.h" +#include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/user_metrics.h" #include "content/public/browser/web_contents.h" namespace { -// A switch which allows consent to be given on the command line. -// TODO(dkrahn): Remove this when UI has been implemented (crbug.com/270908). -const char kAutoApproveSwitch[] = - "auto-approve-platform-verification-consent-prompts"; // A callback method to handle DBus errors. void DBusCallback(const base::Callback<void(bool)>& on_success, @@ -67,12 +65,7 @@ class DefaultDelegate : public PlatformVerificationFlow::Delegate { content::WebContents* web_contents, const PlatformVerificationFlow::Delegate::ConsentCallback& callback) OVERRIDE { - if (CommandLine::ForCurrentProcess()->HasSwitch(kAutoApproveSwitch)) { - LOG(WARNING) << "PlatformVerificationFlow: Automatic approval enabled."; - callback.Run(PlatformVerificationFlow::CONSENT_RESPONSE_ALLOW); - } else { - PlatformVerificationDialog::ShowDialog(web_contents, callback); - } + PlatformVerificationDialog::ShowDialog(web_contents, callback); } private: @@ -84,7 +77,6 @@ PlatformVerificationFlow::PlatformVerificationFlow() async_caller_(cryptohome::AsyncMethodCaller::GetInstance()), cryptohome_client_(DBusThreadManager::Get()->GetCryptohomeClient()), user_manager_(UserManager::Get()), - statistics_provider_(system::StatisticsProvider::GetInstance()), delegate_(NULL), testing_prefs_(NULL), weak_factory_(this) { @@ -104,13 +96,11 @@ PlatformVerificationFlow::PlatformVerificationFlow( cryptohome::AsyncMethodCaller* async_caller, CryptohomeClient* cryptohome_client, UserManager* user_manager, - system::StatisticsProvider* statistics_provider, Delegate* delegate) : attestation_flow_(attestation_flow), async_caller_(async_caller), cryptohome_client_(cryptohome_client), user_manager_(user_manager), - statistics_provider_(statistics_provider), delegate_(delegate), testing_prefs_(NULL), weak_factory_(this) { @@ -143,28 +133,6 @@ void PlatformVerificationFlow::ChallengePlatformKey( cryptohome_client_->TpmAttestationIsEnrolled(dbus_callback); } -void PlatformVerificationFlow::CheckPlatformState( - const base::Callback<void(bool result)>& callback) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - std::string stat_value; - if (!statistics_provider_->GetMachineStatistic(system::kDevSwitchBootMode, - &stat_value)) { - LOG(ERROR) << __func__ << ": Failed to get boot mode statistic."; - callback.Run(false); - return; - } - if (stat_value != "0") { - LOG(INFO) << __func__ << ": Statistic indicates developer mode."; - callback.Run(false); - return; - } - BoolDBusMethodCallback dbus_callback = base::Bind( - &DBusCallback, - callback, - base::Bind(callback, false)); - cryptohome_client_->TpmAttestationIsPrepared(dbus_callback); -} - void PlatformVerificationFlow::CheckConsent(content::WebContents* web_contents, const std::string& service_id, const std::string& challenge, @@ -238,21 +206,29 @@ void PlatformVerificationFlow::OnConsentResponse( // At this point all user interaction is complete and we can proceed with the // certificate request. + chromeos::User* user = GetUser(web_contents); + if (!user) { + ReportError(callback, INTERNAL_ERROR); + LOG(ERROR) << "Profile does not map to a valid user."; + return; + } AttestationFlow::CertificateCallback certificate_callback = base::Bind( &PlatformVerificationFlow::OnCertificateReady, weak_factory_.GetWeakPtr(), + user->email(), service_id, challenge, callback); attestation_flow_->GetCertificate( PROFILE_CONTENT_PROTECTION_CERTIFICATE, - user_manager_->GetActiveUser()->email(), + user->email(), service_id, false, // Don't force a new key. certificate_callback); } void PlatformVerificationFlow::OnCertificateReady( + const std::string& user_id, const std::string& service_id, const std::string& challenge, const ChallengeCallback& callback, @@ -272,6 +248,7 @@ void PlatformVerificationFlow::OnCertificateReady( std::string key_name = kContentProtectionKeyPrefix; key_name += service_id; async_caller_->TpmAttestationSignSimpleChallenge(KEY_USER, + user_id, key_name, challenge, cryptohome_callback); @@ -315,6 +292,13 @@ const GURL& PlatformVerificationFlow::GetURL( return web_contents->GetLastCommittedURL(); } +User* PlatformVerificationFlow::GetUser(content::WebContents* web_contents) { + if (!web_contents) + return user_manager_->GetActiveUser(); + return user_manager_->GetUserByProfile( + Profile::FromBrowserContext(web_contents->GetBrowserContext())); +} + bool PlatformVerificationFlow::IsAttestationEnabled( content::WebContents* web_contents) { // Check the device policy for the feature. diff --git a/chrome/browser/chromeos/attestation/platform_verification_flow.h b/chrome/browser/chromeos/attestation/platform_verification_flow.h index 8ba3b874f7..64f3627464 100644 --- a/chrome/browser/chromeos/attestation/platform_verification_flow.h +++ b/chrome/browser/chromeos/attestation/platform_verification_flow.h @@ -31,10 +31,7 @@ namespace chromeos { class CryptohomeClient; class UserManager; - -namespace system { -class StatisticsProvider; -} +class User; namespace attestation { @@ -112,7 +109,6 @@ class PlatformVerificationFlow { cryptohome::AsyncMethodCaller* async_caller, CryptohomeClient* cryptohome_client, UserManager* user_manager, - system::StatisticsProvider* statistics_provider, Delegate* delegate); virtual ~PlatformVerificationFlow(); @@ -132,13 +128,6 @@ class PlatformVerificationFlow { const std::string& challenge, const ChallengeCallback& callback); - // Performs a quick check to see if platform verification is reasonably - // expected to succeed. The result of the check will be sent to the given - // |callback|. If the |result| is true, then platform verification is - // expected to succeed. However, this result is not authoritative either true - // or false. If an error occurs, |result| will be false. - void CheckPlatformState(const base::Callback<void(bool result)>& callback); - static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* prefs); void set_testing_prefs(PrefService* testing_prefs) { @@ -174,11 +163,13 @@ class PlatformVerificationFlow { // A callback called when an attestation certificate request operation // completes. |service_id|, |challenge|, and |callback| are the same as in - // ChallengePlatformKey. |operation_success| is true iff the certificate + // ChallengePlatformKey. |user_id| identifies the user for which the + // certificate was requested. |operation_success| is true iff the certificate // request operation succeeded. |certificate| holds the certificate for the // platform key on success. If the certificate request was successful, this // method invokes a request to sign the challenge. - void OnCertificateReady(const std::string& service_id, + void OnCertificateReady(const std::string& user_id, + const std::string& service_id, const std::string& challenge, const ChallengeCallback& callback, bool operation_success, @@ -205,6 +196,11 @@ class PlatformVerificationFlow { // set explicitly using set_testing_url(), then this value is always returned. const GURL& GetURL(content::WebContents* web_contents); + // Gets the user associated with the given |web_contents|. NULL may be + // returned. If |web_contents| is NULL (e.g. during testing), then the + // current active user will be returned. + User* GetUser(content::WebContents* web_contents); + // Checks whether policy or profile settings associated with |web_contents| // have attestation for content protection explicitly disabled. bool IsAttestationEnabled(content::WebContents* web_contents); @@ -242,7 +238,6 @@ class PlatformVerificationFlow { cryptohome::AsyncMethodCaller* async_caller_; CryptohomeClient* cryptohome_client_; UserManager* user_manager_; - system::StatisticsProvider* statistics_provider_; Delegate* delegate_; scoped_ptr<Delegate> default_delegate_; PrefService* testing_prefs_; diff --git a/chrome/browser/chromeos/attestation/platform_verification_flow_unittest.cc b/chrome/browser/chromeos/attestation/platform_verification_flow_unittest.cc index 660f6f4ccc..728b1e822c 100644 --- a/chrome/browser/chromeos/attestation/platform_verification_flow_unittest.cc +++ b/chrome/browser/chromeos/attestation/platform_verification_flow_unittest.cc @@ -20,7 +20,6 @@ #include "chromeos/cryptohome/mock_async_method_caller.h" #include "chromeos/dbus/fake_cryptohome_client.h" #include "chromeos/settings/cros_settings_names.h" -#include "chromeos/system/mock_statistics_provider.h" #include "content/public/test/test_browser_thread.h" #include "testing/gtest/include/gtest/gtest.h" @@ -124,33 +123,22 @@ class PlatformVerificationFlowTest : public ::testing::Test { ui_thread_(content::BrowserThread::UI, &message_loop_), certificate_success_(true), sign_challenge_success_(true), - result_(PlatformVerificationFlow::INTERNAL_ERROR), - check_state_result_(false) {} + result_(PlatformVerificationFlow::INTERNAL_ERROR) {} void SetUp() { // Configure a user for the mock user manager. mock_user_manager_.SetActiveUser(kTestEmail); - // Configure the statistics provider to report verified mode. - EXPECT_CALL(mock_statistics_provider_, - GetMachineStatistic(system::kDevSwitchBootMode, _)) - .WillRepeatedly(DoAll(SetArgumentPointee<1>(std::string("0")), - Return(true))); - // Create a verifier for tests to call. verifier_.reset(new PlatformVerificationFlow(&mock_attestation_flow_, &mock_async_caller_, &fake_cryptohome_client_, &mock_user_manager_, - &mock_statistics_provider_, &fake_delegate_)); // Create callbacks for tests to use with verifier_. callback_ = base::Bind(&PlatformVerificationFlowTest::FakeChallengeCallback, base::Unretained(this)); - check_state_callback_ = base::Bind( - &PlatformVerificationFlowTest::FakeCheckStateCallback, - base::Unretained(this)); // Configure the test pref service. pref_service_.registry()->RegisterBooleanPref(prefs::kEnableDRM, true); @@ -197,9 +185,10 @@ class PlatformVerificationFlowTest : public ::testing::Test { std::string expected_key_name = std::string(kContentProtectionKeyPrefix) + std::string(kTestID); EXPECT_CALL(mock_async_caller_, - TpmAttestationSignSimpleChallenge(KEY_USER, expected_key_name, + TpmAttestationSignSimpleChallenge(KEY_USER, kTestEmail, + expected_key_name, kTestChallenge, _)) - .WillRepeatedly(WithArgs<3>(Invoke( + .WillRepeatedly(WithArgs<4>(Invoke( this, &PlatformVerificationFlowTest::FakeSignChallenge))); } @@ -230,10 +219,6 @@ class PlatformVerificationFlowTest : public ::testing::Test { certificate_ = certificate; } - void FakeCheckStateCallback(bool result) { - check_state_result_ = result; - } - std::string CreateFakeResponseProto() { SignedData pb; pb.set_data(kTestSignedData); @@ -250,7 +235,6 @@ class PlatformVerificationFlowTest : public ::testing::Test { cryptohome::MockAsyncMethodCaller mock_async_caller_; CustomFakeCryptohomeClient fake_cryptohome_client_; MockUserManager mock_user_manager_; - system::MockStatisticsProvider mock_statistics_provider_; FakeDelegate fake_delegate_; TestingPrefServiceSimple pref_service_; CrosSettingsProvider* device_settings_provider_; @@ -271,8 +255,6 @@ class PlatformVerificationFlowTest : public ::testing::Test { std::string challenge_salt_; std::string challenge_signature_; std::string certificate_; - base::Callback<void(bool result)> check_state_callback_; - bool check_state_result_; }; TEST_F(PlatformVerificationFlowTest, SuccessNoConsent) { @@ -396,60 +378,5 @@ TEST_F(PlatformVerificationFlowTest, ConsentNoResponse) { EXPECT_EQ(PlatformVerificationFlow::USER_REJECTED, result_); } -TEST_F(PlatformVerificationFlowTest, FastCheck) { - verifier_->CheckPlatformState(check_state_callback_); - base::RunLoop().RunUntilIdle(); - EXPECT_TRUE(check_state_result_); -} - -TEST_F(PlatformVerificationFlowTest, FastCheckNoStat) { - // Configure the stats provider to fail. - EXPECT_CALL(mock_statistics_provider_, - GetMachineStatistic(system::kDevSwitchBootMode, _)) - .WillRepeatedly(Return(false)); - - verifier_->CheckPlatformState(check_state_callback_); - base::RunLoop().RunUntilIdle(); - EXPECT_FALSE(check_state_result_); -} - -TEST_F(PlatformVerificationFlowTest, FastCheckStatDevMode) { - // Configure the stats provider to fail. - EXPECT_CALL(mock_statistics_provider_, - GetMachineStatistic(system::kDevSwitchBootMode, _)) - .WillRepeatedly(DoAll(SetArgumentPointee<1>(std::string("1")), - Return(true))); - - verifier_->CheckPlatformState(check_state_callback_); - base::RunLoop().RunUntilIdle(); - EXPECT_FALSE(check_state_result_); -} - -TEST_F(PlatformVerificationFlowTest, FastCheckStatInvalidMode) { - // Configure the stats provider to fail. - EXPECT_CALL(mock_statistics_provider_, - GetMachineStatistic(system::kDevSwitchBootMode, _)) - .WillRepeatedly(DoAll(SetArgumentPointee<1>(std::string("INVALID")), - Return(true))); - - verifier_->CheckPlatformState(check_state_callback_); - base::RunLoop().RunUntilIdle(); - EXPECT_FALSE(check_state_result_); -} - -TEST_F(PlatformVerificationFlowTest, FastCheckNoAttestation) { - fake_cryptohome_client_.set_attestation_prepared(false); - verifier_->CheckPlatformState(check_state_callback_); - base::RunLoop().RunUntilIdle(); - EXPECT_FALSE(check_state_result_); -} - -TEST_F(PlatformVerificationFlowTest, FastCheckDBusFailure) { - fake_cryptohome_client_.set_call_status(DBUS_METHOD_CALL_FAILURE); - verifier_->CheckPlatformState(check_state_callback_); - base::RunLoop().RunUntilIdle(); - EXPECT_FALSE(check_state_result_); -} - } // namespace attestation } // namespace chromeos diff --git a/chrome/browser/chromeos/boot_times_loader.cc b/chrome/browser/chromeos/boot_times_loader.cc index 223259676d..f7ee861929 100644 --- a/chrome/browser/chromeos/boot_times_loader.cc +++ b/chrome/browser/chromeos/boot_times_loader.cc @@ -86,8 +86,6 @@ static const base::FilePath::CharType kUptimePrefix[] = FPL("uptime-"); static const base::FilePath::CharType kDiskPrefix[] = FPL("disk-"); // Name of the time that Chrome's main() is called. static const base::FilePath::CharType kChromeMain[] = FPL("chrome-main"); -// Delay in milliseconds between file read attempts. -static const int64 kReadAttemptDelayMs = 250; // Delay in milliseconds before writing the login times to disk. static const int64 kLoginTimeWriteDelayMs = 3000; diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc index 56915828aa..e5559b1a1d 100644 --- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc +++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc @@ -82,7 +82,7 @@ #include "chromeos/chromeos_paths.h" #include "chromeos/chromeos_switches.h" #include "chromeos/cryptohome/async_method_caller.h" -#include "chromeos/cryptohome/cryptohome_library.h" +#include "chromeos/cryptohome/system_salt_getter.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/power_policy_controller.h" #include "chromeos/dbus/session_manager_client.h" @@ -102,6 +102,8 @@ #include "net/base/network_change_notifier.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_context_getter.h" +#include "ui/base/touch/touch_device.h" +#include "ui/events/event_utils.h" // Exclude X11 dependents for ozone #if defined(USE_X11) @@ -138,8 +140,7 @@ class StubLogin : public LoginStatusConsumer, public LoginUtils::Delegate { public: StubLogin(std::string username, std::string password) - : pending_requests_(false), - profile_prepared_(false) { + : profile_prepared_(false) { authenticator_ = LoginUtils::Get()->CreateAuthenticator(this); authenticator_.get()->AuthenticateToLogin( g_browser_process->profile_manager()->GetDefaultProfile(), @@ -157,19 +158,15 @@ class StubLogin : public LoginStatusConsumer, delete this; } - virtual void OnLoginSuccess(const UserContext& user_context, - bool pending_requests, - bool using_oauth) OVERRIDE { - pending_requests_ = pending_requests; + virtual void OnLoginSuccess(const UserContext& user_context) OVERRIDE { if (!profile_prepared_) { // Will call OnProfilePrepared in the end. LoginUtils::Get()->PrepareProfile(user_context, std::string(), // display_email - using_oauth, false, // has_cookies true, // has_active_session this); - } else if (!pending_requests) { + } else { delete this; } } @@ -178,12 +175,10 @@ class StubLogin : public LoginStatusConsumer, virtual void OnProfilePrepared(Profile* profile) OVERRIDE { profile_prepared_ = true; LoginUtils::Get()->DoBrowserLaunch(profile, NULL); - if (!pending_requests_) - delete this; + delete this; } scoped_refptr<Authenticator> authenticator_; - bool pending_requests_; bool profile_prepared_; }; @@ -268,7 +263,7 @@ class DBusServices { CrosDBusService::Initialize(); LoginState::Initialize(); - CryptohomeLibrary::Initialize(); + SystemSaltGetter::Initialize(); CertLoader::Initialize(); // This function and SystemKeyEventListener use InputMethodManager. @@ -315,7 +310,7 @@ class DBusServices { disks::DiskMountManager::Shutdown(); input_method::Shutdown(); - CryptohomeLibrary::Shutdown(); + SystemSaltGetter::Shutdown(); LoginState::Shutdown(); CrosDBusService::Shutdown(); @@ -575,6 +570,26 @@ void ChromeBrowserMainPartsChromeos::PostProfileInit() { UserManager::Get()->RestoreActiveSessions(); } + // Initialize the network portal detector for Chrome OS. The network + // portal detector starts to listen for notifications from + // NetworkStateHandler and initiates captive portal detection for + // active networks. Shoule be called before call to + // OptionallyRunChromeOSLoginManager, because it depends on + // NetworkPortalDetector. + NetworkPortalDetector::Initialize(); + { + NetworkPortalDetector* detector = NetworkPortalDetector::Get(); +#if defined(GOOGLE_CHROME_BUILD) + bool is_official_build = true; +#else + bool is_official_build = false; +#endif + // Enable portal detector if EULA was previously accepted or if + // this is an unofficial build. + if (!is_official_build || StartupUtils::IsEulaAccepted()) + detector->Enable(true); + } + // Tests should be able to tune login manager before showing it. // Thus only show login manager in normal (non-testing) mode. if (!parameters().ui_task || @@ -593,23 +608,6 @@ void ChromeBrowserMainPartsChromeos::PostProfileInit() { peripheral_battery_observer_.reset(new PeripheralBatteryObserver()); - // Initialize the network portal detector for Chrome OS. The network portal - // detector starts to listen for notifications from NetworkStateHandler and - // initiates captive portal detection for active networks. - NetworkPortalDetector* detector = NetworkPortalDetector::GetInstance(); - if (NetworkPortalDetector::IsEnabledInCommandLine() && detector) { - detector->Init(); -#if defined(GOOGLE_CHROME_BUILD) - bool is_official_build = true; -#else - bool is_official_build = false; -#endif - // Enable portal detector if EULA was previously accepted or if - // this is an unofficial build. - if (!is_official_build || StartupUtils::IsEulaAccepted()) - detector->Enable(true); - } - display_configuration_observer_.reset( new DisplayConfigurationObserver()); @@ -646,6 +644,13 @@ void ChromeBrowserMainPartsChromeos::PreBrowserStart() { // adjusting the oom priority. g_browser_process->platform_part()->oom_priority_manager()->Start(); + // Turn on natural scroll if we have a touch screen. + if (ui::IsTouchDevicePresent()) { + CommandLine::ForCurrentProcess()->AppendSwitch( + chromeos::switches::kNaturalScrollDefault); + ui::SetNaturalScroll(true); + } + ChromeBrowserMainPartsLinux::PreBrowserStart(); } @@ -678,10 +683,6 @@ void ChromeBrowserMainPartsChromeos::PostMainMessageLoopRun() { if (NetworkChangeNotifierFactoryChromeos::GetInstance()) NetworkChangeNotifierFactoryChromeos::GetInstance()->Shutdown(); - NetworkPortalDetector* detector = NetworkPortalDetector::GetInstance(); - if (NetworkPortalDetector::IsEnabledInCommandLine() && detector) - detector->Shutdown(); - // Destroy UI related classes before destroying services that they may // depend on. data_promo_notification_.reset(); @@ -748,6 +749,12 @@ void ChromeBrowserMainPartsChromeos::PostMainMessageLoopRun() { // http://crbug.com/243364). ChromeBrowserMainPartsLinux::PostMainMessageLoopRun(); + // Called after + // ChromeBrowserMainPartsLinux::PostMainMessageLoopRun() to be + // executed after execution of chrome::CloseAsh(), because some + // parts of WebUI depends on NetworkPortalDetector. + NetworkPortalDetector::Shutdown(); + UserManager::Destroy(); } diff --git a/chrome/browser/chromeos/contacts/gdata_contacts_service_unittest.cc b/chrome/browser/chromeos/contacts/gdata_contacts_service_unittest.cc index a739d50e8f..3dbcae3151 100644 --- a/chrome/browser/chromeos/contacts/gdata_contacts_service_unittest.cc +++ b/chrome/browser/chromeos/contacts/gdata_contacts_service_unittest.cc @@ -31,8 +31,6 @@ using content::BrowserThread; namespace contacts { namespace { -const char kTestGDataAuthToken[] = "testtoken"; - // Filename of JSON feed containing contact groups. const char kGroupsFeedFilename[] = "/groups.json"; diff --git a/chrome/browser/chromeos/display/display_preferences.cc b/chrome/browser/chromeos/display/display_preferences.cc index b7a1659034..09d6c39d47 100644 --- a/chrome/browser/chromeos/display/display_preferences.cc +++ b/chrome/browser/chromeos/display/display_preferences.cc @@ -4,7 +4,6 @@ #include "chrome/browser/chromeos/display/display_preferences.h" -#include "ash/display/display_controller.h" #include "ash/display/display_layout_store.h" #include "ash/display/display_manager.h" #include "ash/display/display_pref_util.h" @@ -277,8 +276,7 @@ void StoreDisplayPrefs() { } void SetCurrentDisplayLayout(const ash::DisplayLayout& layout) { - ash::DisplayController* display_controller = GetDisplayController(); - display_controller->SetLayoutForCurrentDisplays(layout); + GetDisplayManager()->SetLayoutForCurrentDisplays(layout); } void LoadDisplayPreferences(bool first_run_after_boot) { diff --git a/chrome/browser/chromeos/display/overscan_calibrator.cc b/chrome/browser/chromeos/display/overscan_calibrator.cc index 1e8f37ab56..ed4adaeeb7 100644 --- a/chrome/browser/chromeos/display/overscan_calibrator.cc +++ b/chrome/browser/chromeos/display/overscan_calibrator.cc @@ -17,9 +17,6 @@ namespace chromeos { namespace { -// The opacity for the grey out borders. -const float kBorderOpacity = 0.5; - // The opacity for the arrows of the overscan calibration. const float kArrowOpacity = 0.8; diff --git a/chrome/browser/chromeos/drive/async_file_util.cc b/chrome/browser/chromeos/drive/async_file_util.cc index bcd4161ec3..4e9a03a82b 100644 --- a/chrome/browser/chromeos/drive/async_file_util.cc +++ b/chrome/browser/chromeos/drive/async_file_util.cc @@ -282,8 +282,6 @@ void AsyncFileUtil::CopyFileLocal( const StatusCallback& callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - // TODO(hidehiko): Support option. - base::FilePath src_path = util::ExtractDrivePathFromFileSystemUrl(src_url); base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url); if (src_path.empty() || dest_path.empty()) { @@ -309,8 +307,6 @@ void AsyncFileUtil::MoveFileLocal( const StatusCallback& callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - // TODO(hidehiko): Support option. - base::FilePath src_path = util::ExtractDrivePathFromFileSystemUrl(src_url); base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url); if (src_path.empty() || dest_path.empty()) { diff --git a/chrome/browser/chromeos/drive/change_list_loader.cc b/chrome/browser/chromeos/drive/change_list_loader.cc index 19184b12ad..f9718595e2 100644 --- a/chrome/browser/chromeos/drive/change_list_loader.cc +++ b/chrome/browser/chromeos/drive/change_list_loader.cc @@ -389,19 +389,6 @@ void ChangeListLoader::LoadIfNeeded( Load(directory_fetch_info, callback); } -void ChangeListLoader::LoadDirectoryFromServer( - const std::string& directory_resource_id, - const FileOperationCallback& callback) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK(!callback.is_null()); - - scheduler_->GetAboutResource( - base::Bind(&ChangeListLoader::LoadDirectoryFromServerAfterGetAbout, - weak_ptr_factory_.GetWeakPtr(), - directory_resource_id, - callback)); -} - void ChangeListLoader::Load(const DirectoryFetchInfo& directory_fetch_info, const FileOperationCallback& callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -677,25 +664,6 @@ void ChangeListLoader::LoadChangeListFromServerAfterUpdate() { OnLoadFromServerComplete()); } -void ChangeListLoader::LoadDirectoryFromServerAfterGetAbout( - const std::string& directory_resource_id, - const FileOperationCallback& callback, - google_apis::GDataErrorCode status, - scoped_ptr<google_apis::AboutResource> about_resource) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK(!callback.is_null()); - - if (GDataToFileError(status) == FILE_ERROR_OK) { - DCHECK(about_resource); - last_known_remote_changestamp_ = about_resource->largest_change_id(); - root_folder_id_ = about_resource->root_folder_id(); - } - - DoLoadDirectoryFromServer( - DirectoryFetchInfo(directory_resource_id, last_known_remote_changestamp_), - callback); -} - void ChangeListLoader::CheckChangestampAndLoadDirectoryIfNeeded( const DirectoryFetchInfo& directory_fetch_info, int64 local_changestamp, diff --git a/chrome/browser/chromeos/drive/change_list_loader.h b/chrome/browser/chromeos/drive/change_list_loader.h index d4e2a604ec..32141d0936 100644 --- a/chrome/browser/chromeos/drive/change_list_loader.h +++ b/chrome/browser/chromeos/drive/change_list_loader.h @@ -98,12 +98,6 @@ class ChangeListLoader { void LoadIfNeeded(const DirectoryFetchInfo& directory_fetch_info, const FileOperationCallback& callback); - // Loads the directory content from the server, without comparing the - // changestamps. The purpose of this function is to update thumbnail URLs - // in the directory which can stale over time. - void LoadDirectoryFromServer(const std::string& directory_resource_id, - const FileOperationCallback& callback); - private: // Starts the resource metadata loading and calls |callback| when it's // done. |directory_fetch_info| is used for fast fetch. If there is already @@ -184,14 +178,6 @@ class ChangeListLoader { // ================= Implementation for directory loading ================= - // Part of LoadDirectoryFromServer(), called after the current remote - // changestamp is obtained as |about_resource|. - void LoadDirectoryFromServerAfterGetAbout( - const std::string& directory_resource_id, - const FileOperationCallback& callback, - google_apis::GDataErrorCode status, - scoped_ptr<google_apis::AboutResource> about_resource); - // Compares the directory's changestamp and |last_known_remote_changestamp_|. // Starts DoLoadDirectoryFromServer() if the local data is old and runs // |callback| when finished. If it is up to date, calls back immediately. diff --git a/chrome/browser/chromeos/drive/drive.proto b/chrome/browser/chromeos/drive/drive.proto index dc4fa55bf7..c72c49c3bc 100644 --- a/chrome/browser/chromeos/drive/drive.proto +++ b/chrome/browser/chromeos/drive/drive.proto @@ -23,10 +23,7 @@ message PlatformFileInfoProto { // File specific info, which is a part of ResourceEntry. message FileSpecificInfo { - // This URL points to a thumbnail image. The thumbnail URL is not permanent - // as it's not protected by authentication. See crbug.com/127697 for how - // stale thumbnail URLs are handled. - optional string thumbnail_url = 1; + // The argument with ID 1 (thumbnail_url) had been used, but got deleted. // This URL is used for opening hosted documents with Google Drive's web // interface. diff --git a/chrome/browser/chromeos/drive/drive_integration_service.cc b/chrome/browser/chromeos/drive/drive_integration_service.cc index 28d2ab04fa..9e4dbd1c64 100644 --- a/chrome/browser/chromeos/drive/drive_integration_service.cc +++ b/chrome/browser/chromeos/drive/drive_integration_service.cc @@ -115,9 +115,6 @@ FileError InitializeMetadata( file_util::FILE_PERMISSION_EXECUTE_BY_GROUP | file_util::FILE_PERMISSION_EXECUTE_BY_OTHERS); - util::MigrateCacheFilesFromOldDirectories(cache_root_directory, - kCacheFileDirectory); - if (!metadata_storage->Initialize()) { LOG(WARNING) << "Failed to initialize the metadata storage."; return FILE_ERROR_FAILED; diff --git a/chrome/browser/chromeos/drive/file_cache.cc b/chrome/browser/chromeos/drive/file_cache.cc index 148d716f75..ca614c3268 100644 --- a/chrome/browser/chromeos/drive/file_cache.cc +++ b/chrome/browser/chromeos/drive/file_cache.cc @@ -16,6 +16,7 @@ #include "chrome/browser/chromeos/drive/drive.pb.h" #include "chrome/browser/chromeos/drive/file_system_util.h" #include "chrome/browser/chromeos/drive/resource_metadata_storage.h" +#include "chrome/browser/drive/drive_api_util.h" #include "chromeos/chromeos_constants.h" #include "content/public/browser/browser_thread.h" @@ -69,16 +70,6 @@ void RunGetFileFromCacheCallback(const GetFileFromCacheCallback& callback, callback.Run(error, *file_path); } -// Runs callback with pointers dereferenced. -// Used to implement GetCacheEntry(). -void RunGetCacheEntryCallback(const GetCacheEntryCallback& callback, - FileCacheEntry* cache_entry, - bool success) { - DCHECK(cache_entry); - DCHECK(!callback.is_null()); - callback.Run(success, *cache_entry); -} - } // namespace FileCache::FileCache(ResourceMetadataStorage* storage, @@ -114,23 +105,6 @@ bool FileCache::IsUnderFileCacheDirectory(const base::FilePath& path) const { return cache_file_directory_.IsParent(path); } -void FileCache::GetCacheEntryOnUIThread(const std::string& id, - const GetCacheEntryCallback& callback) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK(!callback.is_null()); - - FileCacheEntry* cache_entry = new FileCacheEntry; - base::PostTaskAndReplyWithResult( - blocking_task_runner_.get(), - FROM_HERE, - base::Bind(&FileCache::GetCacheEntry, - base::Unretained(this), - id, - cache_entry), - base::Bind( - &RunGetCacheEntryCallback, callback, base::Owned(cache_entry))); -} - bool FileCache::GetCacheEntry(const std::string& id, FileCacheEntry* entry) { DCHECK(entry); AssertOnSequencedWorkerPool(); @@ -194,25 +168,6 @@ FileError FileCache::GetFile(const std::string& id, return FILE_ERROR_OK; } -void FileCache::StoreOnUIThread(const std::string& id, - const std::string& md5, - const base::FilePath& source_path, - FileOperationType file_operation_type, - const FileOperationCallback& callback) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK(!callback.is_null()); - - base::PostTaskAndReplyWithResult(blocking_task_runner_.get(), - FROM_HERE, - base::Bind(&FileCache::Store, - base::Unretained(this), - id, - md5, - source_path, - file_operation_type), - callback); -} - FileError FileCache::Store(const std::string& id, const std::string& md5, const base::FilePath& source_path, @@ -341,18 +296,6 @@ void FileCache::MarkAsUnmountedOnUIThread( callback); } -void FileCache::MarkDirtyOnUIThread(const std::string& id, - const FileOperationCallback& callback) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK(!callback.is_null()); - - base::PostTaskAndReplyWithResult( - blocking_task_runner_.get(), - FROM_HERE, - base::Bind(&FileCache::MarkDirty, base::Unretained(this), id), - callback); -} - FileError FileCache::MarkDirty(const std::string& id) { AssertOnSequencedWorkerPool(); @@ -399,18 +342,6 @@ FileError FileCache::ClearDirty(const std::string& id, const std::string& md5) { FILE_ERROR_OK : FILE_ERROR_FAILED; } -void FileCache::RemoveOnUIThread(const std::string& id, - const FileOperationCallback& callback) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK(!callback.is_null()); - - base::PostTaskAndReplyWithResult( - blocking_task_runner_.get(), - FROM_HERE, - base::Bind(&FileCache::Remove, base::Unretained(this), id), - callback); -} - FileError FileCache::Remove(const std::string& id) { AssertOnSequencedWorkerPool(); @@ -461,7 +392,8 @@ bool FileCache::ClearAll() { bool FileCache::Initialize() { AssertOnSequencedWorkerPool(); - RenameCacheFilesToNewFormat(); + if (!RenameCacheFilesToNewFormat()) + return false; if (storage_->cache_file_scan_is_needed()) { CacheMap cache_map; @@ -494,11 +426,8 @@ bool FileCache::CanonicalizeIDs( for (; !it->IsAtEnd(); it->Advance()) { const std::string id_canonicalized = id_canonicalizer.Run(it->GetID()); if (id_canonicalized != it->GetID()) { - // Replace the existing entry and rename the file when needed. - const base::FilePath path_old = GetCacheFilePath(it->GetID()); - const base::FilePath path_new = GetCacheFilePath(id_canonicalized); + // Replace the existing entry. if (!storage_->RemoveCacheEntry(it->GetID()) || - (base::PathExists(path_old) && !base::Move(path_old, path_new)) || !storage_->PutCacheEntry(id_canonicalized, it->GetValue())) return false; } @@ -595,27 +524,25 @@ bool FileCache::HasEnoughSpaceFor(int64 num_bytes, return (free_space >= num_bytes); } -void FileCache::RenameCacheFilesToNewFormat() { - // First, remove all files with multiple extensions just in case. - { - base::FileEnumerator enumerator(cache_file_directory_, - false, // not recursive - base::FileEnumerator::FILES, - "*.*.*"); - for (base::FilePath current = enumerator.Next(); !current.empty(); - current = enumerator.Next()) - base::DeleteFile(current, false /* recursive */); - } - - // Rename files. - { - base::FileEnumerator enumerator(cache_file_directory_, - false, // not recursive - base::FileEnumerator::FILES); - for (base::FilePath current = enumerator.Next(); !current.empty(); - current = enumerator.Next()) - base::Move(current, current.RemoveExtension()); +bool FileCache::RenameCacheFilesToNewFormat() { + base::FileEnumerator enumerator(cache_file_directory_, + false, // not recursive + base::FileEnumerator::FILES); + for (base::FilePath current = enumerator.Next(); !current.empty(); + current = enumerator.Next()) { + base::FilePath new_path = current.RemoveExtension(); + if (!new_path.Extension().empty()) { + // Delete files with multiple extensions. + if (!base::DeleteFile(current, false /* recursive */)) + return false; + continue; + } + const std::string& id = GetIdFromPath(new_path); + new_path = GetCacheFilePath(util::CanonicalizeResourceId(id)); + if (new_path != current && !base::Move(current, new_path)) + return false; } + return true; } } // namespace internal diff --git a/chrome/browser/chromeos/drive/file_cache.h b/chrome/browser/chromeos/drive/file_cache.h index 2084ee3764..ba6aa1af8f 100644 --- a/chrome/browser/chromeos/drive/file_cache.h +++ b/chrome/browser/chromeos/drive/file_cache.h @@ -25,13 +25,6 @@ namespace drive { class FileCacheEntry; -// Callback for GetCacheEntry. -// |success| indicates if the operation was successful. -// |cache_entry| is the obtained cache entry. On failure, |cache_state| is -// set to TEST_CACHE_STATE_NONE. -typedef base::Callback<void(bool success, const FileCacheEntry& cache_entry)> - GetCacheEntryCallback; - namespace internal { // Callback for GetFileFromCache. @@ -81,16 +74,8 @@ class FileCache { // Can be called on any thread. bool IsUnderFileCacheDirectory(const base::FilePath& path) const; - // Gets the cache entry for file corresponding to |id| and runs - // |callback| with true and the entry found if entry exists in cache map. - // Otherwise, runs |callback| with false. - // |callback| must not be null. - // Must be called on the UI thread. - void GetCacheEntryOnUIThread(const std::string& id, - const GetCacheEntryCallback& callback); - - // Gets the cache entry by the given ID. - // See also GetCacheEntryOnUIThread(). + // Gets the cache entry for file corresponding to |id| and returns true if + // entry exists in cache map. bool GetCacheEntry(const std::string& id, FileCacheEntry* entry); // Returns an object to iterate over entries. @@ -107,16 +92,6 @@ class FileCache { // |cache_file_path| must not be null. FileError GetFile(const std::string& id, base::FilePath* cache_file_path); - // Runs Store() on |blocking_task_runner_|, and calls |callback| with - // the result asynchronously. - // |callback| must not be null. - // Must be called on the UI thread. - void StoreOnUIThread(const std::string& id, - const std::string& md5, - const base::FilePath& source_path, - FileOperationType file_operation_type, - const FileOperationCallback& callback); - // Stores |source_path| as a cache of the remote content of the file // with |id| and |md5|. FileError Store(const std::string& id, @@ -161,27 +136,13 @@ class FileCache { void MarkAsUnmountedOnUIThread(const base::FilePath& file_path, const FileOperationCallback& callback); - // Runs MarkDirty() on |blocking_task_runner_|, and calls |callback| with the - // result asynchronously. - // |callback| must not be null. - // Must be called on the UI thread. - void MarkDirtyOnUIThread(const std::string& id, - const FileOperationCallback& callback); - // Marks the specified entry dirty. FileError MarkDirty(const std::string& id); // Clears dirty state of the specified entry and updates its MD5. FileError ClearDirty(const std::string& id, const std::string& md5); - // Runs Remove() on |blocking_task_runner_| and runs |callback| with the - // result. - // Must be called on the UI thread. - void RemoveOnUIThread(const std::string& id, - const FileOperationCallback& callback); - // Removes the specified cache entry and delete cache files if available. - // Synchronous version of RemoveOnUIThread(). FileError Remove(const std::string& id); // Removes all the files in the cache directory and cache entries in DB. @@ -232,9 +193,9 @@ class FileCache { // bytes, while keeping kMinFreeSpace bytes on the disk. bool HasEnoughSpaceFor(int64 num_bytes, const base::FilePath& path); - // Renames cache files from old "id.md5" format to the new format. + // Renames cache files from old "prefix:id.md5" format to the new format. // TODO(hashimoto): Remove this method at some point. - void RenameCacheFilesToNewFormat(); + bool RenameCacheFilesToNewFormat(); const base::FilePath cache_file_directory_; diff --git a/chrome/browser/chromeos/drive/file_cache_unittest.cc b/chrome/browser/chromeos/drive/file_cache_unittest.cc index d0f7d17925..d2c88104ff 100644 --- a/chrome/browser/chromeos/drive/file_cache_unittest.cc +++ b/chrome/browser/chromeos/drive/file_cache_unittest.cc @@ -106,9 +106,13 @@ class FileCacheTestOnUIThread : public testing::Test { expected_cache_state_ = expected_cache_state; FileError error = FILE_ERROR_OK; - cache_->StoreOnUIThread( - id, md5, source_path, - FileCache::FILE_OPERATION_COPY, + base::PostTaskAndReplyWithResult( + blocking_task_runner_, + FROM_HERE, + base::Bind(&internal::FileCache::Store, + base::Unretained(cache_.get()), + id, md5, source_path, + FileCache::FILE_OPERATION_COPY), google_apis::test_util::CreateCopyResultCallback(&error)); test_util::RunBlockingPoolTask(); @@ -125,8 +129,12 @@ class FileCacheTestOnUIThread : public testing::Test { expected_error_ = expected_error; FileError error = FILE_ERROR_OK; - cache_->RemoveOnUIThread( - id, + base::PostTaskAndReplyWithResult( + blocking_task_runner_, + FROM_HERE, + base::Bind(&internal::FileCache::Remove, + base::Unretained(cache_.get()), + id), google_apis::test_util::CreateCopyResultCallback(&error)); test_util::RunBlockingPoolTask(); VerifyRemoveFromCache(error, id); @@ -179,8 +187,12 @@ class FileCacheTestOnUIThread : public testing::Test { expected_cache_state_ = expected_cache_state; FileError error = FILE_ERROR_OK; - cache_->MarkDirtyOnUIThread( - id, + base::PostTaskAndReplyWithResult( + blocking_task_runner_, + FROM_HERE, + base::Bind(&internal::FileCache::MarkDirty, + base::Unretained(cache_.get()), + id), google_apis::test_util::CreateCopyResultCallback(&error)); test_util::RunBlockingPoolTask(); @@ -310,9 +322,14 @@ class FileCacheTestOnUIThread : public testing::Test { bool GetCacheEntryFromOriginThread(const std::string& id, FileCacheEntry* cache_entry) { bool result = false; - cache_->GetCacheEntryOnUIThread( - id, - google_apis::test_util::CreateCopyResultCallback(&result, cache_entry)); + base::PostTaskAndReplyWithResult( + blocking_task_runner_, + FROM_HERE, + base::Bind(&internal::FileCache::GetCacheEntry, + base::Unretained(cache_.get()), + id, + cache_entry), + google_apis::test_util::CreateCopyResultCallback(&result)); test_util::RunBlockingPoolTask(); return result; } @@ -721,8 +738,8 @@ class FileCacheTest : public testing::Test { ASSERT_TRUE(cache_->Initialize()); } - static void RenameCacheFilesToNewFormat(FileCache* cache) { - cache->RenameCacheFilesToNewFormat(); + static bool RenameCacheFilesToNewFormat(FileCache* cache) { + return cache->RenameCacheFilesToNewFormat(); } content::TestBrowserThreadBundle thread_bundle_; @@ -892,7 +909,6 @@ TEST_F(FileCacheTest, CanonicalizeIDs) { EXPECT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(), &file)); EXPECT_EQ(FILE_ERROR_OK, cache_->Store(id, md5, file, FileCache::FILE_OPERATION_COPY)); - EXPECT_TRUE(base::PathExists(file_directory.AppendASCII(id))); // Canonicalize IDs. EXPECT_TRUE(cache_->CanonicalizeIDs(id_canonicalizer)); @@ -901,16 +917,15 @@ TEST_F(FileCacheTest, CanonicalizeIDs) { FileCacheEntry entry; EXPECT_FALSE(cache_->GetCacheEntry(id, &entry)); EXPECT_TRUE(cache_->GetCacheEntry(canonicalized_id, &entry)); - EXPECT_TRUE(base::PathExists(file_directory.AppendASCII(canonicalized_id))); } TEST_F(FileCacheTest, RenameCacheFilesToNewFormat) { const base::FilePath file_directory = temp_dir_.path().AppendASCII(kCacheFileDirectory); - // File with an old style "<ID>.<MD5>" name. + // File with an old style "<prefix>:<ID>.<MD5>" name. ASSERT_TRUE(google_apis::test_util::WriteStringToFile( - file_directory.AppendASCII("id_koo.md5"), "koo")); + file_directory.AppendASCII("file:id_koo.md5"), "koo")); // File with multiple extensions should be removed. ASSERT_TRUE(google_apis::test_util::WriteStringToFile( @@ -919,27 +934,27 @@ TEST_F(FileCacheTest, RenameCacheFilesToNewFormat) { file_directory.AppendASCII("id_kyu.md5"), "kyu")); // Rename and verify the result. - RenameCacheFilesToNewFormat(cache_.get()); + EXPECT_TRUE(RenameCacheFilesToNewFormat(cache_.get())); std::string contents; EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_koo"), - &contents)); + &contents)); EXPECT_EQ("koo", contents); contents.clear(); EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_kyu"), - &contents)); + &contents)); EXPECT_EQ("kyu", contents); // Rename again. - RenameCacheFilesToNewFormat(cache_.get()); + EXPECT_TRUE(RenameCacheFilesToNewFormat(cache_.get())); // Files with new style names are not affected. contents.clear(); EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_koo"), - &contents)); + &contents)); EXPECT_EQ("koo", contents); contents.clear(); EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_kyu"), - &contents)); + &contents)); EXPECT_EQ("kyu", contents); } diff --git a/chrome/browser/chromeos/drive/file_system.cc b/chrome/browser/chromeos/drive/file_system.cc index 0162221cf6..a8ee65dd2b 100644 --- a/chrome/browser/chromeos/drive/file_system.cc +++ b/chrome/browser/chromeos/drive/file_system.cc @@ -8,7 +8,6 @@ #include "base/file_util.h" #include "base/platform_file.h" #include "base/prefs/pref_service.h" -#include "base/threading/sequenced_worker_pool.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/drive/change_list_loader.h" #include "chrome/browser/chromeos/drive/change_list_processor.h" @@ -38,7 +37,6 @@ #include "chrome/browser/google_apis/drive_api_parser.h" #include "chrome/common/pref_names.h" #include "content/public/browser/browser_thread.h" -#include "net/http/http_status_code.h" using content::BrowserThread; @@ -195,45 +193,6 @@ void GetFileCallbackToFileOperationCallbackAdapter( callback.Run(error); } -// Checks whether the |url| passed to the constructor is accessible. If it is -// not, invokes |on_stale_closure|. -class StaleURLChecker : public net::URLFetcherDelegate { - public: - StaleURLChecker(const GURL& url, const base::Closure& on_stale_closure) - : on_stale_closure_(on_stale_closure) { - fetcher_.reset(net::URLFetcher::Create(url, net::URLFetcher::HEAD, this)); - fetcher_->SetRequestContext(g_browser_process->system_request_context()); - fetcher_->Start(); - } - - virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE { - int code = source->GetResponseCode(); - if (code == net::HTTP_FORBIDDEN) - on_stale_closure_.Run(); - delete this; - } - - private: - scoped_ptr<net::URLFetcher> fetcher_; - const base::Closure on_stale_closure_; -}; - -// Checks the first thumbnail URL in |entries| whether it is still available -// by sending a HEAD request. If it's stale, invokes |on_stale_closure|. -void CheckStaleThumbnailURL(ResourceEntryVector* entries, - const base::Closure& on_stale_closure) { - const char kImageThumbnailDomain[] = "googleusercontent.com"; - for (size_t i = 0; i < entries->size(); ++i) { - const std::string& url = - entries->at(i).file_specific_info().thumbnail_url(); - if (url.find(kImageThumbnailDomain) != std::string::npos) { - // The stale URL checker deletes itself. - new StaleURLChecker(GURL(url), on_stale_closure); - break; - } - } -} - // Clears |resource_metadata| and |cache|. FileError ResetOnBlockingPool(internal::ResourceMetadata* resource_metadata, internal::FileCache* cache) { @@ -792,14 +751,6 @@ void FileSystem::ReadDirectoryByPathAfterRead( filtered->push_back(entries->at(i)); } - // Thumbnail URLs are short-lived. We check the validness of the URL in - // background, and refresh the metadata for the directory if necessary. - // TODO(kinaba): Remove this hack by using persistent URLs crbug.com/254025. - CheckStaleThumbnailURL(filtered.get(), - base::Bind(&FileSystem::RefreshDirectory, - weak_ptr_factory_.GetWeakPtr(), - directory_path)); - callback.Run(FILE_ERROR_OK, filtered.Pass()); } @@ -1041,34 +992,4 @@ void FileSystem::OpenFile(const base::FilePath& file_path, open_file_operation_->OpenFile(file_path, open_mode, mime_type, callback); } -void FileSystem::RefreshDirectory(const base::FilePath& directory_path) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - resource_metadata_->GetResourceEntryByPathOnUIThread( - directory_path, - base::Bind(&FileSystem::RefreshDirectoryAfterGetResourceEntry, - weak_ptr_factory_.GetWeakPtr(), - directory_path)); -} - -void FileSystem::RefreshDirectoryAfterGetResourceEntry( - const base::FilePath& directory_path, - FileError error, - scoped_ptr<ResourceEntry> entry) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - if (error != FILE_ERROR_OK || !entry->file_info().is_directory()) - return; - - // Do not load special directories. Just return. - const std::string& id = entry->resource_id(); - if (util::IsSpecialResourceId(id)) - return; - - util::Log(logging::LOG_INFO, - "Thumbnail refresh for %s", directory_path.AsUTF8Unsafe().c_str()); - change_list_loader_->LoadDirectoryFromServer( - id, base::Bind(&util::EmptyFileOperationCallback)); -} - } // namespace drive diff --git a/chrome/browser/chromeos/drive/file_system.h b/chrome/browser/chromeos/drive/file_system.h index 07e4479fbc..4f79ed6def 100644 --- a/chrome/browser/chromeos/drive/file_system.h +++ b/chrome/browser/chromeos/drive/file_system.h @@ -36,6 +36,7 @@ class JobScheduler; namespace internal { class ChangeListLoader; +class FileCache; class ResourceMetadata; class SyncClient; } // namespace internal @@ -256,13 +257,6 @@ class FileSystem : public FileSystemInterface, google_apis::GDataErrorCode status, const GURL& share_url); - // Reloads the metadata for the directory to refresh stale thumbnail URLs. - void RefreshDirectory(const base::FilePath& directory_path); - void RefreshDirectoryAfterGetResourceEntry( - const base::FilePath& directory_path, - FileError error, - scoped_ptr<ResourceEntry> entry); - // Used to get Drive related preferences. PrefService* pref_service_; diff --git a/chrome/browser/chromeos/drive/file_system/copy_operation_unittest.cc b/chrome/browser/chromeos/drive/file_system/copy_operation_unittest.cc index 2afd3edc77..1c43ac9ac8 100644 --- a/chrome/browser/chromeos/drive/file_system/copy_operation_unittest.cc +++ b/chrome/browser/chromeos/drive/file_system/copy_operation_unittest.cc @@ -5,6 +5,7 @@ #include "chrome/browser/chromeos/drive/file_system/copy_operation.h" #include "base/file_util.h" +#include "base/task_runner_util.h" #include "chrome/browser/chromeos/drive/file_system/operation_test_base.h" #include "chrome/browser/chromeos/drive/file_system_util.h" #include "chrome/browser/drive/drive_api_util.h" @@ -60,9 +61,14 @@ TEST_F(CopyOperationTest, TransferFileFromLocalToRemote_RegularFile) { GetLocalId(remote_dest_path))); FileCacheEntry cache_entry; bool found = false; - cache()->GetCacheEntryOnUIThread( - GetLocalId(remote_dest_path), - google_apis::test_util::CreateCopyResultCallback(&found, &cache_entry)); + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&internal::FileCache::GetCacheEntry, + base::Unretained(cache()), + GetLocalId(remote_dest_path), + &cache_entry), + google_apis::test_util::CreateCopyResultCallback(&found)); test_util::RunBlockingPoolTask(); EXPECT_TRUE(found); EXPECT_TRUE(cache_entry.is_present()); diff --git a/chrome/browser/chromeos/drive/file_system/download_operation_unittest.cc b/chrome/browser/chromeos/drive/file_system/download_operation_unittest.cc index 282336a6a3..cbb75ded13 100644 --- a/chrome/browser/chromeos/drive/file_system/download_operation_unittest.cc +++ b/chrome/browser/chromeos/drive/file_system/download_operation_unittest.cc @@ -5,6 +5,7 @@ #include "chrome/browser/chromeos/drive/file_system/download_operation.h" #include "base/file_util.h" +#include "base/task_runner_util.h" #include "chrome/browser/chromeos/drive/fake_free_disk_space_getter.h" #include "chrome/browser/chromeos/drive/file_cache.h" #include "chrome/browser/chromeos/drive/file_system/operation_test_base.h" @@ -117,9 +118,13 @@ TEST_F(DownloadOperationTest, ASSERT_TRUE(google_apis::test_util::WriteStringToFile(tmp_file, content)); FileError error = FILE_ERROR_FAILED; - cache()->StoreOnUIThread( - "<id>", "<md5>", tmp_file, - internal::FileCache::FILE_OPERATION_COPY, + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&internal::FileCache::Store, + base::Unretained(cache()), + "<id>", "<md5>", tmp_file, + internal::FileCache::FILE_OPERATION_COPY), google_apis::test_util::CreateCopyResultCallback(&error)); test_util::RunBlockingPoolTask(); EXPECT_EQ(FILE_ERROR_OK, error); @@ -147,10 +152,14 @@ TEST_F(DownloadOperationTest, // The cache entry should be removed in order to free up space. FileCacheEntry cache_entry; bool result = true; - cache()->GetCacheEntryOnUIThread( - "<id>", - google_apis::test_util::CreateCopyResultCallback(&result, - &cache_entry)); + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&internal::FileCache::GetCacheEntry, + base::Unretained(cache()), + "<id>", + &cache_entry), + google_apis::test_util::CreateCopyResultCallback(&result)); test_util::RunBlockingPoolTask(); ASSERT_FALSE(result); } @@ -196,11 +205,15 @@ TEST_F(DownloadOperationTest, EnsureFileDownloadedByPath_FromCache) { // Store something as cached version of this file. FileError error = FILE_ERROR_OK; - cache()->StoreOnUIThread( - GetLocalId(file_in_root), - src_entry.file_specific_info().md5(), - temp_file, - internal::FileCache::FILE_OPERATION_COPY, + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&internal::FileCache::Store, + base::Unretained(cache()), + GetLocalId(file_in_root), + src_entry.file_specific_info().md5(), + temp_file, + internal::FileCache::FILE_OPERATION_COPY), google_apis::test_util::CreateCopyResultCallback(&error)); test_util::RunBlockingPoolTask(); EXPECT_EQ(FILE_ERROR_OK, error); @@ -359,11 +372,15 @@ TEST_F(DownloadOperationTest, EnsureFileDownloadedByLocalId_FromCache) { // Store something as cached version of this file. FileError error = FILE_ERROR_FAILED; - cache()->StoreOnUIThread( - GetLocalId(file_in_root), - src_entry.file_specific_info().md5(), - temp_file, - internal::FileCache::FILE_OPERATION_COPY, + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&internal::FileCache::Store, + base::Unretained(cache()), + GetLocalId(file_in_root), + src_entry.file_specific_info().md5(), + temp_file, + internal::FileCache::FILE_OPERATION_COPY), google_apis::test_util::CreateCopyResultCallback(&error)); test_util::RunBlockingPoolTask(); EXPECT_EQ(FILE_ERROR_OK, error); @@ -402,16 +419,24 @@ TEST_F(DownloadOperationTest, EnsureFileDownloadedByPath_DirtyCache) { // Store the file as a cache, marking it to be dirty. FileError error = FILE_ERROR_FAILED; - cache()->StoreOnUIThread( - GetLocalId(file_in_root), - src_entry.file_specific_info().md5(), - dirty_file, - internal::FileCache::FILE_OPERATION_COPY, + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&internal::FileCache::Store, + base::Unretained(cache()), + GetLocalId(file_in_root), + src_entry.file_specific_info().md5(), + dirty_file, + internal::FileCache::FILE_OPERATION_COPY), google_apis::test_util::CreateCopyResultCallback(&error)); test_util::RunBlockingPoolTask(); EXPECT_EQ(FILE_ERROR_OK, error); - cache()->MarkDirtyOnUIThread( - GetLocalId(file_in_root), + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&internal::FileCache::MarkDirty, + base::Unretained(cache()), + GetLocalId(file_in_root)), google_apis::test_util::CreateCopyResultCallback(&error)); test_util::RunBlockingPoolTask(); EXPECT_EQ(FILE_ERROR_OK, error); diff --git a/chrome/browser/chromeos/drive/file_system/get_file_for_saving_operation.cc b/chrome/browser/chromeos/drive/file_system/get_file_for_saving_operation.cc index fb5a79ef80..e1964a4efb 100644 --- a/chrome/browser/chromeos/drive/file_system/get_file_for_saving_operation.cc +++ b/chrome/browser/chromeos/drive/file_system/get_file_for_saving_operation.cc @@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/bind_helpers.h" +#include "chrome/browser/chromeos/drive/file_cache.h" #include "chrome/browser/chromeos/drive/file_system/create_file_operation.h" #include "chrome/browser/chromeos/drive/file_system/download_operation.h" #include "chrome/browser/chromeos/drive/file_system/operation_observer.h" diff --git a/chrome/browser/chromeos/drive/file_system/get_file_for_saving_operation_unittest.cc b/chrome/browser/chromeos/drive/file_system/get_file_for_saving_operation_unittest.cc index a234111513..4efa89b893 100644 --- a/chrome/browser/chromeos/drive/file_system/get_file_for_saving_operation_unittest.cc +++ b/chrome/browser/chromeos/drive/file_system/get_file_for_saving_operation_unittest.cc @@ -8,6 +8,7 @@ #include "base/file_util.h" #include "base/files/file_path.h" #include "base/run_loop.h" +#include "base/task_runner_util.h" #include "chrome/browser/chromeos/drive/drive.pb.h" #include "chrome/browser/chromeos/drive/file_errors.h" #include "chrome/browser/chromeos/drive/file_system/operation_test_base.h" @@ -92,9 +93,14 @@ TEST_F(GetFileForSavingOperationTest, GetFileForSaving_Exist) { // Checks that it presents in cache and marked dirty. bool success = false; FileCacheEntry cache_entry; - cache()->GetCacheEntryOnUIThread( - GetLocalId(drive_path), - google_apis::test_util::CreateCopyResultCallback(&success, &cache_entry)); + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&internal::FileCache::GetCacheEntry, + base::Unretained(cache()), + GetLocalId(drive_path), + &cache_entry), + google_apis::test_util::CreateCopyResultCallback(&success)); test_util::RunBlockingPoolTask(); EXPECT_TRUE(success); EXPECT_TRUE(cache_entry.is_present()); diff --git a/chrome/browser/chromeos/drive/file_system/open_file_operation_unittest.cc b/chrome/browser/chromeos/drive/file_system/open_file_operation_unittest.cc index 3a7eba4e92..8bfdeb4d05 100644 --- a/chrome/browser/chromeos/drive/file_system/open_file_operation_unittest.cc +++ b/chrome/browser/chromeos/drive/file_system/open_file_operation_unittest.cc @@ -9,6 +9,7 @@ #include "base/file_util.h" #include "base/files/file_path.h" #include "base/memory/scoped_ptr.h" +#include "base/task_runner_util.h" #include "chrome/browser/chromeos/drive/drive.pb.h" #include "chrome/browser/chromeos/drive/file_errors.h" #include "chrome/browser/chromeos/drive/file_system/operation_test_base.h" @@ -168,9 +169,14 @@ TEST_F(OpenFileOperationTest, OpenOrCreateExistingFile) { bool success = false; FileCacheEntry cache_entry; - cache()->GetCacheEntryOnUIThread( - src_entry.local_id(), - google_apis::test_util::CreateCopyResultCallback(&success, &cache_entry)); + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&internal::FileCache::GetCacheEntry, + base::Unretained(cache()), + src_entry.local_id(), + &cache_entry), + google_apis::test_util::CreateCopyResultCallback(&success)); test_util::RunBlockingPoolTask(); EXPECT_TRUE(success); EXPECT_TRUE(cache_entry.is_present()); diff --git a/chrome/browser/chromeos/drive/file_system/update_operation_unittest.cc b/chrome/browser/chromeos/drive/file_system/update_operation_unittest.cc index f194efa711..4964acfaf6 100644 --- a/chrome/browser/chromeos/drive/file_system/update_operation_unittest.cc +++ b/chrome/browser/chromeos/drive/file_system/update_operation_unittest.cc @@ -4,6 +4,7 @@ #include "chrome/browser/chromeos/drive/file_system/update_operation.h" +#include "base/task_runner_util.h" #include "chrome/browser/chromeos/drive/file_system/operation_test_base.h" #include "chrome/browser/chromeos/drive/file_system_interface.h" #include "chrome/browser/drive/fake_drive_service.h" @@ -50,17 +51,25 @@ TEST_F(UpdateOperationTest, UpdateFileByLocalId_PersistentFile) { // First store a file to cache. error = FILE_ERROR_FAILED; - cache()->StoreOnUIThread( - local_id, kMd5, kTestFile, - internal::FileCache::FILE_OPERATION_COPY, + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&internal::FileCache::Store, + base::Unretained(cache()), + local_id, kMd5, kTestFile, + internal::FileCache::FILE_OPERATION_COPY), google_apis::test_util::CreateCopyResultCallback(&error)); test_util::RunBlockingPoolTask(); EXPECT_EQ(FILE_ERROR_OK, error); // Add the dirty bit. error = FILE_ERROR_FAILED; - cache()->MarkDirtyOnUIThread( - local_id, + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&internal::FileCache::MarkDirty, + base::Unretained(cache()), + local_id), google_apis::test_util::CreateCopyResultCallback(&error)); test_util::RunBlockingPoolTask(); EXPECT_EQ(FILE_ERROR_OK, error); @@ -95,9 +104,14 @@ TEST_F(UpdateOperationTest, UpdateFileByLocalId_PersistentFile) { // Make sure that the cache is no longer dirty. bool success = false; FileCacheEntry cache_entry; - cache()->GetCacheEntryOnUIThread( - local_id, - google_apis::test_util::CreateCopyResultCallback(&success, &cache_entry)); + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&internal::FileCache::GetCacheEntry, + base::Unretained(cache()), + local_id, + &cache_entry), + google_apis::test_util::CreateCopyResultCallback(&success)); test_util::RunBlockingPoolTask(); ASSERT_TRUE(success); EXPECT_FALSE(cache_entry.is_dirty()); @@ -128,17 +142,25 @@ TEST_F(UpdateOperationTest, UpdateFileByLocalId_Md5) { // First store a file to cache. FileError error = FILE_ERROR_FAILED; - cache()->StoreOnUIThread( - local_id, kMd5, kTestFile, - internal::FileCache::FILE_OPERATION_COPY, + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&internal::FileCache::Store, + base::Unretained(cache()), + local_id, kMd5, kTestFile, + internal::FileCache::FILE_OPERATION_COPY), google_apis::test_util::CreateCopyResultCallback(&error)); test_util::RunBlockingPoolTask(); EXPECT_EQ(FILE_ERROR_OK, error); // Add the dirty bit. error = FILE_ERROR_FAILED; - cache()->MarkDirtyOnUIThread( - local_id, + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&internal::FileCache::MarkDirty, + base::Unretained(cache()), + local_id), google_apis::test_util::CreateCopyResultCallback(&error)); test_util::RunBlockingPoolTask(); EXPECT_EQ(FILE_ERROR_OK, error); @@ -173,17 +195,26 @@ TEST_F(UpdateOperationTest, UpdateFileByLocalId_Md5) { // Make sure that the cache is no longer dirty. bool success = false; FileCacheEntry cache_entry; - cache()->GetCacheEntryOnUIThread( - local_id, - google_apis::test_util::CreateCopyResultCallback(&success, &cache_entry)); + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&internal::FileCache::GetCacheEntry, + base::Unretained(cache()), + local_id, + &cache_entry), + google_apis::test_util::CreateCopyResultCallback(&success)); test_util::RunBlockingPoolTask(); ASSERT_TRUE(success); EXPECT_FALSE(cache_entry.is_dirty()); // Again mark the cache file dirty. error = FILE_ERROR_FAILED; - cache()->MarkDirtyOnUIThread( - local_id, + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&internal::FileCache::MarkDirty, + base::Unretained(cache()), + local_id), google_apis::test_util::CreateCopyResultCallback(&error)); test_util::RunBlockingPoolTask(); EXPECT_EQ(FILE_ERROR_OK, error); @@ -205,17 +236,26 @@ TEST_F(UpdateOperationTest, UpdateFileByLocalId_Md5) { // Make sure that the cache is no longer dirty. success = false; - cache()->GetCacheEntryOnUIThread( - local_id, - google_apis::test_util::CreateCopyResultCallback(&success, &cache_entry)); + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&internal::FileCache::GetCacheEntry, + base::Unretained(cache()), + local_id, + &cache_entry), + google_apis::test_util::CreateCopyResultCallback(&success)); test_util::RunBlockingPoolTask(); ASSERT_TRUE(success); EXPECT_FALSE(cache_entry.is_dirty()); // Once again mark the cache file dirty. error = FILE_ERROR_FAILED; - cache()->MarkDirtyOnUIThread( - local_id, + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&internal::FileCache::MarkDirty, + base::Unretained(cache()), + local_id), google_apis::test_util::CreateCopyResultCallback(&error)); test_util::RunBlockingPoolTask(); EXPECT_EQ(FILE_ERROR_OK, error); diff --git a/chrome/browser/chromeos/drive/file_system_interface.h b/chrome/browser/chromeos/drive/file_system_interface.h index 59373eeea1..93870eb80e 100644 --- a/chrome/browser/chromeos/drive/file_system_interface.h +++ b/chrome/browser/chromeos/drive/file_system_interface.h @@ -10,7 +10,6 @@ #include "base/memory/scoped_ptr.h" #include "chrome/browser/chromeos/drive/drive.pb.h" -#include "chrome/browser/chromeos/drive/file_cache.h" #include "chrome/browser/chromeos/drive/file_system_metadata.h" #include "chrome/browser/chromeos/drive/resource_metadata.h" #include "chrome/browser/google_apis/base_requests.h" @@ -120,6 +119,12 @@ typedef base::Callback<void(FileError error, const base::FilePath& file_path)> MarkMountedCallback; +// Callback for GetCacheEntryByPath. +// |success| indicates if the operation was successful. +// |cache_entry| is the obtained cache entry. +typedef base::Callback<void(bool success, const FileCacheEntry& cache_entry)> + GetCacheEntryCallback; + // The mode of opening a file. enum OpenMode { // Open the file if exists. If not, failed. diff --git a/chrome/browser/chromeos/drive/file_system_unittest.cc b/chrome/browser/chromeos/drive/file_system_unittest.cc index 9715fb6036..29623b0654 100644 --- a/chrome/browser/chromeos/drive/file_system_unittest.cc +++ b/chrome/browser/chromeos/drive/file_system_unittest.cc @@ -33,8 +33,6 @@ namespace drive { namespace { -const int64 kLotsOfSpace = internal::kMinFreeSpace * 10; - // Counts the number of invocation, and if it increased up to |expected_counter| // quits the current message loop by calling |quit|. void AsyncInitializationCallback( diff --git a/chrome/browser/chromeos/drive/file_system_util.cc b/chrome/browser/chromeos/drive/file_system_util.cc index db13b712cb..40acfd7440 100644 --- a/chrome/browser/chromeos/drive/file_system_util.cc +++ b/chrome/browser/chromeos/drive/file_system_util.cc @@ -10,7 +10,6 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/file_util.h" -#include "base/files/file_enumerator.h" #include "base/files/file_path.h" #include "base/files/scoped_platform_file_closer.h" #include "base/i18n/icu_string_conversions.h" @@ -101,19 +100,6 @@ std::string ReadStringFromGDocFile(const base::FilePath& file_path, return result; } -// Moves all files under |directory_from| to |directory_to|. -void MoveAllFilesFromDirectory(const base::FilePath& directory_from, - const base::FilePath& directory_to) { - base::FileEnumerator enumerator(directory_from, false, // not recursive - base::FileEnumerator::FILES); - for (base::FilePath file_from = enumerator.Next(); !file_from.empty(); - file_from = enumerator.Next()) { - const base::FilePath file_to = directory_to.Append(file_from.BaseName()); - if (!base::PathExists(file_to)) // Do not overwrite existing files. - base::Move(file_from, file_to); - } -} - // Returns DriveIntegrationService instance, if Drive is enabled. // Otherwise, NULL. DriveIntegrationService* GetIntegrationServiceByProfile(Profile* profile) { @@ -323,27 +309,6 @@ std::string NormalizeFileName(const std::string& input) { return output; } -void MigrateCacheFilesFromOldDirectories( - const base::FilePath& cache_root_directory, - const base::FilePath::StringType& cache_file_directory_name) { - const base::FilePath persistent_directory = - cache_root_directory.AppendASCII("persistent"); - const base::FilePath tmp_directory = - cache_root_directory.AppendASCII("tmp"); - if (!base::PathExists(persistent_directory)) - return; - - const base::FilePath cache_file_directory = - cache_root_directory.Append(cache_file_directory_name); - - // Move all files inside "persistent" to "files". - MoveAllFilesFromDirectory(persistent_directory, cache_file_directory); - base::DeleteFile(persistent_directory, true /* recursive */); - - // Move all files inside "tmp" to "files". - MoveAllFilesFromDirectory(tmp_directory, cache_file_directory); -} - void PrepareWritableFileAndRun(Profile* profile, const base::FilePath& path, const PrepareWritableFileCallback& callback) { diff --git a/chrome/browser/chromeos/drive/file_system_util.h b/chrome/browser/chromeos/drive/file_system_util.h index a10e497faf..8af03ec908 100644 --- a/chrome/browser/chromeos/drive/file_system_util.h +++ b/chrome/browser/chromeos/drive/file_system_util.h @@ -141,13 +141,6 @@ std::string NormalizeFileName(const std::string& input); // profile. base::FilePath GetCacheRootPath(Profile* profile); -// Migrates cache files from old "persistent" and "tmp" directories to the new -// "files" directory (see crbug.com/248905). -// TODO(hashimoto): Remove this function at some point. -void MigrateCacheFilesFromOldDirectories( - const base::FilePath& cache_root_directory, - const base::FilePath::StringType& cache_file_directory_name); - // Callback type for PrepareWritableFileAndRun. typedef base::Callback<void (FileError, const base::FilePath& path)> PrepareWritableFileCallback; diff --git a/chrome/browser/chromeos/drive/file_system_util_unittest.cc b/chrome/browser/chromeos/drive/file_system_util_unittest.cc index a8b597943c..b298a10c4e 100644 --- a/chrome/browser/chromeos/drive/file_system_util_unittest.cc +++ b/chrome/browser/chromeos/drive/file_system_util_unittest.cc @@ -180,36 +180,6 @@ TEST(FileSystemUtilTest, GetCacheRootPath) { util::GetCacheRootPath(&profile)); } -TEST(FileSystemUtilTest, MigrateCacheFilesFromOldDirectories) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - - const base::FilePath persistent_directory = - temp_dir.path().AppendASCII("persistent"); - const base::FilePath tmp_directory = temp_dir.path().AppendASCII("tmp"); - const base::FilePath files_directory = - temp_dir.path().AppendASCII("files"); - - // Prepare directories. - ASSERT_TRUE(file_util::CreateDirectory(persistent_directory)); - ASSERT_TRUE(file_util::CreateDirectory(tmp_directory)); - ASSERT_TRUE(file_util::CreateDirectory(files_directory)); - - // Put some files. - ASSERT_TRUE(google_apis::test_util::WriteStringToFile( - persistent_directory.AppendASCII("foo.abc"), "foo")); - ASSERT_TRUE(google_apis::test_util::WriteStringToFile( - tmp_directory.AppendASCII("bar.123"), "bar")); - - // Migrate. - MigrateCacheFilesFromOldDirectories(temp_dir.path(), - FILE_PATH_LITERAL("files")); - - EXPECT_FALSE(base::PathExists(persistent_directory)); - EXPECT_TRUE(base::PathExists(files_directory.AppendASCII("foo.abc"))); - EXPECT_TRUE(base::PathExists(files_directory.AppendASCII("bar.123"))); -} - TEST(FileSystemUtilTest, NeedsNamespaceMigration) { // Not Drive cases. EXPECT_FALSE(NeedsNamespaceMigration( diff --git a/chrome/browser/chromeos/drive/resource_entry_conversion.cc b/chrome/browser/chromeos/drive/resource_entry_conversion.cc index e0a7cd957c..20aa9783b5 100644 --- a/chrome/browser/chromeos/drive/resource_entry_conversion.cc +++ b/chrome/browser/chromeos/drive/resource_entry_conversion.cc @@ -101,11 +101,6 @@ bool ConvertToResourceEntry(const google_apis::ResourceEntry& input, file_specific_info->set_content_mime_type(input.content_mime_type()); file_specific_info->set_is_hosted_document(input.is_hosted_document()); - const google_apis::Link* thumbnail_link = - input.GetLinkByType(google_apis::Link::LINK_THUMBNAIL); - if (thumbnail_link) - file_specific_info->set_thumbnail_url(thumbnail_link->href().spec()); - const google_apis::Link* alternate_link = input.GetLinkByType(google_apis::Link::LINK_ALTERNATE); if (alternate_link) diff --git a/chrome/browser/chromeos/drive/resource_entry_conversion_unittest.cc b/chrome/browser/chromeos/drive/resource_entry_conversion_unittest.cc index 95b573aa7a..f49277f6f7 100644 --- a/chrome/browser/chromeos/drive/resource_entry_conversion_unittest.cc +++ b/chrome/browser/chromeos/drive/resource_entry_conversion_unittest.cc @@ -78,8 +78,6 @@ TEST(ResourceEntryConversionTest, ConvertToResourceEntry_File) { EXPECT_EQ("audio/mpeg", entry.file_specific_info().content_mime_type()); EXPECT_FALSE(entry.file_specific_info().is_hosted_document()); - EXPECT_EQ("", - entry.file_specific_info().thumbnail_url()); EXPECT_EQ("https://file_link_alternate/", entry.file_specific_info().alternate_url()); @@ -162,8 +160,6 @@ TEST(ResourceEntryConversionTest, EXPECT_EQ("text/html", entry.file_specific_info().content_mime_type()); EXPECT_TRUE(entry.file_specific_info().is_hosted_document()); - EXPECT_EQ("https://3_document_thumbnail_link/", - entry.file_specific_info().thumbnail_url()); EXPECT_EQ("https://3_document_alternate_link/", entry.file_specific_info().alternate_url()); @@ -316,8 +312,6 @@ TEST(ResourceEntryConversionTest, EXPECT_EQ("text/html", entry.file_specific_info().content_mime_type()); EXPECT_TRUE(entry.file_specific_info().is_hosted_document()); - EXPECT_EQ("", - entry.file_specific_info().thumbnail_url()); EXPECT_EQ("https://alternate/document%3Adeleted_in_root_id/edit", entry.file_specific_info().alternate_url()); diff --git a/chrome/browser/chromeos/drive/resource_metadata.cc b/chrome/browser/chromeos/drive/resource_metadata.cc index de07b4d250..b0e5f87358 100644 --- a/chrome/browser/chromeos/drive/resource_metadata.cc +++ b/chrome/browser/chromeos/drive/resource_metadata.cc @@ -4,6 +4,7 @@ #include "chrome/browser/chromeos/drive/resource_metadata.h" +#include "base/guid.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/sys_info.h" @@ -161,6 +162,7 @@ FileError ResourceMetadata::SetLargestChangestamp(int64 value) { FileError ResourceMetadata::AddEntry(const ResourceEntry& entry, std::string* out_id) { DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); + DCHECK(entry.local_id().empty()); if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path())) return FILE_ERROR_NO_LOCAL_SPACE; @@ -177,8 +179,13 @@ FileError ResourceMetadata::AddEntry(const ResourceEntry& entry, !parent.file_info().is_directory()) return FILE_ERROR_NOT_FOUND; - // TODO(hashimoto): Generate local ID here. crbug.com/26051 - const std::string local_id = entry.resource_id(); + // Generate unique local ID. + std::string local_id; + ResourceEntry existing_entry; + do { + local_id = base::GenerateGUID(); + } while (storage_->GetEntry(local_id, &existing_entry)); + ResourceEntry new_entry(entry); new_entry.set_local_id(local_id); @@ -208,24 +215,6 @@ FileError ResourceMetadata::RemoveEntry(const std::string& id) { return FILE_ERROR_OK; } -void ResourceMetadata::GetResourceEntryByIdOnUIThread( - const std::string& id, - const GetResourceEntryCallback& callback) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK(!callback.is_null()); - - scoped_ptr<ResourceEntry> entry(new ResourceEntry); - ResourceEntry* entry_ptr = entry.get(); - base::PostTaskAndReplyWithResult( - blocking_task_runner_.get(), - FROM_HERE, - base::Bind(&ResourceMetadata::GetResourceEntryById, - base::Unretained(this), - id, - entry_ptr), - base::Bind(&RunGetResourceEntryCallback, callback, base::Passed(&entry))); -} - FileError ResourceMetadata::GetResourceEntryById(const std::string& id, ResourceEntry* out_entry) { DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); @@ -415,13 +404,8 @@ FileError ResourceMetadata::GetIdByResourceId(const std::string& resource_id, std::string* out_local_id) { DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); - // TODO(hashimoto): Implement the real resource ID to local ID look up. - // crbug.com/260514 - ResourceEntry entry; - FileError error = GetResourceEntryById(resource_id, &entry); - if (error == FILE_ERROR_OK) - *out_local_id = resource_id; - return error; + return storage_->GetIdByResourceId(resource_id, out_local_id) ? + FILE_ERROR_OK : FILE_ERROR_NOT_FOUND; } bool ResourceMetadata::PutEntryUnderDirectory(const ResourceEntry& entry) { diff --git a/chrome/browser/chromeos/drive/resource_metadata.h b/chrome/browser/chromeos/drive/resource_metadata.h index aa40bed61f..e669114490 100644 --- a/chrome/browser/chromeos/drive/resource_metadata.h +++ b/chrome/browser/chromeos/drive/resource_metadata.h @@ -73,12 +73,6 @@ class ResourceMetadata { FileError RemoveEntry(const std::string& id); // Finds an entry (a file or a directory) by |id|. - // |callback| must not be null. - // Must be called on the UI thread. - void GetResourceEntryByIdOnUIThread(const std::string& id, - const GetResourceEntryCallback& callback); - - // Synchronous version of GetResourceEntryByIdOnUIThread(). FileError GetResourceEntryById(const std::string& id, ResourceEntry* out_entry); diff --git a/chrome/browser/chromeos/drive/resource_metadata_storage.cc b/chrome/browser/chromeos/drive/resource_metadata_storage.cc index 528998a779..948e737f4e 100644 --- a/chrome/browser/chromeos/drive/resource_metadata_storage.cc +++ b/chrome/browser/chromeos/drive/resource_metadata_storage.cc @@ -42,6 +42,9 @@ const char kDBKeyDelimeter = '\0'; // String used as a suffix of a key for a cache entry. const char kCacheEntryKeySuffix[] = "CACHE"; +// String used as a prefix of a key for a resource-ID-to-local-ID entry. +const char kIdEntryKeyPrefix[] = "ID"; + // Returns a string to be used as the key for the header. std::string GetHeaderDBKey() { std::string key; @@ -77,6 +80,30 @@ bool IsCacheEntryKey(const leveldb::Slice& key) { return key_substring.compare(expected_suffix) == 0; } +// Returns a string to be used as a key for a resource-ID-to-local-ID entry. +std::string GetIdEntryKey(const std::string& resource_id) { + std::string key; + key.push_back(kDBKeyDelimeter); + key.append(kIdEntryKeyPrefix); + key.push_back(kDBKeyDelimeter); + key.append(resource_id); + return key; +} + +// Returns true if |key| is a key for a resource-ID-to-local-ID entry. +bool IsIdEntryKey(const leveldb::Slice& key) { + // A resource-ID-to-local-ID entry key should start with + // |kDBKeyDelimeter + kIdEntryKeyPrefix + kDBKeyDelimeter|. + const leveldb::Slice expected_prefix(kIdEntryKeyPrefix, + arraysize(kIdEntryKeyPrefix) - 1); + if (key.size() < 2 + expected_prefix.size()) + return false; + const leveldb::Slice key_substring(key.data() + 1, expected_prefix.size()); + return key[0] == kDBKeyDelimeter && + key_substring.compare(expected_prefix) == 0 && + key[expected_prefix.size() + 1] == kDBKeyDelimeter; +} + // Converts leveldb::Status to DBInitStatus. DBInitStatus LevelDBStatusToDBInitStatus(const leveldb::Status status) { if (status.ok()) @@ -411,30 +438,49 @@ bool ResourceMetadataStorage::PutEntry(const ResourceEntry& entry) { const std::string& id = entry.local_id(); DCHECK(!id.empty()); + // Try to get existing entry. std::string serialized_entry; - if (!entry.SerializeToString(&serialized_entry)) { - DLOG(ERROR) << "Failed to serialize the entry: " << id; + leveldb::Status status = resource_map_->Get(leveldb::ReadOptions(), + leveldb::Slice(id), + &serialized_entry); + if (!status.ok() && !status.IsNotFound()) // Unexpected errors. + return false; + + ResourceEntry old_entry; + if (status.ok() && !old_entry.ParseFromString(serialized_entry)) return false; - } + // Construct write batch. leveldb::WriteBatch batch; // Remove from the old parent. - ResourceEntry old_entry; - if (GetEntry(id, &old_entry) && !old_entry.parent_local_id().empty()) { + if (!old_entry.parent_local_id().empty()) { batch.Delete(GetChildEntryKey(old_entry.parent_local_id(), old_entry.base_name())); } - // Add to the new parent. if (!entry.parent_local_id().empty()) batch.Put(GetChildEntryKey(entry.parent_local_id(), entry.base_name()), id); + // Refresh resource-ID-to-local-ID mapping entry. + if (old_entry.resource_id() != entry.resource_id()) { + // Resource ID should not change. + DCHECK(old_entry.resource_id().empty() || entry.resource_id().empty()); + + if (!old_entry.resource_id().empty()) + batch.Delete(GetIdEntryKey(old_entry.resource_id())); + if (!entry.resource_id().empty()) + batch.Put(GetIdEntryKey(entry.resource_id()), id); + } + // Put the entry itself. + if (!entry.SerializeToString(&serialized_entry)) { + DLOG(ERROR) << "Failed to serialize the entry: " << id; + return false; + } batch.Put(id, serialized_entry); - const leveldb::Status status = resource_map_->Write(leveldb::WriteOptions(), - &batch); + status = resource_map_->Write(leveldb::WriteOptions(), &batch); return status.ok(); } @@ -464,6 +510,10 @@ bool ResourceMetadataStorage::RemoveEntry(const std::string& id) { if (!entry.parent_local_id().empty()) batch.Delete(GetChildEntryKey(entry.parent_local_id(), entry.base_name())); + // Remove resource ID-local ID mapping entry. + if (!entry.resource_id().empty()) + batch.Delete(GetIdEntryKey(entry.resource_id())); + // Remove the entry itself. batch.Delete(id); @@ -561,6 +611,19 @@ ResourceMetadataStorage::GetCacheEntryIterator() { return make_scoped_ptr(new CacheEntryIterator(it.Pass())); } +bool ResourceMetadataStorage::GetIdByResourceId( + const std::string& resource_id, + std::string* out_id) { + base::ThreadRestrictions::AssertIOAllowed(); + DCHECK(!resource_id.empty()); + + const leveldb::Status status = resource_map_->Get( + leveldb::ReadOptions(), + leveldb::Slice(GetIdEntryKey(resource_id)), + out_id); + return status.ok(); +} + ResourceMetadataStorage::~ResourceMetadataStorage() { base::ThreadRestrictions::AssertIOAllowed(); } @@ -625,6 +688,9 @@ bool ResourceMetadataStorage::CheckValidity() { // // <key> : <value> // "\0HEADER" : ResourceMetadataHeader + // "\0ID\0|resource ID 1|" : Local ID associated to resource ID 1. + // "\0ID\0|resource ID 2|" : Local ID associated to resource ID 2. + // ... // "|ID of A|" : ResourceEntry for entry A. // "|ID of A|\0CACHE" : FileCacheEntry for entry A. // "|ID of A|\0|child name 1|\0" : ID of the 1st child entry of entry A. @@ -649,7 +715,7 @@ bool ResourceMetadataStorage::CheckValidity() { size_t num_entries_with_parent = 0; size_t num_child_entries = 0; ResourceEntry entry; - std::string serialized_parent_entry; + std::string serialized_entry; std::string child_id; for (it->Next(); it->Valid(); it->Next()) { // Count child entries. @@ -662,6 +728,22 @@ bool ResourceMetadataStorage::CheckValidity() { if (IsCacheEntryKey(it->key())) continue; + // Check if resource-ID-to-local-ID mapping is stored correctly. + if (IsIdEntryKey(it->key())) { + leveldb::Status status = resource_map_->Get( + options, + it->value(), + &serialized_entry); + if (!status.ok() || + !entry.ParseFromString(serialized_entry) || + entry.resource_id().empty() || + leveldb::Slice(GetIdEntryKey(entry.resource_id())) != it->key()) { + DLOG(ERROR) << "Broken ID entry. status = " << status.ToString(); + return false; + } + continue; + } + // Check if stored data is broken. if (!entry.ParseFromArray(it->value().data(), it->value().size())) { DLOG(ERROR) << "Broken entry detected"; @@ -673,7 +755,7 @@ bool ResourceMetadataStorage::CheckValidity() { leveldb::Status status = resource_map_->Get( options, leveldb::Slice(entry.parent_local_id()), - &serialized_parent_entry); + &serialized_entry); if (!status.ok()) { DLOG(ERROR) << "Can't get parent entry. status = " << status.ToString(); return false; diff --git a/chrome/browser/chromeos/drive/resource_metadata_storage.h b/chrome/browser/chromeos/drive/resource_metadata_storage.h index 05d7ef28eb..04a2224f29 100644 --- a/chrome/browser/chromeos/drive/resource_metadata_storage.h +++ b/chrome/browser/chromeos/drive/resource_metadata_storage.h @@ -36,7 +36,7 @@ class ResourceMetadataStorage { public: // This should be incremented when incompatibility change is made to DB // format. - static const int kDBVersion = 10; + static const int kDBVersion = 11; // Object to iterate over entries stored in this storage. class Iterator { @@ -157,6 +157,9 @@ class ResourceMetadataStorage { // Returns an object to iterate over cache entries stored in this storage. scoped_ptr<CacheEntryIterator> GetCacheEntryIterator(); + // Returns the local ID associated with the given resource ID. + bool GetIdByResourceId(const std::string& resource_id, std::string* out_id); + private: friend class ResourceMetadataStorageTest; diff --git a/chrome/browser/chromeos/drive/resource_metadata_storage_unittest.cc b/chrome/browser/chromeos/drive/resource_metadata_storage_unittest.cc index 9dbd6dda13..f4cbfb6665 100644 --- a/chrome/browser/chromeos/drive/resource_metadata_storage_unittest.cc +++ b/chrome/browser/chromeos/drive/resource_metadata_storage_unittest.cc @@ -246,6 +246,29 @@ TEST_F(ResourceMetadataStorageTest, CacheEntryIterator) { EXPECT_EQ(entries.size(), num_entries); } +TEST_F(ResourceMetadataStorageTest, GetIdByResourceId) { + const std::string local_id = "local_id"; + const std::string resource_id = "resource_id"; + + // Resource ID to local ID mapping is not stored yet. + std::string id; + EXPECT_FALSE(storage_->GetIdByResourceId(resource_id, &id)); + + // Put an entry with the resource ID. + ResourceEntry entry; + entry.set_local_id(local_id); + entry.set_resource_id(resource_id); + EXPECT_TRUE(storage_->PutEntry(entry)); + + // Can get local ID by resource ID. + EXPECT_TRUE(storage_->GetIdByResourceId(resource_id, &id)); + EXPECT_EQ(local_id, id); + + // Resource ID to local ID mapping is removed. + EXPECT_TRUE(storage_->RemoveEntry(local_id)); + EXPECT_FALSE(storage_->GetIdByResourceId(resource_id, &id)); +} + TEST_F(ResourceMetadataStorageTest, GetChildren) { const std::string parents_id[] = { "mercury", "venus", "mars", "jupiter", "saturn" }; diff --git a/chrome/browser/chromeos/drive/resource_metadata_unittest.cc b/chrome/browser/chromeos/drive/resource_metadata_unittest.cc index b5a279eeec..c8b660f09b 100644 --- a/chrome/browser/chromeos/drive/resource_metadata_unittest.cc +++ b/chrome/browser/chromeos/drive/resource_metadata_unittest.cc @@ -195,56 +195,6 @@ class ResourceMetadataTestOnUIThread : public testing::Test { resource_metadata_; }; -TEST_F(ResourceMetadataTestOnUIThread, GetResourceEntryById_RootDirectory) { - // Look up the root directory by its resource ID. - FileError error = FILE_ERROR_FAILED; - scoped_ptr<ResourceEntry> entry; - resource_metadata_->GetResourceEntryByIdOnUIThread( - util::kDriveGrandRootSpecialResourceId, - google_apis::test_util::CreateCopyResultCallback(&error, &entry)); - test_util::RunBlockingPoolTask(); - EXPECT_EQ(FILE_ERROR_OK, error); - ASSERT_TRUE(entry.get()); - EXPECT_EQ("drive", entry->base_name()); -} - -TEST_F(ResourceMetadataTestOnUIThread, GetResourceEntryById) { - // Get file4 by path. - FileError error = FILE_ERROR_FAILED; - std::string local_id; - base::PostTaskAndReplyWithResult( - blocking_task_runner_, - FROM_HERE, - base::Bind(&ResourceMetadata::GetIdByPath, - base::Unretained(resource_metadata_.get()), - base::FilePath::FromUTF8Unsafe("drive/root/dir1/file4"), - &local_id), - google_apis::test_util::CreateCopyResultCallback(&error)); - test_util::RunBlockingPoolTask(); - EXPECT_EQ(FILE_ERROR_OK, error); - - // Confirm that an existing file is found. - error = FILE_ERROR_FAILED; - scoped_ptr<ResourceEntry> entry; - resource_metadata_->GetResourceEntryByIdOnUIThread( - local_id, - google_apis::test_util::CreateCopyResultCallback(&error, &entry)); - test_util::RunBlockingPoolTask(); - EXPECT_EQ(FILE_ERROR_OK, error); - ASSERT_TRUE(entry.get()); - EXPECT_EQ("file4", entry->base_name()); - - // Confirm that a non existing file is not found. - error = FILE_ERROR_FAILED; - entry.reset(); - resource_metadata_->GetResourceEntryByIdOnUIThread( - "file:non_existing", - google_apis::test_util::CreateCopyResultCallback(&error, &entry)); - test_util::RunBlockingPoolTask(); - EXPECT_EQ(FILE_ERROR_NOT_FOUND, error); - EXPECT_FALSE(entry.get()); -} - TEST_F(ResourceMetadataTestOnUIThread, GetResourceEntryByPath) { // Confirm that an existing file is found. FileError error = FILE_ERROR_FAILED; @@ -612,6 +562,31 @@ TEST_F(ResourceMetadataTest, RemoveEntry) { util::kDriveGrandRootSpecialResourceId)); } +TEST_F(ResourceMetadataTest, GetResourceEntryById_RootDirectory) { + // Look up the root directory by its ID. + ResourceEntry entry; + EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->GetResourceEntryById( + util::kDriveGrandRootSpecialResourceId, &entry)); + EXPECT_EQ("drive", entry.base_name()); +} + +TEST_F(ResourceMetadataTest, GetResourceEntryById) { + // Get file4 by path. + std::string local_id; + EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->GetIdByPath( + base::FilePath::FromUTF8Unsafe("drive/root/dir1/file4"), &local_id)); + + // Confirm that an existing file is found. + ResourceEntry entry; + EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->GetResourceEntryById( + local_id, &entry)); + EXPECT_EQ("file4", entry.base_name()); + + // Confirm that a non existing file is not found. + EXPECT_EQ(FILE_ERROR_NOT_FOUND, resource_metadata_->GetResourceEntryById( + "file:non_existing", &entry)); +} + TEST_F(ResourceMetadataTest, Iterate) { scoped_ptr<ResourceMetadata::Iterator> it = resource_metadata_->GetIterator(); ASSERT_TRUE(it); diff --git a/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.cc b/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.cc new file mode 100644 index 0000000000..0505894a6e --- /dev/null +++ b/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.cc @@ -0,0 +1,106 @@ +// Copyright 2013 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/chromeos/extensions/device_local_account_external_policy_loader.h" + +#include "base/callback.h" +#include "base/logging.h" +#include "base/prefs/pref_value_map.h" +#include "base/values.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/extensions/policy_handlers.h" +#include "chrome/browser/policy/policy_map.h" +#include "chrome/common/pref_names.h" +#include "net/url_request/url_request_context_getter.h" + +namespace chromeos { + +DeviceLocalAccountExternalPolicyLoader:: + DeviceLocalAccountExternalPolicyLoader(policy::CloudPolicyStore* store, + const base::FilePath& cache_dir) + : store_(store), + cache_dir_(cache_dir) { +} + +bool DeviceLocalAccountExternalPolicyLoader::IsCacheRunning() const { + return external_cache_; +} + +void DeviceLocalAccountExternalPolicyLoader::StartCache( + const scoped_refptr<base::SequencedTaskRunner>& cache_task_runner) { + DCHECK(!external_cache_); + store_->AddObserver(this); + external_cache_.reset(new ExternalCache( + cache_dir_, + g_browser_process->system_request_context(), + cache_task_runner, + this, + true /* always_check_updates */, + false /* wait_for_cache_initialization */)); + + if (store_->is_initialized()) + UpdateExtensionListFromStore(); +} + +void DeviceLocalAccountExternalPolicyLoader::StopCache( + const base::Closure& callback) { + if (external_cache_) { + external_cache_->Shutdown(callback); + external_cache_.reset(); + store_->RemoveObserver(this); + } + + base::DictionaryValue empty_prefs; + OnExtensionListsUpdated(&empty_prefs); +} + +void DeviceLocalAccountExternalPolicyLoader::StartLoading() { + if (prefs_) + LoadFinished(); +} + +void DeviceLocalAccountExternalPolicyLoader::OnStoreLoaded( + policy::CloudPolicyStore* store) { + DCHECK(external_cache_); + DCHECK_EQ(store_, store); + UpdateExtensionListFromStore(); +} + +void DeviceLocalAccountExternalPolicyLoader::OnStoreError( + policy::CloudPolicyStore* store) { + DCHECK(external_cache_); + DCHECK_EQ(store_, store); +} + +void DeviceLocalAccountExternalPolicyLoader::OnExtensionListsUpdated( + const base::DictionaryValue* prefs) { + DCHECK(external_cache_ || prefs->empty()); + prefs_.reset(prefs->DeepCopy()); + LoadFinished(); +} + +DeviceLocalAccountExternalPolicyLoader:: + ~DeviceLocalAccountExternalPolicyLoader() { + DCHECK(!external_cache_); +} + +void DeviceLocalAccountExternalPolicyLoader::UpdateExtensionListFromStore() { + scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue); + const policy::PolicyMap& policy_map = store_->policy_map(); + extensions::ExtensionInstallForcelistPolicyHandler policy_handler; + if (policy_handler.CheckPolicySettings(policy_map, NULL)) { + PrefValueMap pref_value_map; + policy_handler.ApplyPolicySettings(policy_map, &pref_value_map); + const base::Value* value = NULL; + const base::DictionaryValue* dict = NULL; + if (pref_value_map.GetValue(prefs::kExtensionInstallForceList, &value) && + value->GetAsDictionary(&dict)) { + prefs.reset(dict->DeepCopy()); + } + } + + external_cache_->UpdateExtensionsList(prefs.Pass()); +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.h b/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.h new file mode 100644 index 0000000000..c9abf5d0d8 --- /dev/null +++ b/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.h @@ -0,0 +1,78 @@ +// Copyright 2013 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_CHROMEOS_EXTENSIONS_DEVICE_LOCAL_ACCOUNT_EXTERNAL_POLICY_LOADER_H_ +#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_DEVICE_LOCAL_ACCOUNT_EXTERNAL_POLICY_LOADER_H_ + +#include "base/basictypes.h" +#include "base/callback_forward.h" +#include "base/compiler_specific.h" +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/sequenced_task_runner.h" +#include "chrome/browser/chromeos/extensions/external_cache.h" +#include "chrome/browser/extensions/external_loader.h" +#include "chrome/browser/policy/cloud/cloud_policy_store.h" + +namespace chromeos { + +// A specialization of the ExternalLoader that serves external extensions from +// the enterprise policy force-install list. This class is used for device-local +// accounts in place of the ExternalPolicyLoader. The difference is that while +// the ExternalPolicyLoader requires extensions to be downloaded on-the-fly, +// this class caches them, allowing for offline installation. +class DeviceLocalAccountExternalPolicyLoader + : public extensions::ExternalLoader, + public policy::CloudPolicyStore::Observer, + public ExternalCache::Delegate { + public: + // The list of force-installed extensions will be read from |store| and the + // extensions will be cached in the |cache_dir_|. + DeviceLocalAccountExternalPolicyLoader(policy::CloudPolicyStore* store, + const base::FilePath& cache_dir); + + // While running, the cache requires exclusive write access to the + // |cache_dir_|. + bool IsCacheRunning() const; + + // Start the cache. This method must only be invoked when there are no pending + // write operations to |cache_dir_| on any thread and none will be initiated + // while the cache is running. + void StartCache( + const scoped_refptr<base::SequencedTaskRunner>& cache_task_runner); + + // Stop the cache. The |callback| will be invoked when the cache has shut down + // completely and write access to the |cache_dir_| is permitted again. + void StopCache(const base::Closure& callback); + + // extensions::ExternalLoader: + virtual void StartLoading() OVERRIDE; + + // policy::CloudPolicyStore::Observer: + virtual void OnStoreLoaded(policy::CloudPolicyStore* store) OVERRIDE; + virtual void OnStoreError(policy::CloudPolicyStore* store) OVERRIDE; + + // ExternalCache::Delegate: + virtual void OnExtensionListsUpdated( + const base::DictionaryValue* prefs) OVERRIDE; + + private: + // If the cache was started, it must be stopped before |this| is destroyed. + virtual ~DeviceLocalAccountExternalPolicyLoader(); + + // Pass the current list of force-installed extensions from the |store_| to + // the |external_cache_|. + void UpdateExtensionListFromStore(); + + policy::CloudPolicyStore* store_; + const base::FilePath cache_dir_; + scoped_ptr<ExternalCache> external_cache_; + + DISALLOW_COPY_AND_ASSIGN(DeviceLocalAccountExternalPolicyLoader); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_EXTENSIONS_DEVICE_LOCAL_ACCOUNT_EXTERNAL_POLICY_LOADER_H_ diff --git a/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader_unittest.cc b/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader_unittest.cc new file mode 100644 index 0000000000..27a70cd711 --- /dev/null +++ b/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader_unittest.cc @@ -0,0 +1,309 @@ +// Copyright 2013 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/chromeos/extensions/device_local_account_external_policy_loader.h" + +#include <string> + +#include "base/callback.h" +#include "base/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/message_loop/message_loop.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/path_service.h" +#include "base/run_loop.h" +#include "base/strings/stringprintf.h" +#include "base/values.h" +#include "base/version.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/extensions/external_provider_impl.h" +#include "chrome/browser/extensions/external_provider_interface.h" +#include "chrome/browser/extensions/updater/extension_downloader.h" +#include "chrome/browser/policy/cloud/mock_cloud_policy_store.h" +#include "chrome/browser/policy/policy_map.h" +#include "chrome/browser/policy/policy_types.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_constants.h" +#include "chrome/test/base/testing_browser_process.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/notification_source.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "content/public/test/test_utils.h" +#include "extensions/common/manifest.h" +#include "net/url_request/test_url_fetcher_factory.h" +#include "net/url_request/url_fetcher_delegate.h" +#include "net/url_request/url_request_context_getter.h" +#include "net/url_request/url_request_test_util.h" +#include "policy/policy_constants.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +using ::testing::InvokeWithoutArgs; +using ::testing::Mock; +using ::testing::_; + +namespace chromeos { + +namespace { + +const char kCacheDir[] = "cache"; +const char kExtensionId[] = "ldnnhddmnhbkjipkidpdiheffobcpfmf"; +const char kExtensionUpdateManifest[] = + "extensions/good_v1_update_manifest.xml"; +const char kExtensionCRXSourceDir[] = "extensions"; +const char kExtensionCRXFile[] = "good.crx"; +const char kExtensionCRXVersion[] = "1.0.0.0"; + +class MockExternalPolicyProviderVisitor + : public extensions::ExternalProviderInterface::VisitorInterface { + public: + MockExternalPolicyProviderVisitor(); + virtual ~MockExternalPolicyProviderVisitor(); + + MOCK_METHOD6(OnExternalExtensionFileFound, + bool(const std::string&, + const base::Version*, + const base::FilePath&, + extensions::Manifest::Location, + int, + bool)); + MOCK_METHOD5(OnExternalExtensionUpdateUrlFound, + bool(const std::string&, + const GURL&, + extensions::Manifest::Location, + int, + bool)); + MOCK_METHOD1(OnExternalProviderReady, + void(const extensions::ExternalProviderInterface* provider)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockExternalPolicyProviderVisitor); +}; + +MockExternalPolicyProviderVisitor::MockExternalPolicyProviderVisitor() { +} + +MockExternalPolicyProviderVisitor::~MockExternalPolicyProviderVisitor() { +} + +} // namespace + +class DeviceLocalAccountExternalPolicyLoaderTest : public testing::Test { + protected: + DeviceLocalAccountExternalPolicyLoaderTest(); + virtual ~DeviceLocalAccountExternalPolicyLoaderTest(); + + virtual void SetUp() OVERRIDE; + virtual void TearDown() OVERRIDE; + + void VerifyAndResetVisitorCallExpectations(); + void SetForceInstallListPolicy(); + + content::TestBrowserThreadBundle thread_bundle_; + base::ScopedTempDir temp_dir_; + base::FilePath cache_dir_; + policy::MockCloudPolicyStore store_; + scoped_refptr<net::URLRequestContextGetter> request_context_getter_; + base::FilePath test_dir_; + + scoped_refptr<DeviceLocalAccountExternalPolicyLoader> loader_; + MockExternalPolicyProviderVisitor visitor_; + scoped_ptr<extensions::ExternalProviderImpl> provider_; +}; + +DeviceLocalAccountExternalPolicyLoaderTest:: + DeviceLocalAccountExternalPolicyLoaderTest() + : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) { +} + +DeviceLocalAccountExternalPolicyLoaderTest:: + ~DeviceLocalAccountExternalPolicyLoaderTest() { +} + +void DeviceLocalAccountExternalPolicyLoaderTest::SetUp() { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + cache_dir_ = temp_dir_.path().Append(kCacheDir); + ASSERT_TRUE(file_util::CreateDirectoryAndGetError(cache_dir_, NULL)); + request_context_getter_ = + new net::TestURLRequestContextGetter(base::MessageLoopProxy::current()); + TestingBrowserProcess::GetGlobal()->SetSystemRequestContext( + request_context_getter_.get()); + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir_)); + + loader_ = new DeviceLocalAccountExternalPolicyLoader(&store_, cache_dir_); + provider_.reset(new extensions::ExternalProviderImpl( + &visitor_, + loader_, + NULL, + extensions::Manifest::EXTERNAL_POLICY, + extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD, + extensions::Extension::NO_FLAGS)); + + content::RenderProcessHost::SetRunRendererInProcess(true); + + VerifyAndResetVisitorCallExpectations(); +} + +void DeviceLocalAccountExternalPolicyLoaderTest::TearDown() { + content::RenderProcessHost::SetRunRendererInProcess(false); + TestingBrowserProcess::GetGlobal()->SetSystemRequestContext(NULL); +} + +void DeviceLocalAccountExternalPolicyLoaderTest:: + VerifyAndResetVisitorCallExpectations() { + Mock::VerifyAndClearExpectations(&visitor_); + EXPECT_CALL(visitor_, OnExternalExtensionFileFound(_, _, _, _, _, _)) + .Times(0); + EXPECT_CALL(visitor_, OnExternalExtensionUpdateUrlFound(_, _, _, _, _)) + .Times(0); + EXPECT_CALL(visitor_, OnExternalProviderReady(_)) + .Times(0); +} + +void DeviceLocalAccountExternalPolicyLoaderTest::SetForceInstallListPolicy() { + scoped_ptr<base::ListValue> forcelist(new base::ListValue); + forcelist->AppendString("invalid"); + forcelist->AppendString(base::StringPrintf( + "%s;%s", + kExtensionId, + extension_urls::GetWebstoreUpdateUrl().spec().c_str())); + store_.policy_map_.Set(policy::key::kExtensionInstallForcelist, + policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, + forcelist.release(), + NULL); + store_.NotifyStoreLoaded(); +} + +// Verifies that when the cache is not explicitly started, the loader does not +// serve any extensions, even if the force-install list policy is set or a load +// is manually requested. +TEST_F(DeviceLocalAccountExternalPolicyLoaderTest, CacheNotStarted) { + // Set the force-install list policy. + SetForceInstallListPolicy(); + + // Manually request a load. + loader_->StartLoading(); + + EXPECT_FALSE(loader_->IsCacheRunning()); + EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting()); +} + +// Verifies that the cache can be started and stopped correctly. +TEST_F(DeviceLocalAccountExternalPolicyLoaderTest, ForceInstallListEmpty) { + // Set an empty force-install list policy. + store_.NotifyStoreLoaded(); + + // Start the cache. Verify that the loader announces an empty extension list. + EXPECT_CALL(visitor_, OnExternalProviderReady(provider_.get())) + .Times(1); + loader_->StartCache(base::MessageLoopProxy::current()); + VerifyAndResetVisitorCallExpectations(); + + // Stop the cache. Verify that the loader announces an empty extension list. + EXPECT_CALL(visitor_, OnExternalProviderReady(provider_.get())) + .Times(1); + base::RunLoop run_loop; + loader_->StopCache(run_loop.QuitClosure()); + VerifyAndResetVisitorCallExpectations(); + + // Spin the loop until the cache shutdown callback is invoked. Verify that at + // that point, no further file I/O tasks are pending. + run_loop.Run(); + EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting()); +} + +// Verifies that when a force-install list policy referencing an extension is +// set and the cache is started, the loader downloads, caches and serves the +// extension. +TEST_F(DeviceLocalAccountExternalPolicyLoaderTest, ForceInstallListSet) { + // Set a force-install list policy that contains an invalid entry (which + // should be ignored) and a valid reference to an extension. + SetForceInstallListPolicy(); + + // Start the cache. + loader_->StartCache(base::MessageLoopProxy::current()); + + // Spin the loop, allowing the loader to process the force-install list. + // Verify that the loader announces an empty extension list. + net::TestURLFetcherFactory factory; + EXPECT_CALL(visitor_, OnExternalProviderReady(provider_.get())) + .Times(1); + base::MessageLoop::current()->RunUntilIdle(); + + // Verify that a downloader has started and is attempting to download an + // update manifest. + net::TestURLFetcher* fetcher = factory.GetFetcherByID( + extensions::ExtensionDownloader::kManifestFetcherId); + ASSERT_TRUE(fetcher); + ASSERT_TRUE(fetcher->delegate()); + + // Return a manifest to the downloader. + std::string manifest; + EXPECT_TRUE(base::ReadFileToString(test_dir_.Append(kExtensionUpdateManifest), + &manifest)); + fetcher->set_response_code(200); + fetcher->SetResponseString(manifest); + fetcher->delegate()->OnURLFetchComplete(fetcher); + + // Wait for the manifest to be parsed. + content::WindowedNotificationObserver( + chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND, + content::NotificationService::AllSources()).Wait(); + + // Verify that the downloader is attempting to download a CRX file. + fetcher = factory.GetFetcherByID( + extensions::ExtensionDownloader::kExtensionFetcherId); + ASSERT_TRUE(fetcher); + ASSERT_TRUE(fetcher->delegate()); + + // Create a temporary CRX file and return its path to the downloader. + EXPECT_TRUE(base::CopyFile( + test_dir_.Append(kExtensionCRXSourceDir).Append(kExtensionCRXFile), + temp_dir_.path().Append(kExtensionCRXFile))); + fetcher->set_response_code(200); + fetcher->SetResponseFilePath(temp_dir_.path().Append(kExtensionCRXFile)); + fetcher->delegate()->OnURLFetchComplete(fetcher); + + // Spin the loop. Verify that the loader announces the presence of a new CRX + // file, served from the cache directory. + const base::FilePath cached_crx_path = cache_dir_.Append(base::StringPrintf( + "%s-%s.crx", kExtensionId, kExtensionCRXVersion)); + base::RunLoop cache_run_loop; + EXPECT_CALL(visitor_, OnExternalExtensionFileFound( + kExtensionId, + _, + cached_crx_path, + extensions::Manifest::EXTERNAL_POLICY, + _, + _)); + EXPECT_CALL(visitor_, OnExternalProviderReady(provider_.get())) + .Times(1) + .WillOnce(InvokeWithoutArgs(&cache_run_loop, &base::RunLoop::Quit)); + cache_run_loop.Run(); + VerifyAndResetVisitorCallExpectations(); + + // Verify that the CRX file actually exists in the cache directory and its + // contents matches the file returned to the downloader. + EXPECT_TRUE(base::ContentsEqual( + test_dir_.Append(kExtensionCRXSourceDir).Append(kExtensionCRXFile), + cached_crx_path)); + + // Stop the cache. Verify that the loader announces an empty extension list. + EXPECT_CALL(visitor_, OnExternalProviderReady(provider_.get())) + .Times(1); + base::RunLoop shutdown_run_loop; + loader_->StopCache(shutdown_run_loop.QuitClosure()); + VerifyAndResetVisitorCallExpectations(); + + // Spin the loop until the cache shutdown callback is invoked. Verify that at + // that point, no further file I/O tasks are pending. + shutdown_run_loop.Run(); + EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting()); +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/extensions/external_pref_cache_loader.h b/chrome/browser/chromeos/extensions/external_pref_cache_loader.h index 16a08ff050..e448182a07 100644 --- a/chrome/browser/chromeos/extensions/external_pref_cache_loader.h +++ b/chrome/browser/chromeos/extensions/external_pref_cache_loader.h @@ -9,9 +9,8 @@ namespace chromeos { -// A specialization of the ExternalPrefCacheLoader that caches crx files for -// external extensions with update URL in common place for all users on the -// machine. +// A specialization of the ExternalPrefLoader that caches crx files for external +// extensions with update URL in a common place for all users on the machine. class ExternalPrefCacheLoader : public extensions::ExternalPrefLoader { public: // All instances of ExternalPrefCacheLoader use the same cache so diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc index 443ccf0ac1..21a775939a 100644 --- a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc +++ b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc @@ -116,7 +116,6 @@ bool FileBrowserPrivateGetStringsFunction::RunImpl() { SET_STRING("ACTION_OPEN_GSLIDES", IDS_FILE_BROWSER_ACTION_OPEN_GSLIDES); SET_STRING("ACTION_WATCH", IDS_FILE_BROWSER_ACTION_WATCH); SET_STRING("ACTION_LISTEN", IDS_FILE_BROWSER_ACTION_LISTEN); - SET_STRING("INSTALL_CRX", IDS_FILE_BROWSER_INSTALL_CRX); SET_STRING("SEND_TO_DRIVE", IDS_FILE_BROWSER_SEND_TO_DRIVE); SET_STRING("GALLERY_NO_IMAGES", IDS_FILE_BROWSER_GALLERY_NO_IMAGES); @@ -239,6 +238,17 @@ bool FileBrowserPrivateGetStringsFunction::RunImpl() { SET_STRING("PHOTO_IMPORT_MY_PHOTOS_DIRECTORY_NAME", IDS_FILE_BROWSER_PHOTO_IMPORT_MY_PHOTOS_DIRECTORY_NAME); + SET_STRING("CONFLICT_DIALOG_TITLE", + IDS_FILE_BROWSER_CONFLICT_DIALOG_TITLE); + SET_STRING("CONFLICT_DIALOG_MESSAGE", + IDS_FILE_BROWSER_CONFLICT_DIALOG_MESSAGE); + SET_STRING("CONFLICT_DIALOG_KEEP_BOTH", + IDS_FILE_BROWSER_CONFLICT_DIALOG_KEEP_BOTH); + SET_STRING("CONFLICT_DIALOG_REPLACE", + IDS_FILE_BROWSER_CONFLICT_DIALOG_REPLACE); + SET_STRING("CONFLICT_DIALOG_APPLY_TO_ALL", + IDS_FILE_BROWSER_CONFLICT_DIALOG_APPLY_TO_ALL); + SET_STRING("CONFIRM_OVERWRITE_FILE", IDS_FILE_BROWSER_CONFIRM_OVERWRITE_FILE); SET_STRING("FILE_ALREADY_EXISTS", IDS_FILE_BROWSER_FILE_ALREADY_EXISTS); SET_STRING("DIRECTORY_ALREADY_EXISTS", @@ -452,6 +462,8 @@ bool FileBrowserPrivateGetStringsFunction::RunImpl() { IDS_FILE_BROWSER_GDRAW_DOCUMENT_FILE_TYPE); SET_STRING("GTABLE_DOCUMENT_FILE_TYPE", IDS_FILE_BROWSER_GTABLE_DOCUMENT_FILE_TYPE); + SET_STRING("GFORM_DOCUMENT_FILE_TYPE", + IDS_FILE_BROWSER_GFORM_DOCUMENT_FILE_TYPE); SET_STRING("GLINK_DOCUMENT_FILE_TYPE", IDS_FILE_BROWSER_GLINK_DOCUMENT_FILE_TYPE); diff --git a/chrome/browser/chromeos/extensions/wallpaper_private_api.cc b/chrome/browser/chromeos/extensions/wallpaper_private_api.cc index 1fcd5d4821..b89dedfc7c 100644 --- a/chrome/browser/chromeos/extensions/wallpaper_private_api.cc +++ b/chrome/browser/chromeos/extensions/wallpaper_private_api.cc @@ -45,7 +45,6 @@ namespace get_thumbnail = wallpaper_private::GetThumbnail; namespace save_thumbnail = wallpaper_private::SaveThumbnail; namespace get_offline_wallpaper_list = wallpaper_private::GetOfflineWallpaperList; -typedef extensions::api::wallpaper_private::WallpaperSource WallpaperSource; namespace { @@ -227,31 +226,16 @@ bool WallpaperPrivateSetWallpaperIfExistsFunction::RunImpl() { ash::WallpaperResolution resolution = ash::Shell::GetInstance()-> desktop_background_controller()->GetAppropriateResolution(); - if (params->source == wallpaper_private::WALLPAPER_SOURCE_ONLINE) { - type_ = chromeos::User::ONLINE; - std::string file_name = GURL(params->url).ExtractFileName(); - CHECK(PathService::Get(chrome::DIR_CHROMEOS_WALLPAPERS, - &wallpaper_path)); - fallback_path = wallpaper_path.Append(file_name); - if (params->layout != wallpaper_private::WALLPAPER_LAYOUT_STRETCH && - resolution == ash::WALLPAPER_RESOLUTION_SMALL) { - file_name = base::FilePath(file_name).InsertBeforeExtension( - chromeos::kSmallWallpaperSuffix).value(); - } - wallpaper_path = wallpaper_path.Append(file_name); - } else { - type_ = chromeos::User::CUSTOMIZED; - std::string file_name = params->url; - std::string username_hash = - chromeos::UserManager::Get()->GetLoggedInUser()->username_hash(); - const char* sub_dir = (resolution == ash::WALLPAPER_RESOLUTION_SMALL) ? - chromeos::kSmallWallpaperSubDir : chromeos::kLargeWallpaperSubDir; - wallpaper_path = chromeos::WallpaperManager::Get()->GetCustomWallpaperPath( - sub_dir, username_hash, file_name); - - fallback_path = chromeos::WallpaperManager::Get()->GetCustomWallpaperPath( - chromeos::kOriginalWallpaperSubDir, username_hash, file_name); + std::string file_name = GURL(params->url).ExtractFileName(); + CHECK(PathService::Get(chrome::DIR_CHROMEOS_WALLPAPERS, + &wallpaper_path)); + fallback_path = wallpaper_path.Append(file_name); + if (params->layout != wallpaper_private::WALLPAPER_LAYOUT_STRETCH && + resolution == ash::WALLPAPER_RESOLUTION_SMALL) { + file_name = base::FilePath(file_name).InsertBeforeExtension( + chromeos::kSmallWallpaperSuffix).value(); } + wallpaper_path = wallpaper_path.Append(file_name); sequence_token_ = BrowserThread::GetBlockingPool()-> GetNamedSequenceToken(chromeos::kWallpaperSequenceTokenName); @@ -311,7 +295,7 @@ void WallpaperPrivateSetWallpaperIfExistsFunction::OnWallpaperDecoded( chromeos::WallpaperInfo info = { params->url, layout, - type_, + chromeos::User::ONLINE, base::Time::Now().LocalMidnight() }; std::string email = chromeos::UserManager::Get()->GetLoggedInUser()->email(); @@ -613,7 +597,7 @@ bool WallpaperPrivateGetThumbnailFunction::RunImpl() { base::FilePath thumbnail_path; std::string email = chromeos::UserManager::Get()->GetLoggedInUser()->email(); - if (params->source == wallpaper_private::WALLPAPER_SOURCE_ONLINE) { + if (params->source == get_thumbnail::Params::SOURCE_ONLINE) { std::string file_name = GURL(params->url_or_file).ExtractFileName(); CHECK(PathService::Get(chrome::DIR_CHROMEOS_WALLPAPER_THUMBNAILS, &thumbnail_path)); @@ -743,12 +727,6 @@ WallpaperPrivateGetOfflineWallpaperListFunction:: } bool WallpaperPrivateGetOfflineWallpaperListFunction::RunImpl() { - scoped_ptr<get_offline_wallpaper_list::Params> params( - get_offline_wallpaper_list::Params::Create(*args_)); - EXTENSION_FUNCTION_VALIDATE(params); - - std::string email = chromeos::UserManager::Get()->GetLoggedInUser()->email(); - sequence_token_ = BrowserThread::GetBlockingPool()-> GetNamedSequenceToken(chromeos::kWallpaperSequenceTokenName); scoped_refptr<base::SequencedTaskRunner> task_runner = @@ -758,31 +736,25 @@ bool WallpaperPrivateGetOfflineWallpaperListFunction::RunImpl() { task_runner->PostTask(FROM_HERE, base::Bind(&WallpaperPrivateGetOfflineWallpaperListFunction::GetList, - this, email, params->source)); + this)); return true; } -void WallpaperPrivateGetOfflineWallpaperListFunction::GetList( - const std::string& email, - WallpaperSource source) { +void WallpaperPrivateGetOfflineWallpaperListFunction::GetList() { DCHECK(BrowserThread::GetBlockingPool()->IsRunningSequenceOnCurrentThread( sequence_token_)); std::vector<std::string> file_list; - // TODO(bshe): This api function is only used for ONLINE wallpapers. Remove - // source. - if (source == wallpaper_private::WALLPAPER_SOURCE_ONLINE) { - base::FilePath wallpaper_dir; - CHECK(PathService::Get(chrome::DIR_CHROMEOS_WALLPAPERS, &wallpaper_dir)); - if (base::DirectoryExists(wallpaper_dir)) { - base::FileEnumerator files(wallpaper_dir, false, - base::FileEnumerator::FILES); - for (base::FilePath current = files.Next(); !current.empty(); - current = files.Next()) { - std::string file_name = current.BaseName().RemoveExtension().value(); - // Do not add file name of small resolution wallpaper to the list. - if (!EndsWith(file_name, chromeos::kSmallWallpaperSuffix, true)) - file_list.push_back(current.BaseName().value()); - } + base::FilePath wallpaper_dir; + CHECK(PathService::Get(chrome::DIR_CHROMEOS_WALLPAPERS, &wallpaper_dir)); + if (base::DirectoryExists(wallpaper_dir)) { + base::FileEnumerator files(wallpaper_dir, false, + base::FileEnumerator::FILES); + for (base::FilePath current = files.Next(); !current.empty(); + current = files.Next()) { + std::string file_name = current.BaseName().RemoveExtension().value(); + // Do not add file name of small resolution wallpaper to the list. + if (!EndsWith(file_name, chromeos::kSmallWallpaperSuffix, true)) + file_list.push_back(current.BaseName().value()); } } BrowserThread::PostTask( diff --git a/chrome/browser/chromeos/extensions/wallpaper_private_api.h b/chrome/browser/chromeos/extensions/wallpaper_private_api.h index f73c8fa612..91ba2dac93 100644 --- a/chrome/browser/chromeos/extensions/wallpaper_private_api.h +++ b/chrome/browser/chromeos/extensions/wallpaper_private_api.h @@ -7,7 +7,6 @@ #include "base/threading/sequenced_worker_pool.h" #include "chrome/browser/chromeos/extensions/wallpaper_function_base.h" -#include "chrome/browser/chromeos/login/user.h" #include "chrome/common/extensions/api/wallpaper_private.h" #include "net/url_request/url_fetcher_delegate.h" @@ -56,9 +55,6 @@ class WallpaperPrivateSetWallpaperIfExistsFunction scoped_ptr<extensions::api::wallpaper_private::SetWallpaperIfExists::Params> params; - // Type of the loaded wallpaper. - chromeos::User::WallpaperType type_; - // Sequence token associated with wallpaper operations. Shared with // WallpaperManager. base::SequencedWorkerPool::SequenceToken sequence_token_; @@ -274,9 +270,8 @@ class WallpaperPrivateGetOfflineWallpaperListFunction virtual bool RunImpl() OVERRIDE; private: - // Enumerates the list of files in wallpaper directory of given |source|. - void GetList(const std::string& email, - extensions::api::wallpaper_private::WallpaperSource source); + // Enumerates the list of files in online wallpaper directory. + void GetList(); // Sends the list of files to extension api caller. If no files or no // directory, sends empty list. diff --git a/chrome/browser/chromeos/file_manager/file_browser_handlers.cc b/chrome/browser/chromeos/file_manager/file_browser_handlers.cc index 1af7759226..54ea3af837 100644 --- a/chrome/browser/chromeos/file_manager/file_browser_handlers.cc +++ b/chrome/browser/chromeos/file_manager/file_browser_handlers.cc @@ -17,6 +17,7 @@ #include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/lazy_background_task_queue.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser_finder.h" @@ -105,7 +106,7 @@ FileBrowserHandlerList FindFileBrowserHandlersForURL( ++iter) { const Extension* extension = iter->get(); if (profile->IsOffTheRecord() && - !service->IsIncognitoEnabled(extension->id())) + !extension_util::IsIncognitoEnabled(extension->id(), service)) continue; FileBrowserHandler::List* handler_list = @@ -445,7 +446,6 @@ bool ShouldBeOpenedWithBrowser(const std::string& extension_id, (action_id == "view-pdf" || action_id == "view-swf" || action_id == "view-in-browser" || - action_id == "install-crx" || action_id == "open-hosted-generic" || action_id == "open-hosted-gdoc" || action_id == "open-hosted-gsheet" || diff --git a/chrome/browser/chromeos/file_manager/file_tasks.cc b/chrome/browser/chromeos/file_manager/file_tasks.cc index 4bb9e0a929..6468508ccb 100644 --- a/chrome/browser/chromeos/file_manager/file_tasks.cc +++ b/chrome/browser/chromeos/file_manager/file_tasks.cc @@ -11,6 +11,7 @@ #include "chrome/browser/chromeos/drive/drive_app_registry.h" #include "chrome/browser/chromeos/drive/file_system_util.h" #include "chrome/browser/chromeos/drive/file_task_executor.h" +#include "chrome/browser/chromeos/file_manager/app_id.h" #include "chrome/browser/chromeos/file_manager/file_browser_handlers.h" #include "chrome/browser/chromeos/file_manager/fileapi_util.h" #include "chrome/browser/chromeos/file_manager/open_util.h" @@ -21,6 +22,7 @@ #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/extensions/extension_tab_util.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/webui/extensions/extension_icon_source.h" #include "chrome/common/extensions/api/file_browser_handlers/file_browser_handler.h" @@ -50,9 +52,6 @@ const char kDriveAppTaskType[] = "drive"; // Drive apps always use the action ID. const char kDriveAppActionID[] = "open-with"; -// Default icon path for drive docs. -const char kDefaultIcon[] = "images/filetype_generic.png"; - // Converts a TaskType to a string. std::string TaskTypeToString(TaskType task_type) { switch (task_type) { @@ -112,6 +111,29 @@ bool FileBrowserHasAccessPermissionForFiles( return true; } +// Returns true if path_mime_set contains a Google document. +bool ContainsGoogleDocument(const PathAndMimeTypeSet& path_mime_set) { + for (PathAndMimeTypeSet::const_iterator iter = path_mime_set.begin(); + iter != path_mime_set.end(); ++iter) { + if (google_apis::ResourceEntry::ClassifyEntryKindByFileExtension( + iter->first) & + google_apis::ResourceEntry::KIND_OF_GOOGLE_DOCUMENT) { + return true; + } + } + return false; +} + +// Leaves tasks handled by the file manger itself as is and removes all others. +void KeepOnlyFileManagerInternalTasks(std::vector<FullTaskDescriptor>* tasks) { + std::vector<FullTaskDescriptor> filtered; + for (size_t i = 0; i < tasks->size(); ++i) { + if ((*tasks)[i].task_descriptor().app_id == kFileManagerAppId) + filtered.push_back((*tasks)[i]); + } + tasks->swap(filtered); +} + } // namespace FullTaskDescriptor::FullTaskDescriptor( @@ -298,17 +320,6 @@ void FindDriveAppTasks( std::vector<FullTaskDescriptor>* result_list) { DCHECK(result_list); - // Check if path_mime_set contains a google document. Return immediately if - // it's found. - for (PathAndMimeTypeSet::const_iterator iter = path_mime_set.begin(); - iter != path_mime_set.end(); ++iter) { - if (google_apis::ResourceEntry::ClassifyEntryKindByFileExtension( - iter->first) & - google_apis::ResourceEntry::KIND_OF_GOOGLE_DOCUMENT) { - return; - } - } - bool is_first = true; typedef std::map<std::string, drive::DriveAppInfo> DriveAppInfoMap; DriveAppInfoMap drive_app_map; @@ -390,7 +401,7 @@ void FindFileHandlerTasks( continue; if (profile->IsOffTheRecord() && - !service->IsIncognitoEnabled(extension->id())) + !extension_util::IsIncognitoEnabled(extension->id(), service)) continue; typedef std::vector<const extensions::FileHandlerInfo*> FileHandlerList; @@ -480,20 +491,18 @@ void FindAllTypesOfTasks( // Find and append file handler tasks. We know there aren't duplicates // because Drive apps and platform apps are entirely different kinds of // tasks. - FindFileHandlerTasks(profile, - path_mime_set, - result_list); + FindFileHandlerTasks(profile, path_mime_set, result_list); // Find and append file browser handler tasks. We know there aren't // duplicates because "file_browser_handlers" and "file_handlers" shouldn't // be used in the same manifest.json. - FindFileBrowserHandlerTasks(profile, - file_urls, - result_list); + FindFileBrowserHandlerTasks(profile, file_urls, result_list); + + // Google documents can only be handled by internal handlers. + if (ContainsGoogleDocument(path_mime_set)) + KeepOnlyFileManagerInternalTasks(result_list); - ChooseAndSetDefaultTask(*profile->GetPrefs(), - path_mime_set, - result_list); + ChooseAndSetDefaultTask(*profile->GetPrefs(), path_mime_set, result_list); } void ChooseAndSetDefaultTask(const PrefService& pref_service, diff --git a/chrome/browser/chromeos/file_manager/file_tasks.h b/chrome/browser/chromeos/file_manager/file_tasks.h index 23e69c3c31..8e2e777b61 100644 --- a/chrome/browser/chromeos/file_manager/file_tasks.h +++ b/chrome/browser/chromeos/file_manager/file_tasks.h @@ -258,10 +258,6 @@ typedef extensions::app_file_handler_util::PathAndMimeTypeSet // Finds the Drive app tasks that can be used with the given |path_mime_set| // from |drive_app_registry|, and append them to the |result_list|. // Drive app tasks will be found only if all of the files are on Drive. -// -// If |path_mime_set| contains a Google document, no Drive app tasks are -// returned. This is to avoid dups since Files.app already provides an -// internal handler for Google documents. void FindDriveAppTasks(const drive::DriveAppRegistry& drive_app_registry, const PathAndMimeTypeSet& path_mime_set, std::vector<FullTaskDescriptor>* result_list); @@ -286,6 +282,12 @@ void FindFileBrowserHandlerTasks( // Drive app tasks will be found only if all of the files are on Drive. // |drive_app_registry| can be NULL if the drive app registry is not // present. +// +// If |path_mime_set| contains a Google document, only the internal tasks of +// Files.app (i.e., tasks having the app ID of Files.app) are listed. +// This is to avoid dups between Drive app tasks and an internal handler that +// Files.app provides, and to avoid listing normal file handler and file browser +// handler tasks, which can handle only normal files. void FindAllTypesOfTasks( Profile* profile, const drive::DriveAppRegistry* drive_app_registry, diff --git a/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc b/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc index 598ff0a103..0d2612ebe1 100644 --- a/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc +++ b/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc @@ -253,41 +253,6 @@ TEST(FileManagerFileTasksTest, FindDriveAppTasks) { ASSERT_TRUE(tasks.empty()); } -TEST(FileManagerFileTasksTest, FindDriveAppTasks_GoogleDocument) { - // For DriveAppRegistry, which checks CurrentlyOn(BrowserThread::UI). - content::TestBrowserThreadBundle thread_bundle; - - // Foo.app can handle ".gdoc" files. - scoped_ptr<google_apis::AppResource> foo_app(new google_apis::AppResource); - foo_app->set_product_url( - GURL("https://chrome.google.com/webstore/detail/foo_app_id")); - foo_app->set_application_id("foo_app_id"); - foo_app->set_name("Foo"); - foo_app->set_object_type("foo_object_type"); - ScopedVector<std::string> foo_extensions; - foo_extensions.push_back(new std::string("gdoc")); // Not ".gdoc" - foo_app->set_primary_file_extensions(foo_extensions.Pass()); - - // Prepare DriveAppRegistry from Foo.app. - ScopedVector<google_apis::AppResource> app_resources; - app_resources.push_back(foo_app.release()); - google_apis::AppList app_list; - app_list.set_items(app_resources.Pass()); - drive::DriveAppRegistry drive_app_registry(NULL); - drive_app_registry.UpdateFromAppList(app_list); - - // Find apps for a ".gdoc file". Foo.app can handle it but should not be - // found, as it should be rejected. - PathAndMimeTypeSet path_mime_set; - path_mime_set.insert( - std::make_pair( - drive::util::GetDriveMountPointPath().AppendASCII("foo.gdoc"), - "application/vnd.google-apps.document")); - std::vector<FullTaskDescriptor> tasks; - FindDriveAppTasks(drive_app_registry, path_mime_set, &tasks); - EXPECT_TRUE(tasks.empty()); -} - // Test that the right task is chosen from multiple choices per mime types // and file extensions. TEST(FileManagerFileTasksTest, ChooseAndSetDefaultTask_MultipleTasks) { @@ -686,5 +651,86 @@ TEST_F(FileManagerFileTasksComplexTest, FindAllTypesOfTasks) { EXPECT_EQ(kBazId, app_ids[2]); } +TEST_F(FileManagerFileTasksComplexTest, FindAllTypesOfTasks_GoogleDocument) { + // kFooId and kBarId copied from FindFileHandlerTasks test above. + const char kFooId[] = "hhgbjpmdppecanaaogonaigmmifgpaph"; + const char kBarId[] = "odlhccgofgkadkkhcmhgnhgahonahoca"; + + // Foo.app can handle ".gdoc" files. + scoped_ptr<google_apis::AppResource> foo_app(new google_apis::AppResource); + foo_app->set_product_url( + GURL("https://chrome.google.com/webstore/detail/foo_app")); + foo_app->set_application_id(kFooId); + foo_app->set_name("Foo"); + foo_app->set_object_type("foo_object_type"); + ScopedVector<std::string> foo_extensions; + foo_extensions.push_back(new std::string("gdoc")); // Not ".gdoc" + foo_app->set_primary_file_extensions(foo_extensions.Pass()); + + // Prepare DriveAppRegistry from Foo.app. + ScopedVector<google_apis::AppResource> app_resources; + app_resources.push_back(foo_app.release()); + google_apis::AppList app_list; + app_list.set_items(app_resources.Pass()); + drive::DriveAppRegistry drive_app_registry(NULL); + drive_app_registry.UpdateFromAppList(app_list); + + // Bar.app can handle ".gdoc" files. + // This is an extension (file browser handler). + extensions::ExtensionBuilder bar_app; + bar_app.SetManifest(extensions::DictionaryBuilder() + .Set("name", "Bar") + .Set("version", "1.0.0") + .Set("manifest_version", 2) + .Set("file_browser_handlers", + extensions::ListBuilder() + .Append(extensions::DictionaryBuilder() + .Set("id", "open") + .Set("default_title", "open") + .Set("file_filters", + extensions::ListBuilder() + .Append("filesystem:*.gdoc"))))); + bar_app.SetID(kBarId); + extension_service_->AddExtension(bar_app.Build().get()); + + // Files.app can handle ".gdoc" files. + // The ID "kFileManagerAppId" used here is precisely the one that identifies + // the Chrome OS Files.app application. + extensions::ExtensionBuilder files_app; + files_app.SetManifest(extensions::DictionaryBuilder() + .Set("name", "Files") + .Set("version", "1.0.0") + .Set("manifest_version", 2) + .Set("file_browser_handlers", + extensions::ListBuilder() + .Append(extensions::DictionaryBuilder() + .Set("id", "open") + .Set("default_title", "open") + .Set("file_filters", + extensions::ListBuilder() + .Append("filesystem:*.gdoc"))))); + files_app.SetID(kFileManagerAppId); + extension_service_->AddExtension(files_app.Build().get()); + + // Find apps for a ".gdoc file". Only the built-in handler of Files.apps + // should be found. + PathAndMimeTypeSet path_mime_set; + std::vector<GURL> file_urls; + path_mime_set.insert( + std::make_pair( + drive::util::GetDriveMountPointPath().AppendASCII("foo.gdoc"), + "application/vnd.google-apps.document")); + file_urls.push_back(GURL("filesystem:chrome-extension://id/dir/foo.gdoc")); + + std::vector<FullTaskDescriptor> tasks; + FindAllTypesOfTasks(&test_profile_, + &drive_app_registry, + path_mime_set, + file_urls, + &tasks); + ASSERT_EQ(1U, tasks.size()); + EXPECT_EQ(kFileManagerAppId, tasks[0].task_descriptor().app_id); +} + } // namespace file_tasks } // namespace file_manager. diff --git a/chrome/browser/chromeos/file_manager/open_with_browser.cc b/chrome/browser/chromeos/file_manager/open_with_browser.cc index fddb999b38..0ee88143a8 100644 --- a/chrome/browser/chromeos/file_manager/open_with_browser.cc +++ b/chrome/browser/chromeos/file_manager/open_with_browser.cc @@ -10,14 +10,8 @@ #include "base/path_service.h" #include "base/threading/sequenced_worker_pool.h" #include "chrome/browser/browser_process.h" -#include "chrome/browser/chromeos/drive/drive.pb.h" -#include "chrome/browser/chromeos/drive/file_system.h" #include "chrome/browser/chromeos/drive/file_system_util.h" #include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h" -#include "chrome/browser/extensions/crx_installer.h" -#include "chrome/browser/extensions/extension_install_prompt.h" -#include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/plugins/plugin_prefs.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" @@ -42,7 +36,6 @@ namespace file_manager { namespace util { namespace { -const base::FilePath::CharType kCRXExtension[] = FILE_PATH_LITERAL(".crx"); const base::FilePath::CharType kPdfExtension[] = FILE_PATH_LITERAL(".pdf"); const base::FilePath::CharType kSwfExtension[] = FILE_PATH_LITERAL(".swf"); @@ -126,36 +119,6 @@ void OpenNewTab(Profile* profile, const GURL& url) { browser->window()->Show(); } -void InstallCRX(Profile* profile, const base::FilePath& file_path) { - DCHECK(profile); - - ExtensionService* service = - extensions::ExtensionSystem::Get(profile)->extension_service(); - CHECK(service); - - scoped_refptr<extensions::CrxInstaller> installer( - extensions::CrxInstaller::Create( - service, - scoped_ptr<ExtensionInstallPrompt>(new ExtensionInstallPrompt( - profile, NULL, NULL)))); - installer->set_error_on_unsupported_requirements(true); - installer->set_is_gallery_install(false); - installer->set_allow_silent_install(false); - installer->InstallCrx(file_path); -} - -// Called when a crx file on Drive was downloaded. -void OnCRXDownloadCallback(Profile* profile, - drive::FileError error, - const base::FilePath& file, - scoped_ptr<drive::ResourceEntry> entry) { - DCHECK(profile); - - if (error != drive::FILE_ERROR_OK) - return; - InstallCRX(profile, file); -} - // Reads the alternate URL from a GDoc file. When it fails, returns a file URL // for |file_path| as fallback. // Note that an alternate url is a URL to open a hosted document. @@ -204,21 +167,6 @@ bool OpenFileWithBrowser(Profile* profile, const base::FilePath& file_path) { return true; } - if (file_path.MatchesExtension(kCRXExtension)) { - if (drive::util::IsUnderDriveMountPoint(file_path)) { - drive::FileSystemInterface* file_system = - drive::util::GetFileSystemByProfile(profile); - if (!file_system) - return false; - file_system->GetFileByPath( - drive::util::ExtractDrivePath(file_path), - base::Bind(&OnCRXDownloadCallback, profile)); - } else { - InstallCRX(profile, file_path); - } - return true; - } - // Failed to open the file of unknown type. LOG(WARNING) << "Unknown file type: " << file_path.value(); return false; diff --git a/chrome/browser/chromeos/file_manager/open_with_browser.h b/chrome/browser/chromeos/file_manager/open_with_browser.h index 6f9395a1c8..acb678a86a 100644 --- a/chrome/browser/chromeos/file_manager/open_with_browser.h +++ b/chrome/browser/chromeos/file_manager/open_with_browser.h @@ -9,7 +9,6 @@ #include "base/files/file_path.h" -class Browser; class Profile; namespace file_manager { @@ -21,7 +20,6 @@ namespace util { // - If there is no active browser window, open it. // - If the file is a Drive hosted document, the hosted document will be // opened in the browser by extracting the right URL for the file. -// - If the file is a CRX file, the CRX file will be installed. // - If the file is on Drive, the file will be downloaded from Drive as // needed. // diff --git a/chrome/browser/chromeos/file_manager/url_util.cc b/chrome/browser/chromeos/file_manager/url_util.cc index fecf759a1e..ff703e83f5 100644 --- a/chrome/browser/chromeos/file_manager/url_util.cc +++ b/chrome/browser/chromeos/file_manager/url_util.cc @@ -113,8 +113,6 @@ GURL GetFileManagerMainPageUrlWithParams( std::string json_args; base::JSONWriter::Write(&arg_value, &json_args); - // kChromeUIFileManagerURL could not be used since query parameters are not - // supported for it. std::string url = GetFileManagerMainPageUrl().spec() + '?' + net::EscapeUrlEncodedData(json_args, false); // Space to %20 instead of +. diff --git a/chrome/browser/chromeos/input_method/input_method_configuration_unittest.cc b/chrome/browser/chromeos/input_method/input_method_configuration_unittest.cc index a5aaae41ba..77bd6ae92a 100644 --- a/chrome/browser/chromeos/input_method/input_method_configuration_unittest.cc +++ b/chrome/browser/chromeos/input_method/input_method_configuration_unittest.cc @@ -5,7 +5,7 @@ #include "chrome/browser/chromeos/input_method/input_method_configuration.h" #include "chrome/browser/chromeos/input_method/mock_input_method_manager.h" #include "chromeos/dbus/dbus_thread_manager.h" -#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" +#include "chromeos/dbus/fake_dbus_thread_manager.h" #include "content/public/browser/browser_thread.h" #include "testing/gtest/include/gtest/gtest.h" @@ -16,7 +16,7 @@ class InputMethodConfigurationTest : public testing::Test { public: virtual void SetUp() { chromeos::DBusThreadManager::InitializeForTesting( - new chromeos::MockDBusThreadManagerWithoutGMock()); + new chromeos::FakeDBusThreadManager()); } virtual void TearDown() { diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc index 7624f75dec..13d39be633 100644 --- a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc +++ b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc @@ -32,8 +32,6 @@ namespace input_method { namespace { -const char nacl_mozc_us_id[] = - "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_us"; const char nacl_mozc_jp_id[] = "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_jp"; diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl_unittest.cc b/chrome/browser/chromeos/input_method/input_method_manager_impl_unittest.cc index cd5c49b310..45f3780b0a 100644 --- a/chrome/browser/chromeos/input_method/input_method_manager_impl_unittest.cc +++ b/chrome/browser/chromeos/input_method/input_method_manager_impl_unittest.cc @@ -15,8 +15,8 @@ #include "base/message_loop/message_loop.h" #include "chrome/browser/chromeos/input_method/mock_candidate_window_controller.h" #include "chrome/browser/chromeos/input_method/mock_ibus_controller.h" +#include "chromeos/dbus/fake_dbus_thread_manager.h" #include "chromeos/dbus/ibus/mock_ibus_client.h" -#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" #include "chromeos/ime/extension_ime_util.h" #include "chromeos/ime/fake_input_method_delegate.h" #include "chromeos/ime/mock_component_extension_ime_manager_delegate.h" @@ -61,10 +61,10 @@ class InputMethodManagerImplTest : public testing::Test { mock_ibus_daemon_controller_ = new chromeos::MockIBusDaemonController(); chromeos::IBusDaemonController::InitializeForTesting( mock_ibus_daemon_controller_); - mock_dbus_thread_manager_ = - new chromeos::MockDBusThreadManagerWithoutGMock(); + fake_dbus_thread_manager_ = + new chromeos::FakeDBusThreadManager(); chromeos::DBusThreadManager::InitializeForTesting( - mock_dbus_thread_manager_); + fake_dbus_thread_manager_); delegate_ = new FakeInputMethodDelegate(); manager_.reset(new InputMethodManagerImpl( scoped_ptr<InputMethodDelegate>(delegate_))); @@ -151,9 +151,9 @@ class InputMethodManagerImplTest : public testing::Test { // Helper function to initialize IBus bus connection for testing. Do not use // ibus related mocks before calling this function. void InitIBusBus() { - mock_dbus_thread_manager_->InitIBusBus("dummy address", + fake_dbus_thread_manager_->InitIBusBus("dummy address", base::Bind(&base::DoNothing)); - mock_ibus_client_ = mock_dbus_thread_manager_->mock_ibus_client(); + mock_ibus_client_ = fake_dbus_thread_manager_->mock_ibus_client(); mock_ibus_daemon_controller_->EmulateConnect(); } @@ -164,7 +164,7 @@ class InputMethodManagerImplTest : public testing::Test { MockIBusDaemonController* mock_ibus_daemon_controller_; scoped_ptr<MockIMEEngineHandler> mock_engine_handler_; MockIBusClient* mock_ibus_client_; - MockDBusThreadManagerWithoutGMock* mock_dbus_thread_manager_; + FakeDBusThreadManager* fake_dbus_thread_manager_; MockXKeyboard* xkeyboard_; base::MessageLoop message_loop_; MockComponentExtIMEManagerDelegate* mock_delegate_; diff --git a/chrome/browser/chromeos/kiosk_mode/kiosk_mode_idle_logout.cc b/chrome/browser/chromeos/kiosk_mode/kiosk_mode_idle_logout.cc index 47cc5a9a3b..d005433b5a 100644 --- a/chrome/browser/chromeos/kiosk_mode/kiosk_mode_idle_logout.cc +++ b/chrome/browser/chromeos/kiosk_mode/kiosk_mode_idle_logout.cc @@ -22,8 +22,6 @@ namespace chromeos { namespace { -const int64 kLoginIdleTimeout = 100; // seconds - static base::LazyInstance<KioskModeIdleLogout> g_kiosk_mode_idle_logout = LAZY_INSTANCE_INITIALIZER; diff --git a/chrome/browser/chromeos/login/app_launch_signin_screen.cc b/chrome/browser/chromeos/login/app_launch_signin_screen.cc index 06f17a795d..2092480262 100644 --- a/chrome/browser/chromeos/login/app_launch_signin_screen.cc +++ b/chrome/browser/chromeos/login/app_launch_signin_screen.cc @@ -196,9 +196,7 @@ void AppLaunchSigninScreen::OnLoginFailure(const LoginFailure& error) { HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT_OFFLINE); } -void AppLaunchSigninScreen::OnLoginSuccess(const UserContext& user_context, - bool pending_requests, - bool using_oauth) { +void AppLaunchSigninScreen::OnLoginSuccess(const UserContext& user_context) { delegate_->OnOwnerSigninSuccess(); } diff --git a/chrome/browser/chromeos/login/app_launch_signin_screen.h b/chrome/browser/chromeos/login/app_launch_signin_screen.h index 7b2ac1536e..dc5b2f423e 100644 --- a/chrome/browser/chromeos/login/app_launch_signin_screen.h +++ b/chrome/browser/chromeos/login/app_launch_signin_screen.h @@ -84,9 +84,7 @@ class AppLaunchSigninScreen // LoginStatusConsumer implementation: virtual void OnLoginFailure(const LoginFailure& error) OVERRIDE; - virtual void OnLoginSuccess(const UserContext& user_context, - bool pending_requests, - bool using_oauth) OVERRIDE; + virtual void OnLoginSuccess(const UserContext& user_context) OVERRIDE; OobeUI* oobe_ui_; Delegate* delegate_; diff --git a/chrome/browser/chromeos/login/auth_sync_observer.cc b/chrome/browser/chromeos/login/auth_sync_observer.cc index e2dd8a072d..4c623c2bee 100644 --- a/chrome/browser/chromeos/login/auth_sync_observer.cc +++ b/chrome/browser/chromeos/login/auth_sync_observer.cc @@ -5,6 +5,7 @@ #include "chrome/browser/chromeos/login/auth_sync_observer.h" #include "base/prefs/pref_service.h" +#include "chrome/browser/chromeos/login/supervised_user_manager.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/sync/profile_sync_service_factory.h" @@ -58,7 +59,7 @@ void AuthSyncObserver::OnStateChanged() { std::string sync_id = profile_->GetPrefs()->GetString(prefs::kManagedUserId); const User* user = - UserManager::Get()->FindLocallyManagedUserBySyncId(sync_id); + UserManager::Get()->GetSupervisedUserManager()->FindBySyncId(sync_id); if (user) email = user->email(); } diff --git a/chrome/browser/chromeos/login/authenticator.h b/chrome/browser/chromeos/login/authenticator.h index b20a35bd17..f061803683 100644 --- a/chrome/browser/chromeos/login/authenticator.h +++ b/chrome/browser/chromeos/login/authenticator.h @@ -19,11 +19,10 @@ namespace chromeos { struct UserContext; // An interface for objects that will authenticate a Chromium OS user. -// When authentication successfully completes, will call -// consumer_->OnLoginSuccess() on the UI thread. -// On failure, will call consumer_->OnLoginFailure() on the UI thread. -// On password change detected, will call -// consumer_->OnPasswordChangeDetected() on the UI thread. +// Callbacks will be called on the UI thread: +// 1. On successful authentication, will call consumer_->OnLoginSuccess(). +// 2. On failure, will call consumer_->OnLoginFailure(). +// 3. On password change, will call consumer_->OnPasswordChangeDetected(). class Authenticator : public base::RefCountedThreadSafe<Authenticator> { public: explicit Authenticator(LoginStatusConsumer* consumer); @@ -42,8 +41,7 @@ class Authenticator : public base::RefCountedThreadSafe<Authenticator> { // Given a user credentials in |user_context|, this method attempts to // authenticate to unlock the computer. // Must be called on the UI thread. - virtual void AuthenticateToUnlock( - const UserContext& user_context) = 0; + virtual void AuthenticateToUnlock(const UserContext& user_context) = 0; // Initiates locally managed user login. virtual void LoginAsLocallyManagedUser( @@ -65,11 +63,8 @@ class Authenticator : public base::RefCountedThreadSafe<Authenticator> { // Completes retail mode login. virtual void OnRetailModeLoginSuccess() = 0; - // Notifies caller that login was successful. - // |request_pending| is true if we still plan to call consumer_ with the - // results of more requests. - // Must be called on the UI thread. - virtual void OnLoginSuccess(bool request_pending) = 0; + // Notifies caller that login was successful. Must be called on the UI thread. + virtual void OnLoginSuccess() = 0; // Must be called on the UI thread. virtual void OnLoginFailure(const LoginFailure& error) = 0; diff --git a/chrome/browser/chromeos/login/captive_portal_view.cc b/chrome/browser/chromeos/login/captive_portal_view.cc index c6dc73e634..96f348ff6a 100644 --- a/chrome/browser/chromeos/login/captive_portal_view.cc +++ b/chrome/browser/chromeos/login/captive_portal_view.cc @@ -77,8 +77,7 @@ views::NonClientFrameView* CaptivePortalView::CreateNonClientFrameView( return views::DialogDelegate::CreateNewStyleFrameView(widget, force_opaque_border); } - ash::CustomFrameViewAsh* frame = new ash::CustomFrameViewAsh; - frame->Init(widget); + ash::CustomFrameViewAsh* frame = new ash::CustomFrameViewAsh(widget); // Always use "active" look. frame->SetInactiveRenderingDisabled(true); return frame; diff --git a/chrome/browser/chromeos/login/chrome_restart_request.cc b/chrome/browser/chromeos/login/chrome_restart_request.cc index 742583849e..e921b95409 100644 --- a/chrome/browser/chromeos/login/chrome_restart_request.cc +++ b/chrome/browser/chromeos/login/chrome_restart_request.cc @@ -54,9 +54,6 @@ const char kGuestModeLoggingLevel[] = "1"; // Format of command line switch. const char kSwitchFormatString[] = " --%s=\"%s\""; -// User name which is used in the Guest session. -const char kGuestUserName[] = ""; - // Derives the new command line from |base_command_line| by doing the following: // - Forward a given switches list to new command; // - Set start url if given; @@ -89,6 +86,7 @@ std::string DeriveCommandLine(const GURL& start_url, ::switches::kDisableThreadedCompositing, ::switches::kDisableTouchDragDrop, ::switches::kDisableTouchEditing, + ::switches::kDisableUniversalAcceleratedOverflowScroll, ::switches::kDisableWebKitMediaSource, ::switches::kDisableAcceleratedFixedRootBackground, ::switches::kEnableAcceleratedFixedRootBackground, @@ -106,6 +104,7 @@ std::string DeriveCommandLine(const GURL& start_url, ::switches::kEnableThreadedCompositing, ::switches::kEnableTouchDragDrop, ::switches::kEnableTouchEditing, + ::switches::kEnableUniversalAcceleratedOverflowScroll, ::switches::kEnableViewport, ::switches::kForceDeviceScaleFactor, ::switches::kGpuStartupDialog, diff --git a/chrome/browser/chromeos/login/crash_restore_browsertest.cc b/chrome/browser/chromeos/login/crash_restore_browsertest.cc index 6062fd8a49..d956d39aa5 100644 --- a/chrome/browser/chromeos/login/crash_restore_browsertest.cc +++ b/chrome/browser/chromeos/login/crash_restore_browsertest.cc @@ -14,8 +14,8 @@ #include "chrome/test/base/in_process_browser_test.h" #include "chromeos/chromeos_switches.h" #include "chromeos/dbus/cryptohome_client.h" +#include "chromeos/dbus/fake_dbus_thread_manager.h" #include "chromeos/dbus/fake_session_manager_client.h" -#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" #include "chromeos/dbus/session_manager_client.h" #include "content/public/test/test_utils.h" #include "testing/gmock/include/gmock/gmock.h" @@ -47,8 +47,8 @@ class CrashRestoreSimpleTest : public InProcessBrowserTest { virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { // Redirect session_manager DBus calls to FakeSessionManagerClient. - MockDBusThreadManagerWithoutGMock* dbus_thread_manager = - new MockDBusThreadManagerWithoutGMock(); + FakeDBusThreadManager* dbus_thread_manager = + new FakeDBusThreadManager(); session_manager_client_ = dbus_thread_manager->fake_session_manager_client(); DBusThreadManager::InitializeForTesting(dbus_thread_manager); diff --git a/chrome/browser/chromeos/login/eula_browsertest.cc b/chrome/browser/chromeos/login/eula_browsertest.cc index 412ddb5c91..e1cb49f1d4 100644 --- a/chrome/browser/chromeos/login/eula_browsertest.cc +++ b/chrome/browser/chromeos/login/eula_browsertest.cc @@ -22,7 +22,9 @@ namespace { const char kEULAURL[] = "https://www.google.com/intl/en-US/chrome/eula_text.html"; const char kFakeOnlineEULA[] = "No obligations at all"; +#if defined(GOOGLE_CHROME_BUILD) const char kOfflineEULAWarning[] = "A copy of the Google Terms of Service"; +#endif class TermsOfServiceProcessBrowserTest : public InProcessBrowserTest { }; diff --git a/chrome/browser/chromeos/login/existing_user_controller.cc b/chrome/browser/chromeos/login/existing_user_controller.cc index cfe8624c5c..ceef789e26 100644 --- a/chrome/browser/chromeos/login/existing_user_controller.cc +++ b/chrome/browser/chromeos/login/existing_user_controller.cc @@ -66,9 +66,6 @@ namespace chromeos { namespace { -// Major version where we still show GSG as "Release Notes" after the update. -const long int kReleaseNotesTargetRelease = 19; - // URL for account creation. const char kCreateAccountURL[] = "https://accounts.google.com/NewAccount?service=mail"; @@ -84,9 +81,6 @@ const long int kAuthCacheTransferDelayMs = 2000; // Delay for restarting the ui if safe-mode login has failed. const long int kSafeModeRestartUiDelayMs = 30000; -// Delay for rebooting machine if TPM critical error was encountered. -const long int kCriticalErrorRebootDelayMs = 3500; - // Makes a call to the policy subsystem to reload the policy when we detect // authentication change. void RefreshPoliciesOnUIThread() { @@ -749,10 +743,7 @@ void ExistingUserController::OnLoginFailure(const LoginFailure& failure) { display_email_.clear(); } -void ExistingUserController::OnLoginSuccess( - const UserContext& user_context, - bool pending_requests, - bool using_oauth) { +void ExistingUserController::OnLoginSuccess(const UserContext& user_context) { is_login_in_progress_ = false; offline_failed_ = false; login_display_->set_signin_completed(true); @@ -778,7 +769,6 @@ void ExistingUserController::OnLoginSuccess( // Will call OnProfilePrepared() in the end. LoginUtils::Get()->PrepareProfile(user_context, display_email_, - using_oauth, has_cookies, false, // Start session for user. this); @@ -824,13 +814,9 @@ void ExistingUserController::OnProfilePrepared(Profile* profile) { LoginUtils::Get()->DoBrowserLaunch(profile, host_); host_ = NULL; } - // Inform |login_status_consumer_| about successful login. Set most - // parameters to empty since they're not needed. - if (login_status_consumer_) { - login_status_consumer_->OnLoginSuccess(UserContext(), - false, // pending_requests - false); // using_oauth - } + // Inform |login_status_consumer_| about successful login. + if (login_status_consumer_) + login_status_consumer_->OnLoginSuccess(UserContext()); login_display_->OnFadeOut(); } diff --git a/chrome/browser/chromeos/login/existing_user_controller.h b/chrome/browser/chromeos/login/existing_user_controller.h index 80fbcd5545..ace3e0595a 100644 --- a/chrome/browser/chromeos/login/existing_user_controller.h +++ b/chrome/browser/chromeos/login/existing_user_controller.h @@ -134,10 +134,7 @@ class ExistingUserController : public LoginDisplay::Delegate, // LoginPerformer::Delegate implementation: virtual void OnLoginFailure(const LoginFailure& error) OVERRIDE; - virtual void OnLoginSuccess( - const UserContext& user_context, - bool pending_requests, - bool using_oauth) OVERRIDE; + virtual void OnLoginSuccess(const UserContext& user_context) OVERRIDE; virtual void OnOffTheRecordLoginSuccess() OVERRIDE; virtual void OnPasswordChangeDetected() OVERRIDE; virtual void WhiteListCheckFailed(const std::string& email) OVERRIDE; diff --git a/chrome/browser/chromeos/login/existing_user_controller_auto_login_unittest.cc b/chrome/browser/chromeos/login/existing_user_controller_auto_login_unittest.cc index 03c5638d66..e0a9ba01bf 100644 --- a/chrome/browser/chromeos/login/existing_user_controller_auto_login_unittest.cc +++ b/chrome/browser/chromeos/login/existing_user_controller_auto_login_unittest.cc @@ -34,7 +34,6 @@ namespace { const char kAutoLoginAccountId[] = "public_session_user@localhost"; // These values are only used to test the configuration. They don't // delay the test. -const int kAutoLoginNoDelay = 0; const int kAutoLoginDelay1 = 60000; const int kAutoLoginDelay2 = 180000; diff --git a/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc b/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc index 70b8084982..2b75231684 100644 --- a/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc +++ b/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc @@ -240,7 +240,7 @@ IN_PROC_BROWSER_TEST_P(ExistingUserControllerTest, ExistingUserLogin) { .WillOnce(WithArg<0>(CreateAuthenticator(kUsername, kPassword))); EXPECT_CALL(*mock_login_utils_, PrepareProfile(UserContext(kUsername, kPassword, "", kUsername), - _, _, _, _, _)) + _, _, _, _)) .Times(1) .WillOnce(InvokeWithoutArgs(&profile_prepared_cb_, &base::Callback<void(void)>::Run)); @@ -309,7 +309,7 @@ IN_PROC_BROWSER_TEST_P(ExistingUserControllerTest, kPassword, std::string(), kNewUsername), - _, _, _, _, _)) + _, _, _, _)) .Times(1) .WillOnce(InvokeWithoutArgs(&profile_prepared_cb_, &base::Callback<void(void)>::Run)); @@ -433,7 +433,7 @@ class ExistingUserControllerPublicSessionTest .WillOnce(WithArg<0>(CreateAuthenticator(username, password))); EXPECT_CALL(*mock_login_utils_, PrepareProfile(UserContext(username, password, "", username), - _, _, _, _, _)) + _, _, _, _)) .Times(1) .WillOnce(InvokeWithoutArgs(&profile_prepared_cb_, &base::Callback<void(void)>::Run)); diff --git a/chrome/browser/chromeos/login/fake_login_utils.cc b/chrome/browser/chromeos/login/fake_login_utils.cc index e5e5b06272..cd573deb4f 100644 --- a/chrome/browser/chromeos/login/fake_login_utils.cc +++ b/chrome/browser/chromeos/login/fake_login_utils.cc @@ -11,6 +11,7 @@ #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chromeos/login/login_display_host.h" #include "chrome/browser/chromeos/login/mock_authenticator.h" +#include "chrome/browser/chromeos/login/supervised_user_manager.h" #include "chrome/browser/first_run/first_run.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/startup/startup_browser_creator.h" @@ -55,7 +56,6 @@ void FakeLoginUtils::DoBrowserLaunch(Profile* profile, void FakeLoginUtils::PrepareProfile(const UserContext& user_context, const std::string& display_email, - bool using_oauth, bool has_cookies, bool has_active_session, LoginUtils::Delegate* delegate) { @@ -76,7 +76,8 @@ void FakeLoginUtils::PrepareProfile(const UserContext& user_context, if (UserManager::Get()->IsLoggedInAsLocallyManagedUser()) { User* active_user = UserManager::Get()->GetActiveUser(); std::string managed_user_sync_id = - UserManager::Get()->GetManagedUserSyncId(active_user->email()); + UserManager::Get()->GetSupervisedUserManager()-> + GetUserSyncId(active_user->email()); if (managed_user_sync_id.empty()) managed_user_sync_id = "DUMMY ID"; profile->GetPrefs()->SetString(prefs::kManagedUserId, diff --git a/chrome/browser/chromeos/login/fake_login_utils.h b/chrome/browser/chromeos/login/fake_login_utils.h index 2cf4c29150..23a834e838 100644 --- a/chrome/browser/chromeos/login/fake_login_utils.h +++ b/chrome/browser/chromeos/login/fake_login_utils.h @@ -24,7 +24,6 @@ class FakeLoginUtils : public LoginUtils { LoginDisplayHost* login_host) OVERRIDE; virtual void PrepareProfile(const UserContext& user_context, const std::string& display_email, - bool using_oauth, bool has_cookies, bool has_active_session, LoginUtils::Delegate* delegate) OVERRIDE; diff --git a/chrome/browser/chromeos/login/fake_supervised_user_manager.cc b/chrome/browser/chromeos/login/fake_supervised_user_manager.cc new file mode 100644 index 0000000000..6310d9e80b --- /dev/null +++ b/chrome/browser/chromeos/login/fake_supervised_user_manager.cc @@ -0,0 +1,58 @@ +// Copyright 2013 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/chromeos/login/fake_supervised_user_manager.h" + +#include <string> + +namespace chromeos { + +FakeSupervisedUserManager::FakeSupervisedUserManager() {} + +FakeSupervisedUserManager::~FakeSupervisedUserManager() { +} + +const User* FakeSupervisedUserManager::CreateUserRecord( + const std::string& manager_id, + const std::string& local_user_id, + const std::string& sync_user_id, + const string16& display_name) { + return NULL; +} + +std::string FakeSupervisedUserManager::GenerateUserId() { + return std::string(); +} + +const User* FakeSupervisedUserManager::FindByDisplayName( + const string16& display_name) const { + return NULL; +} + +const User* FakeSupervisedUserManager::FindBySyncId( + const std::string& sync_id) const { + return NULL; +} + +std::string FakeSupervisedUserManager::GetUserSyncId( + const std::string& managed_user_id) const { + return std::string(); +} + +string16 FakeSupervisedUserManager::GetManagerDisplayName( + const std::string& managed_user_id) const { + return string16(); +} + +std::string FakeSupervisedUserManager::GetManagerUserId( + const std::string& managed_user_id) const { + return std::string(); +} + +std::string FakeSupervisedUserManager::GetManagerDisplayEmail( + const std::string& managed_user_id) const { + return std::string(); +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/fake_supervised_user_manager.h b/chrome/browser/chromeos/login/fake_supervised_user_manager.h new file mode 100644 index 0000000000..a291ade8df --- /dev/null +++ b/chrome/browser/chromeos/login/fake_supervised_user_manager.h @@ -0,0 +1,48 @@ +// Copyright 2013 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_CHROMEOS_LOGIN_FAKE_SUPERVISED_USER_MANAGER_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_FAKE_SUPERVISED_USER_MANAGER_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "chrome/browser/chromeos/login/supervised_user_manager.h" + +namespace chromeos { + +// Fake supervised user manager with a barebones implementation. +class FakeSupervisedUserManager : public SupervisedUserManager { + public: + FakeSupervisedUserManager(); + virtual ~FakeSupervisedUserManager(); + + virtual const User* CreateUserRecord( + const std::string& manager_id, + const std::string& local_user_id, + const std::string& sync_user_id, + const string16& display_name) OVERRIDE; + virtual std::string GenerateUserId() OVERRIDE; + virtual const User* FindByDisplayName(const string16& display_name) const + OVERRIDE; + virtual const User* FindBySyncId(const std::string& sync_id) const OVERRIDE; + virtual std::string GetUserSyncId(const std::string& user_id) const OVERRIDE; + virtual string16 GetManagerDisplayName(const std::string& user_id) const + OVERRIDE; + virtual std::string GetManagerUserId(const std::string& user_id) const + OVERRIDE; + virtual std::string GetManagerDisplayEmail(const std::string& user_id) const + OVERRIDE; + virtual void StartCreationTransaction(const string16& display_name) + OVERRIDE {} + virtual void SetCreationTransactionUserId(const std::string& user_id) + OVERRIDE {} + virtual void CommitCreationTransaction() OVERRIDE {} + + private: + DISALLOW_COPY_AND_ASSIGN(FakeSupervisedUserManager); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_FAKE_SUPERVISED_USER_MANAGER_H_ diff --git a/chrome/browser/chromeos/login/fake_user_manager.cc b/chrome/browser/chromeos/login/fake_user_manager.cc index 2cb93e72f9..f5b1e7e059 100644 --- a/chrome/browser/chromeos/login/fake_user_manager.cc +++ b/chrome/browser/chromeos/login/fake_user_manager.cc @@ -4,6 +4,8 @@ #include "chrome/browser/chromeos/login/fake_user_manager.h" +#include "chrome/browser/chromeos/login/fake_supervised_user_manager.h" + namespace { // As defined in /chromeos/dbus/cryptohome_client.cc. @@ -13,7 +15,9 @@ static const char kUserIdHashSuffix[] = "-hash"; namespace chromeos { -FakeUserManager::FakeUserManager() : primary_user_(NULL) {} +FakeUserManager::FakeUserManager() + : supervised_user_manager_(new FakeSupervisedUserManager), + primary_user_(NULL) {} FakeUserManager::~FakeUserManager() { // Can't use STLDeleteElements because of the private destructor of User. @@ -107,6 +111,10 @@ void FakeUserManager::UpdateUserAccountData(const std::string&, const string16&, // Not implemented } +SupervisedUserManager* FakeUserManager::GetSupervisedUserManager() { + return supervised_user_manager_.get(); +} + UserImageManager* FakeUserManager::GetUserImageManager() { return NULL; } @@ -123,18 +131,6 @@ const std::string& FakeUserManager::GetOwnerEmail() { return owner_email_; } -const User* FakeUserManager::CreateLocallyManagedUserRecord( - const std::string& manager_id, - const std::string& local_user_id, - const std::string& sync_user_id, - const string16& display_name) { - return NULL; -} - -std::string FakeUserManager::GenerateUniqueLocallyManagedUserId() { - return std::string(); -} - bool FakeUserManager::IsKnownUser(const std::string& email) const { return true; } @@ -143,16 +139,6 @@ const User* FakeUserManager::FindUser(const std::string& email) const { return NULL; } -const User* FakeUserManager::FindLocallyManagedUser( - const string16& display_name) const { - return NULL; -} - -const User* FakeUserManager::FindLocallyManagedUserBySyncId( - const std::string& sync_id) const { - return NULL; -} - const User* FakeUserManager::GetLoggedInUser() const { return NULL; } @@ -179,26 +165,6 @@ std::string FakeUserManager::GetUserDisplayEmail( return std::string(); } -std::string FakeUserManager::GetManagedUserSyncId( - const std::string& managed_user_id) const { - return std::string(); -} - -string16 FakeUserManager::GetManagerDisplayNameForManagedUser( - const std::string& managed_user_id) const { - return string16(); -} - -std::string FakeUserManager::GetManagerUserIdForManagedUser( - const std::string& managed_user_id) const { - return std::string(); -} - -std::string FakeUserManager::GetManagerDisplayEmailForManagedUser( - const std::string& managed_user_id) const { - return std::string(); -} - bool FakeUserManager::IsCurrentUserOwner() const { return false; } diff --git a/chrome/browser/chromeos/login/fake_user_manager.h b/chrome/browser/chromeos/login/fake_user_manager.h index 0a6671b674..2423a6f26f 100644 --- a/chrome/browser/chromeos/login/fake_user_manager.h +++ b/chrome/browser/chromeos/login/fake_user_manager.h @@ -7,6 +7,7 @@ #include <string> +#include "base/memory/scoped_ptr.h" #include "chrome/browser/chromeos/login/user.h" #include "chrome/browser/chromeos/login/user_flow.h" #include "chrome/browser/chromeos/login/user_image.h" @@ -14,6 +15,8 @@ namespace chromeos { +class FakeSupervisedUserManager; + // Fake user manager with a barebones implementation. Users can be added // and set as logged in, and those users can be returned. class FakeUserManager : public UserManager { @@ -50,27 +53,18 @@ class FakeUserManager : public UserManager { // Not implemented. virtual void Shutdown() OVERRIDE {} virtual UserImageManager* GetUserImageManager() OVERRIDE; + virtual SupervisedUserManager* GetSupervisedUserManager() OVERRIDE; virtual const UserList& GetLRULoggedInUsers() OVERRIDE; virtual UserList GetUnlockUsers() const OVERRIDE; virtual const std::string& GetOwnerEmail() OVERRIDE; virtual void SwitchActiveUser(const std::string& email) OVERRIDE {} virtual void SessionStarted() OVERRIDE {} virtual void RestoreActiveSessions() OVERRIDE {} - virtual const User* CreateLocallyManagedUserRecord( - const std::string& manager_id, - const std::string& local_user_id, - const std::string& sync_user_id, - const string16& display_name) OVERRIDE; - virtual std::string GenerateUniqueLocallyManagedUserId() OVERRIDE; virtual void RemoveUser(const std::string& email, RemoveUserDelegate* delegate) OVERRIDE {} virtual void RemoveUserFromList(const std::string& email) OVERRIDE {} virtual bool IsKnownUser(const std::string& email) const OVERRIDE; virtual const User* FindUser(const std::string& email) const OVERRIDE; - virtual const User* FindLocallyManagedUser( - const string16& display_name) const OVERRIDE; - virtual const User* FindLocallyManagedUserBySyncId( - const std::string& sync_id) const OVERRIDE; virtual const User* GetLoggedInUser() const OVERRIDE; virtual User* GetLoggedInUser() OVERRIDE; virtual const User* GetPrimaryUser() const OVERRIDE; @@ -84,14 +78,6 @@ class FakeUserManager : public UserManager { const std::string& display_email) OVERRIDE {} virtual std::string GetUserDisplayEmail( const std::string& username) const OVERRIDE; - virtual std::string GetManagedUserSyncId( - const std::string& managed_user_id) const OVERRIDE; - virtual string16 GetManagerDisplayNameForManagedUser( - const std::string& managed_user_id) const OVERRIDE; - virtual std::string GetManagerUserIdForManagedUser( - const std::string& managed_user_id) const OVERRIDE; - virtual std::string GetManagerDisplayEmailForManagedUser( - const std::string& managed_user_id) const OVERRIDE; virtual bool IsCurrentUserOwner() const OVERRIDE; virtual bool IsCurrentUserNew() const OVERRIDE; virtual bool IsCurrentUserNonCryptohomeDataEphemeral() const OVERRIDE; @@ -109,11 +95,6 @@ class FakeUserManager : public UserManager { virtual bool HasBrowserRestarted() const OVERRIDE; virtual bool IsUserNonCryptohomeDataEphemeral( const std::string& email) const OVERRIDE; - virtual void StartLocallyManagedUserCreationTransaction( - const string16& display_name) OVERRIDE {} - virtual void SetLocallyManagedUserCreationTransactionUserId( - const std::string& email) OVERRIDE {} - virtual void CommitLocallyManagedUserCreationTransaction() OVERRIDE {} virtual void SetUserFlow(const std::string& email, UserFlow* flow) OVERRIDE {} virtual UserFlow* GetCurrentUserFlow() const OVERRIDE; virtual UserFlow* GetUserFlow(const std::string& email) const OVERRIDE; @@ -145,6 +126,7 @@ class FakeUserManager : public UserManager { // We use this internal function for const-correctness. User* GetActiveUserInternal() const; + scoped_ptr<FakeSupervisedUserManager> supervised_user_manager_; UserList user_list_; UserList logged_in_users_; std::string owner_email_; diff --git a/chrome/browser/chromeos/login/login_display_host_impl.cc b/chrome/browser/chromeos/login/login_display_host_impl.cc index b5673dfd45..5095ad8797 100644 --- a/chrome/browser/chromeos/login/login_display_host_impl.cc +++ b/chrome/browser/chromeos/login/login_display_host_impl.cc @@ -10,7 +10,7 @@ #include "ash/desktop_background/user_wallpaper_delegate.h" #include "ash/shell.h" #include "ash/shell_window_ids.h" -#include "ash/wm/frame_painter.h" +#include "ash/wm/header_painter.h" #include "base/bind.h" #include "base/command_line.h" #include "base/debug/trace_event.h" @@ -765,7 +765,7 @@ void LoginDisplayHostImpl::StartPostponedWebUI() { void LoginDisplayHostImpl::InitLoginWindowAndView() { if (login_window_) return; - ash::FramePainter::SetSoloWindowHeadersEnabled(false); + ash::HeaderPainter::SetSoloWindowHeadersEnabled(false); views::Widget::InitParams params( views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); @@ -808,7 +808,7 @@ void LoginDisplayHostImpl::InitLoginWindowAndView() { void LoginDisplayHostImpl::ResetLoginWindowAndView() { if (!login_window_) return; - ash::FramePainter::SetSoloWindowHeadersEnabled(true); + ash::HeaderPainter::SetSoloWindowHeadersEnabled(true); login_window_->Close(); login_window_ = NULL; login_view_ = NULL; diff --git a/chrome/browser/chromeos/login/login_performer.cc b/chrome/browser/chromeos/login/login_performer.cc index e8537d1f63..365704f512 100644 --- a/chrome/browser/chromeos/login/login_performer.cc +++ b/chrome/browser/chromeos/login/login_performer.cc @@ -90,32 +90,14 @@ void LoginPerformer::OnRetailModeLoginSuccess( LoginStatusConsumer::OnRetailModeLoginSuccess(user_context); } -void LoginPerformer::OnLoginSuccess( - const UserContext& user_context, - bool pending_requests, - bool using_oauth) { +void LoginPerformer::OnLoginSuccess(const UserContext& user_context) { content::RecordAction(UserMetricsAction("Login_Success")); - // The value of |pending_requests| indicates: - // 0 - New regular user, login success offline and online. - // - or - - // Existing regular user, login success offline and online, offline - // authentication took longer than online authentication. - // - or - - // Public account user, login successful. - // 1 - Existing regular user, login success offline only. - UMA_HISTOGRAM_ENUMERATION("Login.SuccessReason", pending_requests, 2); - - VLOG(1) << "LoginSuccess hash: " << user_context.username_hash - << ", pending_requests " << pending_requests; + VLOG(1) << "LoginSuccess hash: " << user_context.username_hash; DCHECK(delegate_); // After delegate_->OnLoginSuccess(...) is called, delegate_ releases // LoginPerformer ownership. LP now manages it's lifetime on its own. - DCHECK(!pending_requests); base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); - - delegate_->OnLoginSuccess(user_context, - pending_requests, - using_oauth); + delegate_->OnLoginSuccess(user_context); } void LoginPerformer::OnOffTheRecordLoginSuccess() { diff --git a/chrome/browser/chromeos/login/login_performer.h b/chrome/browser/chromeos/login/login_performer.h index d96ebcfd38..379a4588a0 100644 --- a/chrome/browser/chromeos/login/login_performer.h +++ b/chrome/browser/chromeos/login/login_performer.h @@ -54,10 +54,7 @@ class LoginPerformer : public LoginStatusConsumer, virtual void OnLoginFailure(const LoginFailure& error) OVERRIDE; virtual void OnRetailModeLoginSuccess( const UserContext& user_context) OVERRIDE; - virtual void OnLoginSuccess( - const UserContext& user_context, - bool pending_requests, - bool using_oauth) OVERRIDE; + virtual void OnLoginSuccess(const UserContext& user_context) OVERRIDE; virtual void OnOffTheRecordLoginSuccess() OVERRIDE; virtual void OnPasswordChangeDetected() OVERRIDE; diff --git a/chrome/browser/chromeos/login/login_status_consumer.cc b/chrome/browser/chromeos/login/login_status_consumer.cc index d6db996dd4..923e54d5e7 100644 --- a/chrome/browser/chromeos/login/login_status_consumer.cc +++ b/chrome/browser/chromeos/login/login_status_consumer.cc @@ -9,9 +9,7 @@ namespace chromeos { void LoginStatusConsumer::OnRetailModeLoginSuccess( const UserContext& user_context) { - OnLoginSuccess(user_context, - false, // pending_requests - false); // using_oauth + OnLoginSuccess(user_context); } void LoginStatusConsumer::OnPasswordChangeDetected() { diff --git a/chrome/browser/chromeos/login/login_status_consumer.h b/chrome/browser/chromeos/login/login_status_consumer.h index 9f1b14d8cd..fccbaa4af3 100644 --- a/chrome/browser/chromeos/login/login_status_consumer.h +++ b/chrome/browser/chromeos/login/login_status_consumer.h @@ -119,12 +119,7 @@ class LoginStatusConsumer { // OnLoginSuccess with the magic |kRetailModeUserEMail| constant. virtual void OnRetailModeLoginSuccess(const UserContext& user_context); // The current login attempt has succeeded for |user_context|. - // If |pending_requests| is false, we're totally done. - // If it's true, we will still have some more results to report later. - virtual void OnLoginSuccess( - const UserContext& user_context, - bool pending_requests, - bool using_oauth) = 0; + virtual void OnLoginSuccess(const UserContext& user_context) = 0; // The current guest login attempt has succeeded. virtual void OnOffTheRecordLoginSuccess() {} // The same password didn't work both online and offline. diff --git a/chrome/browser/chromeos/login/login_utils.cc b/chrome/browser/chromeos/login/login_utils.cc index d11cb5a65a..aaebc2513b 100644 --- a/chrome/browser/chromeos/login/login_utils.cc +++ b/chrome/browser/chromeos/login/login_utils.cc @@ -41,6 +41,7 @@ #include "chrome/browser/chromeos/login/parallel_authenticator.h" #include "chrome/browser/chromeos/login/profile_auth_data.h" #include "chrome/browser/chromeos/login/screen_locker.h" +#include "chrome/browser/chromeos/login/supervised_user_manager.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/extensions/extension_service.h" @@ -104,8 +105,7 @@ class LoginUtilsImpl public base::SupportsWeakPtr<LoginUtilsImpl> { public: LoginUtilsImpl() - : using_oauth_(false), - has_web_auth_cookies_(false), + : has_web_auth_cookies_(false), delegate_(NULL), should_restore_auth_session_(false), exit_after_session_restore_(false), @@ -124,7 +124,6 @@ class LoginUtilsImpl virtual void PrepareProfile( const UserContext& user_context, const std::string& display_email, - bool using_oauth, bool has_cookies, bool has_active_session, LoginUtils::Delegate* delegate) OVERRIDE; @@ -193,7 +192,6 @@ class LoginUtilsImpl void AttemptExit(Profile* profile); UserContext user_context_; - bool using_oauth_; // True if the authentication profile's cookie jar should contain // authentication cookies from the authentication extension log in flow. @@ -315,7 +313,6 @@ void LoginUtilsImpl::DoBrowserLaunch(Profile* profile, void LoginUtilsImpl::PrepareProfile( const UserContext& user_context, const std::string& display_email, - bool using_oauth, bool has_cookies, bool has_active_session, LoginUtils::Delegate* delegate) { @@ -347,7 +344,6 @@ void LoginUtilsImpl::PrepareProfile( user_context_ = user_context; - using_oauth_ = using_oauth; has_web_auth_cookies_ = has_cookies; delegate_ = delegate; InitSessionRestoreStrategy(); @@ -374,7 +370,8 @@ void LoginUtilsImpl::InitProfilePreferences(Profile* user_profile, if (UserManager::Get()->IsLoggedInAsLocallyManagedUser()) { User* active_user = UserManager::Get()->GetActiveUser(); std::string managed_user_sync_id = - UserManager::Get()->GetManagedUserSyncId(active_user->email()); + UserManager::Get()->GetSupervisedUserManager()-> + GetUserSyncId(active_user->email()); // TODO(ibraaaa): Remove that when 97% of our users are using M31. // http://crbug.com/276163 @@ -461,7 +458,7 @@ void LoginUtilsImpl::UserProfileInitialized(Profile* user_profile) { BootTimesLoader* btl = BootTimesLoader::Get(); btl->AddLoginTimeMarker("UserProfileGotten", false); - if (using_oauth_) { + if (user_context_.using_oauth) { // Transfer proxy authentication cache, cookies (optionally) and server // bound certs from the profile that was used for authentication. This // profile contains cookies that auth extension should have already put in diff --git a/chrome/browser/chromeos/login/login_utils.h b/chrome/browser/chromeos/login/login_utils.h index b000403fd0..0e6438371e 100644 --- a/chrome/browser/chromeos/login/login_utils.h +++ b/chrome/browser/chromeos/login/login_utils.h @@ -70,7 +70,6 @@ class LoginUtils { virtual void PrepareProfile( const UserContext& user_context, const std::string& display_email, - bool using_oauth, bool has_cookies, bool has_active_session, Delegate* delegate) = 0; diff --git a/chrome/browser/chromeos/login/login_utils_browsertest.cc b/chrome/browser/chromeos/login/login_utils_browsertest.cc index a182cb27dd..465b33e977 100644 --- a/chrome/browser/chromeos/login/login_utils_browsertest.cc +++ b/chrome/browser/chromeos/login/login_utils_browsertest.cc @@ -42,9 +42,9 @@ #include "chrome/test/base/scoped_testing_local_state.h" #include "chrome/test/base/testing_browser_process.h" #include "chromeos/chromeos_switches.h" -#include "chromeos/cryptohome/cryptohome_library.h" #include "chromeos/cryptohome/mock_async_method_caller.h" -#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" +#include "chromeos/cryptohome/system_salt_getter.h" +#include "chromeos/dbus/fake_dbus_thread_manager.h" #include "chromeos/disks/disk_mount_manager.h" #include "chromeos/disks/mock_disk_mount_manager.h" #include "chromeos/login/login_state.h" @@ -84,19 +84,10 @@ using ::testing::SetArgPointee; using ::testing::_; using content::BrowserThread; -const char kTrue[] = "true"; -const char kFalse[] = "false"; const char kDomain[] = "domain.com"; const char kUsername[] = "user@domain.com"; -const char kMode[] = "enterprise"; const char kDeviceId[] = "100200300"; const char kUsernameOtherDomain[] = "user@other.com"; -const char kAttributeOwned[] = "enterprise.owned"; -const char kAttributeOwner[] = "enterprise.user"; -const char kAttributeConsumerKiosk[] = "consumer.app_kiosk_enabled"; -const char kAttrEnterpriseDomain[] = "enterprise.domain"; -const char kAttrEnterpriseMode[] = "enterprise.mode"; -const char kAttrEnterpriseDeviceId[] = "enterprise.device_id"; const char kOAuthTokenCookie[] = "oauth_token=1234"; @@ -213,9 +204,9 @@ class LoginUtilsTest : public testing::Test, // DBusThreadManager should be initialized before io_thread_state_, as // DBusThreadManager is used from chromeos::ProxyConfigServiceImpl, // which is part of io_thread_state_. - DBusThreadManager::InitializeForTesting(&mock_dbus_thread_manager_); + DBusThreadManager::InitializeForTesting(&fake_dbus_thread_manager_); - CryptohomeLibrary::Initialize(); + SystemSaltGetter::Initialize(); LoginState::Initialize(); EXPECT_CALL(mock_statistics_provider_, GetMachineStatistic(_, _)) @@ -279,7 +270,7 @@ class LoginUtilsTest : public testing::Test, input_method::Shutdown(); LoginState::Shutdown(); - CryptohomeLibrary::Shutdown(); + SystemSaltGetter::Shutdown(); // These trigger some tasks that have to run while BrowserThread::UI // exists. Delete all the profiles before deleting the connector. @@ -356,9 +347,7 @@ class LoginUtilsTest : public testing::Test, FAIL() << "OnLoginFailure not expected"; } - virtual void OnLoginSuccess(const UserContext& user_context, - bool pending_requests, - bool using_oauth) OVERRIDE { + virtual void OnLoginSuccess(const UserContext& user_context) OVERRIDE { FAIL() << "OnLoginSuccess not expected"; } @@ -403,8 +392,8 @@ class LoginUtilsTest : public testing::Test, const bool kHasCookies = false; const bool kHasActiveSession = false; LoginUtils::Get()->PrepareProfile( - UserContext(username, "password", std::string(), username), - std::string(), kUsingOAuth, kHasCookies, kHasActiveSession, this); + UserContext(username, "password", std::string(), username, kUsingOAuth), + std::string(), kHasCookies, kHasActiveSession, this); device_settings_test_helper.Flush(); RunUntilIdle(); @@ -478,7 +467,7 @@ class LoginUtilsTest : public testing::Test, scoped_ptr<content::TestBrowserThread> io_thread_; scoped_ptr<IOThread> io_thread_state_; - MockDBusThreadManagerWithoutGMock mock_dbus_thread_manager_; + FakeDBusThreadManager fake_dbus_thread_manager_; input_method::MockInputMethodManager* mock_input_method_manager_; disks::MockDiskMountManager mock_disk_mount_manager_; net::TestURLFetcherFactory test_url_fetcher_factory_; @@ -489,7 +478,7 @@ class LoginUtilsTest : public testing::Test, policy::BrowserPolicyConnector* connector_; - // Initialized after |mock_dbus_thread_manager_| is set up. + // Initialized after |fake_dbus_thread_manager_| is set up. scoped_ptr<ScopedTestDeviceSettingsService> test_device_settings_service_; scoped_ptr<ScopedTestCrosSettings> test_cros_settings_; scoped_ptr<ScopedTestUserManager> test_user_manager_; diff --git a/chrome/browser/chromeos/login/managed/locally_managed_user_creation_controller.cc b/chrome/browser/chromeos/login/managed/locally_managed_user_creation_controller.cc index 9ed61d1964..f8fc7aff08 100644 --- a/chrome/browser/chromeos/login/managed/locally_managed_user_creation_controller.cc +++ b/chrome/browser/chromeos/login/managed/locally_managed_user_creation_controller.cc @@ -15,6 +15,7 @@ #include "base/values.h" #include "chrome/browser/chromeos/login/managed/locally_managed_user_constants.h" #include "chrome/browser/chromeos/login/mount_manager.h" +#include "chrome/browser/chromeos/login/supervised_user_manager.h" #include "chrome/browser/chromeos/login/user.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/lifetime/application_lifetime.h" @@ -31,7 +32,6 @@ namespace chromeos { namespace { -const int kDummyAvatarIndex = -111; const int kMasterKeySize = 32; const int kUserCreationTimeoutSeconds = 30; // 30 seconds. @@ -48,11 +48,16 @@ bool StoreManagedUserFiles(const std::string& token, } // namespace +// static +const int LocallyManagedUserCreationController::kDummyAvatarIndex = -111; + LocallyManagedUserCreationController::StatusConsumer::~StatusConsumer() {} LocallyManagedUserCreationController::UserCreationContext::UserCreationContext() - : token_acquired(false), + : avatar_index(kDummyAvatarIndex), + token_acquired(false), token_succesfully_written(false), + creation_type(NEW_USER), manager_profile(NULL) {} LocallyManagedUserCreationController::UserCreationContext:: @@ -78,11 +83,30 @@ LocallyManagedUserCreationController::~LocallyManagedUserCreationController() { current_controller_ = NULL; } -void LocallyManagedUserCreationController::SetUpCreation(string16 display_name, - std::string password) { +void LocallyManagedUserCreationController::SetUpCreation( + const string16& display_name, + const std::string& password, + int avatar_index) { DCHECK(creation_context_); creation_context_->display_name = display_name; creation_context_->password = password; + creation_context_->avatar_index = avatar_index; +} + +void LocallyManagedUserCreationController::StartImport( + const string16& display_name, + const std::string& password, + int avatar_index, + const std::string& sync_id, + const std::string& master_key) { + DCHECK(creation_context_); + creation_context_->creation_type = USER_IMPORT; + creation_context_->display_name = display_name; + creation_context_->password = password; + creation_context_->avatar_index = avatar_index; + creation_context_->sync_user_id = sync_id; + creation_context_->master_key = master_key; + StartCreation(); } void LocallyManagedUserCreationController::SetManagerProfile( @@ -107,23 +131,24 @@ void LocallyManagedUserCreationController::StartCreation() { FROM_HERE, base::TimeDelta::FromSeconds(kUserCreationTimeoutSeconds), this, &LocallyManagedUserCreationController::CreationTimedOut); + SupervisedUserManager* manager = + UserManager::Get()->GetSupervisedUserManager(); + manager->StartCreationTransaction(creation_context_->display_name); - UserManager::Get()->StartLocallyManagedUserCreationTransaction( - creation_context_->display_name); + creation_context_->local_user_id = manager->GenerateUserId(); - creation_context_->local_user_id = - UserManager::Get()->GenerateUniqueLocallyManagedUserId(); - creation_context_->sync_user_id = - ManagedUserRegistrationUtility::GenerateNewManagedUserId(); + if (creation_context_->creation_type == NEW_USER) { + creation_context_->sync_user_id = + ManagedUserRegistrationUtility::GenerateNewManagedUserId(); + } - UserManager::Get()->CreateLocallyManagedUserRecord( + manager->CreateUserRecord( creation_context_->manager_id, creation_context_->local_user_id, creation_context_->sync_user_id, creation_context_->display_name); - UserManager::Get()->SetLocallyManagedUserCreationTransactionUserId( - creation_context_->local_user_id); + manager->SetCreationTransactionUserId(creation_context_->local_user_id); VLOG(1) << "Creating cryptohome"; authenticator_ = new ManagedUserAuthenticator(this); authenticator_->AuthenticateToCreate(creation_context_->local_user_id, @@ -155,12 +180,15 @@ void LocallyManagedUserCreationController::OnMountSuccess( const std::string& mount_hash) { creation_context_->mount_hash = mount_hash; - // Generate master password. - char master_key_bytes[kMasterKeySize]; - crypto::RandBytes(&master_key_bytes, sizeof(master_key_bytes)); - creation_context_->master_key = StringToLowerASCII(base::HexEncode( - reinterpret_cast<const void*>(master_key_bytes), - sizeof(master_key_bytes))); + if (creation_context_->creation_type == NEW_USER) { + // Generate master password. + char master_key_bytes[kMasterKeySize]; + crypto::RandBytes(&master_key_bytes, sizeof(master_key_bytes)); + creation_context_->master_key = StringToLowerASCII(base::HexEncode( + reinterpret_cast<const void*>(master_key_bytes), + sizeof(master_key_bytes))); + } + VLOG(1) << "Adding master key"; authenticator_->AddMasterKey(creation_context_->local_user_id, creation_context_->password, @@ -174,7 +202,7 @@ void LocallyManagedUserCreationController::OnAddKeySuccess() { VLOG(1) << "Creating user on server"; ManagedUserRegistrationInfo info(creation_context_->display_name, - kDummyAvatarIndex); + creation_context_->avatar_index); info.master_key = creation_context_->master_key; timeout_timer_.Stop(); creation_context_->registration_utility->Register( @@ -252,7 +280,8 @@ void LocallyManagedUserCreationController::OnManagedUserFilesStored( // sync service fails to use it. UserManager::Get()->SaveUserOAuthStatus(creation_context_->local_user_id, User::OAUTH2_TOKEN_STATUS_VALID); - UserManager::Get()->CommitLocallyManagedUserCreationTransaction(); + UserManager::Get()->GetSupervisedUserManager()-> + CommitCreationTransaction(); if (consumer_) consumer_->OnCreationSuccess(); } diff --git a/chrome/browser/chromeos/login/managed/locally_managed_user_creation_controller.h b/chrome/browser/chromeos/login/managed/locally_managed_user_creation_controller.h index 1793092229..ce50959235 100644 --- a/chrome/browser/chromeos/login/managed/locally_managed_user_creation_controller.h +++ b/chrome/browser/chromeos/login/managed/locally_managed_user_creation_controller.h @@ -34,6 +34,11 @@ namespace chromeos { class LocallyManagedUserCreationController : public ManagedUserAuthenticator::AuthStatusConsumer { public: + // This constant is used to indicate that user does not have one of default + // avatars: either he has no chromeos avatar at all, or has an external + // image as an avatar. + static const int kDummyAvatarIndex; + enum ErrorCode { NO_ERROR, CRYPTOHOME_NO_MOUNT, @@ -65,7 +70,21 @@ class LocallyManagedUserCreationController return current_controller_; } - void SetUpCreation(string16 display_name, std::string password); + // Set up controller for creating new supervised user with |display_name|, + // |password| and avatar indexed by |avatar_index|. StartCreation() have to + // be called to actually start creating user. + void SetUpCreation(const string16& display_name, + const std::string& password, + int avatar_index); + + // Configures and initiates importing existing supervised user to this device. + // Existing user is identified by |sync_id|, has |display_name|, |password|, + // |avatar_index|. The master key for cryptohome is a |master_key|. + void StartImport(const string16& display_name, + const std::string& password, + int avatar_index, + const std::string& sync_id, + const std::string& master_key); void SetManagerProfile(Profile* manager_profile); void StartCreation(); void CancelCreation(); @@ -73,12 +92,19 @@ class LocallyManagedUserCreationController std::string GetManagedUserId(); private: + // Indicates if we create new user, or import an existing one. + enum CreationType { + NEW_USER, + USER_IMPORT, + }; + // Contains information necessary for new user creation. struct UserCreationContext { UserCreationContext(); ~UserCreationContext(); string16 display_name; + int avatar_index; std::string manager_id; std::string local_user_id; // Used to identify cryptohome. std::string sync_user_id; // Used to identify user in manager's sync data. @@ -88,6 +114,7 @@ class LocallyManagedUserCreationController bool token_acquired; std::string token; bool token_succesfully_written; + CreationType creation_type; Profile* manager_profile; scoped_ptr<ManagedUserRegistrationUtility> registration_utility; }; diff --git a/chrome/browser/chromeos/login/managed/locally_managed_user_creation_screen.cc b/chrome/browser/chromeos/login/managed/locally_managed_user_creation_screen.cc index d683060132..043a53678e 100644 --- a/chrome/browser/chromeos/login/managed/locally_managed_user_creation_screen.cc +++ b/chrome/browser/chromeos/login/managed/locally_managed_user_creation_screen.cc @@ -6,15 +6,21 @@ #include "ash/desktop_background/desktop_background_controller.h" #include "ash/shell.h" +#include "base/command_line.h" +#include "base/rand_util.h" #include "base/values.h" #include "chrome/browser/chromeos/camera_detector.h" #include "chrome/browser/chromeos/login/existing_user_controller.h" #include "chrome/browser/chromeos/login/managed/locally_managed_user_creation_controller.h" #include "chrome/browser/chromeos/login/screens/error_screen.h" #include "chrome/browser/chromeos/login/screens/screen_observer.h" +#include "chrome/browser/chromeos/login/supervised_user_manager.h" #include "chrome/browser/chromeos/login/user_image.h" #include "chrome/browser/chromeos/login/user_image_manager.h" #include "chrome/browser/chromeos/login/wizard_controller.h" +#include "chrome/browser/managed_mode/managed_user_sync_service.h" +#include "chrome/browser/managed_mode/managed_user_sync_service_factory.h" +#include "chrome/common/chrome_switches.h" #include "chromeos/network/network_state.h" #include "content/public/browser/browser_thread.h" #include "grit/generated_resources.h" @@ -26,6 +32,17 @@ namespace chromeos { namespace { +// Key for (boolean) value that indicates that user already exists on device. +const char kUserExists[] = "exists"; +// Key for value that indicates why user can not be imported. +const char kUserConflict[] = "conflict"; +// User is already imported. +const char kUserConflictImported[] = "imported"; +// There is another supervised user with same name. +const char kUserConflictName[] = "name"; + +const char kAvatarURLKey[] = "avatarurl"; +const char kRandomAvatarKey[] = "randomAvatar"; const char kNameOfIntroScreen[] = "intro"; const char kNameOfNewUserParametersScreen[] = "username"; @@ -79,6 +96,7 @@ LocallyManagedUserCreationScreen::~LocallyManagedUserCreationScreen() { actor_->SetDelegate(NULL); if (image_decoder_.get()) image_decoder_->set_delegate(NULL); + NetworkPortalDetector::Get()->RemoveObserver(this); } void LocallyManagedUserCreationScreen::PrepareToShow() { @@ -97,9 +115,8 @@ void LocallyManagedUserCreationScreen::Show() { actor_->ShowIntroPage(); } - NetworkPortalDetector* detector = NetworkPortalDetector::GetInstance(); - if (detector && !on_error_screen_) - detector->AddAndFireObserver(this); + if (!on_error_screen_) + NetworkPortalDetector::Get()->AddAndFireObserver(this); on_error_screen_ = false; } @@ -142,9 +159,8 @@ void LocallyManagedUserCreationScreen::ShowInitialScreen() { void LocallyManagedUserCreationScreen::Hide() { if (actor_) actor_->Hide(); - NetworkPortalDetector* detector = NetworkPortalDetector::GetInstance(); - if (detector && !on_error_screen_) - detector->RemoveObserver(this); + if (!on_error_screen_) + NetworkPortalDetector::Get()->RemoveObserver(this); } std::string LocallyManagedUserCreationScreen::GetName() const { @@ -176,10 +192,57 @@ void LocallyManagedUserCreationScreen::CreateManagedUser( const string16& display_name, const std::string& managed_user_password) { DCHECK(controller_.get()); - controller_->SetUpCreation(display_name, managed_user_password); + int image; + if (selected_image_ == User::kExternalImageIndex) + // TODO(dzhioev): crbug/249660 + image = LocallyManagedUserCreationController::kDummyAvatarIndex; + else + image = selected_image_; + controller_->SetUpCreation(display_name, managed_user_password, image); controller_->StartCreation(); } +void LocallyManagedUserCreationScreen::ImportManagedUser( + const std::string& user_id) { + DCHECK(controller_.get()); + DCHECK(existing_users_.get()); + VLOG(1) << "Importing user " << user_id; + DictionaryValue* user_info; + if (!existing_users_->GetDictionary(user_id, &user_info)) { + LOG(ERROR) << "Can not import non-existing user " << user_id; + return; + } + string16 display_name; + std::string master_key; + std::string avatar; + bool exists; + int avatar_index = LocallyManagedUserCreationController::kDummyAvatarIndex; + user_info->GetString(ManagedUserSyncService::kName, &display_name); + user_info->GetString(ManagedUserSyncService::kMasterKey, &master_key); + user_info->GetString(ManagedUserSyncService::kChromeOsAvatar, &avatar); + user_info->GetBoolean(kUserExists, &exists); + + // We should not get here with existing user selected, so just display error. + if (exists) { + actor_->ShowErrorPage( + l10n_util::GetStringUTF16( + IDS_CREATE_LOCALLY_MANAGED_USER_GENERIC_ERROR_TITLE), + l10n_util::GetStringUTF16( + IDS_CREATE_LOCALLY_MANAGED_USER_GENERIC_ERROR), + l10n_util::GetStringUTF16( + IDS_CREATE_LOCALLY_MANAGED_USER_GENERIC_ERROR_BUTTON)); + return; + } + + ManagedUserSyncService::GetAvatarIndex(avatar, &avatar_index); + + controller_->StartImport(display_name, + std::string(), + avatar_index, + user_id, + master_key); +} + void LocallyManagedUserCreationScreen::OnManagerLoginFailure() { if (actor_) actor_->ShowManagerPasswordError(); @@ -196,7 +259,17 @@ void LocallyManagedUserCreationScreen::OnManagerFullyAuthenticated( controller_->SetManagerProfile(manager_profile); if (actor_) actor_->ShowUsernamePage(); + last_page_ = kNameOfNewUserParametersScreen; + + CommandLine* command_line = CommandLine::ForCurrentProcess(); + if (!command_line->HasSwitch(::switches::kAllowCreateExistingManagedUsers)) + return; + + ManagedUserSyncServiceFactory::GetForProfile(manager_profile)-> + GetManagedUsersAsync(base::Bind( + &LocallyManagedUserCreationScreen::OnGetManagedUsers, + weak_factory_.GetWeakPtr())); } void LocallyManagedUserCreationScreen::OnManagerCryptohomeAuthenticated() { @@ -260,6 +333,28 @@ void LocallyManagedUserCreationScreen::OnLongCreationWarning() { } } +bool LocallyManagedUserCreationScreen::FindUserByDisplayName( + const string16& display_name, + std::string *out_id) const { + if (!existing_users_.get()) + return false; + for (base::DictionaryValue::Iterator it(*existing_users_.get()); + !it.IsAtEnd(); it.Advance()) { + const base::DictionaryValue* user_info = + static_cast<const base::DictionaryValue*>(&it.value()); + string16 user_display_name; + if (user_info->GetString(ManagedUserSyncService::kName, + &user_display_name)) { + if (display_name == user_display_name) { + if (out_id) + *out_id = it.key(); + return true; + } + } + } + return false; +} + // TODO(antrim) : this is an explicit code duplications with UserImageScreen. // It should be removed by issue 251179. @@ -306,6 +401,68 @@ void LocallyManagedUserCreationScreen::OnCameraPresenceCheckDone() { } } +void LocallyManagedUserCreationScreen::OnGetManagedUsers( + const base::DictionaryValue* users) { + // Copy for passing to WebUI, contains only id, name and avatar URL. + scoped_ptr<base::ListValue> ui_users(new base::ListValue()); + SupervisedUserManager* supervised_user_manager = + UserManager::Get()->GetSupervisedUserManager(); + + // Stored copy, contains all necessary information. + existing_users_.reset(new base::DictionaryValue()); + for (base::DictionaryValue::Iterator it(*users); !it.IsAtEnd(); + it.Advance()) { + // Copy that would be stored in this class. + base::DictionaryValue* local_copy = + static_cast<base::DictionaryValue*>(it.value().DeepCopy()); + // Copy that would be passed to WebUI. It has some extra values for + // displaying, but does not contain sensitive data, such as master password. + base::DictionaryValue* ui_copy = + static_cast<base::DictionaryValue*>(new base::DictionaryValue()); + + int avatar_index = LocallyManagedUserCreationController::kDummyAvatarIndex; + std::string chromeos_avatar; + if (local_copy->GetString(ManagedUserSyncService::kChromeOsAvatar, + &chromeos_avatar) && + !chromeos_avatar.empty() && + ManagedUserSyncService::GetAvatarIndex( + chromeos_avatar, &avatar_index)) { + ui_copy->SetString(kAvatarURLKey, GetDefaultImageUrl(avatar_index)); + } else { + int i = base::RandInt(kFirstDefaultImageIndex, kDefaultImagesCount - 1); + local_copy->SetString( + ManagedUserSyncService::kChromeOsAvatar, + ManagedUserSyncService::BuildAvatarString(i)); + local_copy->SetBoolean(kRandomAvatarKey, true); + ui_copy->SetString(kAvatarURLKey, GetDefaultImageUrl(i)); + } + + local_copy->SetBoolean(kUserExists, false); + ui_copy->SetBoolean(kUserExists, false); + + string16 display_name; + local_copy->GetString(ManagedUserSyncService::kName, &display_name); + + if (supervised_user_manager->FindBySyncId(it.key())) { + local_copy->SetBoolean(kUserExists, true); + ui_copy->SetBoolean(kUserExists, true); + local_copy->SetString(kUserConflict, kUserConflictImported); + ui_copy->SetString(kUserConflict, kUserConflictImported); + } else if (supervised_user_manager->FindByDisplayName(display_name)) { + local_copy->SetBoolean(kUserExists, true); + ui_copy->SetBoolean(kUserExists, true); + local_copy->SetString(kUserConflict, kUserConflictName); + ui_copy->SetString(kUserConflict, kUserConflictName); + } + ui_copy->SetString(ManagedUserSyncService::kName, display_name); + ui_copy->SetString("id", it.key()); + + existing_users_->Set(it.key(), local_copy); + ui_users->Append(ui_copy); + } + actor_->ShowExistingManagedUsers(ui_users.get()); +} + void LocallyManagedUserCreationScreen::OnPhotoTaken( const std::string& raw_data) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); diff --git a/chrome/browser/chromeos/login/managed/locally_managed_user_creation_screen.h b/chrome/browser/chromeos/login/managed/locally_managed_user_creation_screen.h index 6e97595504..43be20b000 100644 --- a/chrome/browser/chromeos/login/managed/locally_managed_user_creation_screen.h +++ b/chrome/browser/chromeos/login/managed/locally_managed_user_creation_screen.h @@ -70,11 +70,14 @@ class LocallyManagedUserCreationScreen virtual void CreateManagedUser( const string16& display_name, const std::string& managed_user_password) OVERRIDE; + virtual void ImportManagedUser(const std::string& user_id) OVERRIDE; virtual void AuthenticateManager( const std::string& manager_id, const std::string& manager_password) OVERRIDE; virtual void AbortFlow() OVERRIDE; virtual void FinishFlow() OVERRIDE; + virtual bool FindUserByDisplayName(const string16& display_name, + std::string *out_id) const OVERRIDE; virtual void OnPageSelected(const std::string& page) OVERRIDE; // LocallyManagedUserController::StatusConsumer overrides. @@ -106,11 +109,13 @@ class LocallyManagedUserCreationScreen private: void ApplyPicture(); void OnCameraPresenceCheckDone(); + void OnGetManagedUsers(const base::DictionaryValue* users); base::WeakPtrFactory<LocallyManagedUserCreationScreen> weak_factory_; LocallyManagedUserCreationScreenHandler* actor_; scoped_ptr<LocallyManagedUserCreationController> controller_; + scoped_ptr<base::DictionaryValue> existing_users_; bool on_error_screen_; std::string last_page_; diff --git a/chrome/browser/chromeos/login/managed/managed_user_authenticator.cc b/chrome/browser/chromeos/login/managed/managed_user_authenticator.cc index fa4363cb93..5fae224d67 100644 --- a/chrome/browser/chromeos/login/managed/managed_user_authenticator.cc +++ b/chrome/browser/chromeos/login/managed/managed_user_authenticator.cc @@ -10,7 +10,7 @@ #include "chrome/browser/chromeos/boot_times_loader.h" #include "chrome/browser/chromeos/login/parallel_authenticator.h" #include "chromeos/cryptohome/async_method_caller.h" -#include "chromeos/cryptohome/cryptohome_library.h" +#include "chromeos/cryptohome/system_salt_getter.h" #include "chromeos/dbus/cryptohome_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "content/public/browser/browser_thread.h" @@ -24,9 +24,6 @@ namespace chromeos { namespace { -// Milliseconds until we timeout our attempt to hit ClientLogin. -const int kClientLoginTimeoutMs = 10000; - // Records status and calls resolver->Resolve(). void TriggerResolve(ManagedUserAuthenticator::AuthAttempt* attempt, scoped_refptr<ManagedUserAuthenticator> resolver, @@ -111,7 +108,7 @@ void ManagedUserAuthenticator::AuthenticateToMount( current_state_.reset(new ManagedUserAuthenticator::AuthAttempt( canonicalized, password, false)); - CryptohomeLibrary::Get()->GetSystemSalt( + SystemSaltGetter::Get()->GetSystemSalt( base::Bind(&Mount, current_state_.get(), scoped_refptr<ManagedUserAuthenticator>(this), @@ -126,7 +123,7 @@ void ManagedUserAuthenticator::AuthenticateToCreate( current_state_.reset(new ManagedUserAuthenticator::AuthAttempt( canonicalized, password, false)); - CryptohomeLibrary::Get()->GetSystemSalt( + SystemSaltGetter::Get()->GetSystemSalt( base::Bind(&Mount, current_state_.get(), scoped_refptr<ManagedUserAuthenticator>(this), @@ -142,7 +139,7 @@ void ManagedUserAuthenticator::AddMasterKey( current_state_.reset(new ManagedUserAuthenticator::AuthAttempt( canonicalized, password, true)); - CryptohomeLibrary::Get()->GetSystemSalt( + SystemSaltGetter::Get()->GetSystemSalt( base::Bind(&AddKey, current_state_.get(), scoped_refptr<ManagedUserAuthenticator>(this), diff --git a/chrome/browser/chromeos/login/managed/supervised_user_creation_browsertest.cc b/chrome/browser/chromeos/login/managed/supervised_user_creation_browsertest.cc index 4d3aade154..def99d6198 100644 --- a/chrome/browser/chromeos/login/managed/supervised_user_creation_browsertest.cc +++ b/chrome/browser/chromeos/login/managed/supervised_user_creation_browsertest.cc @@ -15,7 +15,7 @@ #include "chrome/browser/chromeos/login/login_manager_test.h" #include "chrome/browser/chromeos/login/startup_utils.h" #include "chrome/browser/chromeos/login/webui_login_view.h" -#include "chrome/browser/chromeos/net/network_portal_detector_stub.h" +#include "chrome/browser/chromeos/net/network_portal_detector_test_impl.h" #include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" #include "chrome/browser/managed_mode/managed_user_registration_utility.h" #include "chrome/browser/managed_mode/managed_user_registration_utility_stub.h" @@ -46,7 +46,7 @@ class SupervisedUserTest : public chromeos::LoginManagerTest { protected: SupervisedUserTest() : LoginManagerTest(true), mock_async_method_caller_(NULL), - network_portal_detector_stub_(NULL), + network_portal_detector_(NULL), registration_utility_stub_(NULL) { } @@ -70,15 +70,14 @@ class SupervisedUserTest : public chromeos::LoginManagerTest { // Setup network portal detector to return online state for both // ethernet and wifi networks. Ethernet is an active network by // default. - network_portal_detector_stub_ = - static_cast<NetworkPortalDetectorStub*>( - NetworkPortalDetector::GetInstance()); + network_portal_detector_ = new NetworkPortalDetectorTestImpl(); + NetworkPortalDetector::InitializeForTesting(network_portal_detector_); NetworkPortalDetector::CaptivePortalState online_state; online_state.status = NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE; online_state.response_code = 204; - network_portal_detector_stub_->SetDefaultNetworkPathForTesting( + network_portal_detector_->SetDefaultNetworkPathForTesting( kStubEthernetServicePath); - network_portal_detector_stub_->SetDetectionResultsForTesting( + network_portal_detector_->SetDetectionResultsForTesting( kStubEthernetServicePath, online_state); } @@ -93,6 +92,10 @@ class SupervisedUserTest : public chromeos::LoginManagerTest { LoginManagerTest::TearDown(); } + virtual void TearDownInProcessBrowserTestFixture() OVERRIDE { + NetworkPortalDetector::Shutdown(); + } + void JSEval(const std::string& script) { EXPECT_TRUE(content::ExecuteScript(web_contents(), script)); } @@ -126,7 +129,7 @@ class SupervisedUserTest : public chromeos::LoginManagerTest { protected: cryptohome::MockAsyncMethodCaller* mock_async_method_caller_; - NetworkPortalDetectorStub* network_portal_detector_stub_; + NetworkPortalDetectorTestImpl* network_portal_detector_; ManagedUserRegistrationUtilityStub* registration_utility_stub_; scoped_ptr<ScopedTestingManagedUserRegistrationUtility> scoped_utility_; diff --git a/chrome/browser/chromeos/login/mock_authenticator.cc b/chrome/browser/chromeos/login/mock_authenticator.cc index da3b14159e..696db2c344 100644 --- a/chrome/browser/chromeos/login/mock_authenticator.cc +++ b/chrome/browser/chromeos/login/mock_authenticator.cc @@ -17,7 +17,7 @@ void MockAuthenticator::AuthenticateToLogin(Profile* profile, if (expected_username_ == user_context.username && expected_password_ == user_context.password) { BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&MockAuthenticator::OnLoginSuccess, this, false)); + base::Bind(&MockAuthenticator::OnLoginSuccess, this)); return; } GoogleServiceAuthError error( @@ -31,7 +31,7 @@ void MockAuthenticator::CompleteLogin(Profile* profile, const UserContext& user_context) { CHECK_EQ(expected_username_, user_context.username); CHECK_EQ(expected_password_, user_context.password); - OnLoginSuccess(false); + OnLoginSuccess(); } void MockAuthenticator::AuthenticateToUnlock( @@ -44,9 +44,7 @@ void MockAuthenticator::LoginAsLocallyManagedUser( consumer_->OnLoginSuccess(UserContext(expected_username_, std::string(), std::string(), - user_context.username), // username_hash - false, - false); + user_context.username)); // hash } void MockAuthenticator::LoginRetailMode() { @@ -60,9 +58,7 @@ void MockAuthenticator::LoginAsPublicAccount(const std::string& username) { consumer_->OnLoginSuccess(UserContext(expected_username_, std::string(), std::string(), - expected_username_), - false, - false); + expected_username_)); } void MockAuthenticator::LoginAsKioskAccount( @@ -70,9 +66,7 @@ void MockAuthenticator::LoginAsKioskAccount( consumer_->OnLoginSuccess(UserContext(expected_username_, std::string(), std::string(), - expected_username_), - false, - false); + expected_username_)); } void MockAuthenticator::LoginOffTheRecord() { @@ -86,15 +80,13 @@ void MockAuthenticator::OnRetailModeLoginSuccess() { expected_username_)); } -void MockAuthenticator::OnLoginSuccess(bool request_pending) { +void MockAuthenticator::OnLoginSuccess() { // If we want to be more like the real thing, we could save username // in AuthenticateToLogin, but there's not much of a point. consumer_->OnLoginSuccess(UserContext(expected_username_, expected_password_, std::string(), - expected_username_), - request_pending, - false); + expected_username_)); } void MockAuthenticator::OnLoginFailure(const LoginFailure& failure) { diff --git a/chrome/browser/chromeos/login/mock_authenticator.h b/chrome/browser/chromeos/login/mock_authenticator.h index e810bcf0df..12e22e44a3 100644 --- a/chrome/browser/chromeos/login/mock_authenticator.h +++ b/chrome/browser/chromeos/login/mock_authenticator.h @@ -43,7 +43,7 @@ class MockAuthenticator : public Authenticator { virtual void OnRetailModeLoginSuccess() OVERRIDE; - virtual void OnLoginSuccess(bool request_pending) OVERRIDE; + virtual void OnLoginSuccess() OVERRIDE; virtual void OnLoginFailure(const LoginFailure& failure) OVERRIDE; diff --git a/chrome/browser/chromeos/login/mock_login_status_consumer.cc b/chrome/browser/chromeos/login/mock_login_status_consumer.cc index 6b666c8403..f648cd019b 100644 --- a/chrome/browser/chromeos/login/mock_login_status_consumer.cc +++ b/chrome/browser/chromeos/login/mock_login_status_consumer.cc @@ -39,18 +39,12 @@ void MockConsumer::OnGuestSuccessQuitAndFail() { } // static -void MockConsumer::OnSuccessQuit( - const UserContext& user_context, - bool pending_requests, - bool using_oauth) { +void MockConsumer::OnSuccessQuit(const UserContext& user_context) { base::MessageLoop::current()->Quit(); } // static -void MockConsumer::OnSuccessQuitAndFail( - const UserContext& user_context, - bool pending_requests, - bool using_oauth) { +void MockConsumer::OnSuccessQuitAndFail(const UserContext& user_context) { ADD_FAILURE() << "Login should NOT have succeeded!"; base::MessageLoop::current()->Quit(); } diff --git a/chrome/browser/chromeos/login/mock_login_status_consumer.h b/chrome/browser/chromeos/login/mock_login_status_consumer.h index 1c0237b723..1ef80dc307 100644 --- a/chrome/browser/chromeos/login/mock_login_status_consumer.h +++ b/chrome/browser/chromeos/login/mock_login_status_consumer.h @@ -19,10 +19,7 @@ class MockConsumer : public LoginStatusConsumer { MOCK_METHOD1(OnLoginFailure, void(const LoginFailure& error)); MOCK_METHOD1(OnRetailModeLoginSuccess, void( const UserContext& user_context)); - MOCK_METHOD3(OnLoginSuccess, void( - const UserContext& user_context, - bool pending_requests, - bool using_oauth)); + MOCK_METHOD1(OnLoginSuccess, void(const UserContext& user_context)); MOCK_METHOD0(OnOffTheRecordLoginSuccess, void(void)); MOCK_METHOD0(OnPasswordChangeDetected, void(void)); @@ -38,14 +35,8 @@ class MockConsumer : public LoginStatusConsumer { static void OnGuestSuccessQuitAndFail(); // Compatible with LoginStatusConsumer::OnLoginSuccess() - static void OnSuccessQuit( - const UserContext& user_context, - bool pending_requests, - bool using_oauth); - static void OnSuccessQuitAndFail( - const UserContext& user_context, - bool pending_requests, - bool using_oauth); + static void OnSuccessQuit(const UserContext& user_context); + static void OnSuccessQuitAndFail(const UserContext& user_context); // Compatible with LoginStatusConsumer::OnLoginFailure() static void OnFailQuit(const LoginFailure& error); diff --git a/chrome/browser/chromeos/login/mock_login_utils.cc b/chrome/browser/chromeos/login/mock_login_utils.cc index cb28297d13..1adc516177 100644 --- a/chrome/browser/chromeos/login/mock_login_utils.cc +++ b/chrome/browser/chromeos/login/mock_login_utils.cc @@ -19,7 +19,7 @@ void MockLoginUtils::DelegateToFake() { FakeLoginUtils* fake = fake_login_utils_.get(); ON_CALL(*this, DoBrowserLaunch(_, _)) .WillByDefault(Invoke(fake, &FakeLoginUtils::DoBrowserLaunch)); - ON_CALL(*this, PrepareProfile(_, _, _, _, _, _)) + ON_CALL(*this, PrepareProfile(_, _, _, _, _)) .WillByDefault(Invoke(fake, &FakeLoginUtils::PrepareProfile)); ON_CALL(*this, CreateAuthenticator(_)) .WillByDefault(Invoke(fake, &FakeLoginUtils::CreateAuthenticator)); diff --git a/chrome/browser/chromeos/login/mock_login_utils.h b/chrome/browser/chromeos/login/mock_login_utils.h index c01726e36d..269ac493af 100644 --- a/chrome/browser/chromeos/login/mock_login_utils.h +++ b/chrome/browser/chromeos/login/mock_login_utils.h @@ -31,9 +31,9 @@ class MockLoginUtils : public LoginUtils { virtual ~MockLoginUtils(); MOCK_METHOD2(DoBrowserLaunch, void(Profile*, LoginDisplayHost*)); - MOCK_METHOD6(PrepareProfile, + MOCK_METHOD5(PrepareProfile, void(const UserContext&, const std::string&, - bool, bool, bool, LoginUtils::Delegate*)); + bool, bool, LoginUtils::Delegate*)); MOCK_METHOD1(DelegateDeleted, void(LoginUtils::Delegate*)); MOCK_METHOD1(CompleteOffTheRecordLogin, void(const GURL&)); MOCK_METHOD1(SetFirstLoginPrefs, void(PrefService*)); diff --git a/chrome/browser/chromeos/login/mock_user_manager.cc b/chrome/browser/chromeos/login/mock_user_manager.cc index dc4af28438..59bb2737e3 100644 --- a/chrome/browser/chromeos/login/mock_user_manager.cc +++ b/chrome/browser/chromeos/login/mock_user_manager.cc @@ -4,10 +4,14 @@ #include "chrome/browser/chromeos/login/mock_user_manager.h" +#include "chrome/browser/chromeos/login/fake_supervised_user_manager.h" + namespace chromeos { -MockUserManager::MockUserManager() : user_(NULL), - user_flow_(new DefaultUserFlow()) {} +MockUserManager::MockUserManager() + : user_(NULL), + user_flow_(new DefaultUserFlow()), + supervised_user_manager_(new FakeSupervisedUserManager()) {} MockUserManager::~MockUserManager() { delete user_; @@ -53,6 +57,10 @@ UserImageManager* MockUserManager::GetUserImageManager() { return user_image_manager_.get(); } +SupervisedUserManager* MockUserManager::GetSupervisedUserManager() { + return supervised_user_manager_.get(); +} + // Creates a new User instance. void MockUserManager::SetActiveUser(const std::string& email) { delete user_; diff --git a/chrome/browser/chromeos/login/mock_user_manager.h b/chrome/browser/chromeos/login/mock_user_manager.h index dc1c2e6f3b..eb3710c39e 100644 --- a/chrome/browser/chromeos/login/mock_user_manager.h +++ b/chrome/browser/chromeos/login/mock_user_manager.h @@ -18,6 +18,8 @@ namespace chromeos { +class FakeSupervisedUserManager; + class MockUserManager : public UserManager { public: MockUserManager(); @@ -36,9 +38,6 @@ class MockUserManager : public UserManager { MOCK_METHOD1(RemoveUserFromList, void(const std::string&)); MOCK_CONST_METHOD1(IsKnownUser, bool(const std::string&)); MOCK_CONST_METHOD1(FindUser, const User*(const std::string&)); - MOCK_CONST_METHOD1(FindLocallyManagedUser, const User*(const string16&)); - MOCK_CONST_METHOD1(FindLocallyManagedUserBySyncId, - const User*(const std::string&)); MOCK_METHOD2(SaveUserOAuthStatus, void(const std::string&, User::OAuthTokenStatus)); MOCK_METHOD2(SaveUserDisplayName, void(const std::string&, @@ -75,25 +74,6 @@ class MockUserManager : public UserManager { MOCK_METHOD0(NotifyLocalStateChanged, void(void)); MOCK_METHOD2(SetUserFlow, void(const std::string&, UserFlow*)); MOCK_METHOD1(ResetUserFlow, void(const std::string&)); - MOCK_METHOD4(CreateLocallyManagedUserRecord, const User*( - const std::string&, - const std::string&, - const std::string&, - const string16&)); - MOCK_CONST_METHOD1(GetManagedUserSyncId, std::string( - const std::string& managed_user_id)); - MOCK_CONST_METHOD1(GetManagerDisplayNameForManagedUser, string16( - const std::string&)); - MOCK_CONST_METHOD1(GetManagerUserIdForManagedUser, std::string( - const std::string&)); - MOCK_CONST_METHOD1(GetManagerDisplayEmailForManagedUser, std::string( - const std::string&)); - MOCK_METHOD0(GenerateUniqueLocallyManagedUserId, std::string(void)); - MOCK_METHOD1(StartLocallyManagedUserCreationTransaction, - void(const string16&)); - MOCK_METHOD1(SetLocallyManagedUserCreationTransactionUserId, - void(const std::string&)); - MOCK_METHOD0(CommitLocallyManagedUserCreationTransaction, void(void)); MOCK_METHOD2(GetAppModeChromeClientOAuthInfo, bool(std::string*, std::string*)); @@ -116,6 +96,7 @@ class MockUserManager : public UserManager { virtual User* GetUserByProfile(Profile* profile) const OVERRIDE; virtual UserImageManager* GetUserImageManager() OVERRIDE; + virtual SupervisedUserManager* GetSupervisedUserManager() OVERRIDE; virtual UserFlow* GetCurrentUserFlow() const OVERRIDE; virtual UserFlow* GetUserFlow(const std::string&) const OVERRIDE; @@ -130,8 +111,9 @@ class MockUserManager : public UserManager { User* CreatePublicAccountUser(const std::string& email); User* user_; - scoped_ptr<MockUserImageManager> user_image_manager_; scoped_ptr<UserFlow> user_flow_; + scoped_ptr<MockUserImageManager> user_image_manager_; + scoped_ptr<FakeSupervisedUserManager> supervised_user_manager_; UserList user_list_; }; diff --git a/chrome/browser/chromeos/login/oauth2_login_verifier.cc b/chrome/browser/chromeos/login/oauth2_login_verifier.cc index 64f21d6d51..dca159f4f2 100644 --- a/chrome/browser/chromeos/login/oauth2_login_verifier.cc +++ b/chrome/browser/chromeos/login/oauth2_login_verifier.cc @@ -62,7 +62,7 @@ void OAuth2LoginVerifier::VerifyProfileTokens(Profile* profile) { // portal. const NetworkState* default_network = NetworkHandler::Get()->network_state_handler()->DefaultNetwork(); - NetworkPortalDetector* detector = NetworkPortalDetector::GetInstance(); + NetworkPortalDetector* detector = NetworkPortalDetector::Get(); NetworkPortalDetector::CaptivePortalState state = detector->GetCaptivePortalState(default_network); if (!default_network || diff --git a/chrome/browser/chromeos/login/parallel_authenticator.cc b/chrome/browser/chromeos/login/parallel_authenticator.cc index 202cd39f48..f866c5b640 100644 --- a/chrome/browser/chromeos/login/parallel_authenticator.cc +++ b/chrome/browser/chromeos/login/parallel_authenticator.cc @@ -19,7 +19,7 @@ #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/common/chrome_switches.h" #include "chromeos/cryptohome/async_method_caller.h" -#include "chromeos/cryptohome/cryptohome_library.h" +#include "chromeos/cryptohome/system_salt_getter.h" #include "chromeos/dbus/cryptohome_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/login/login_state.h" @@ -201,7 +201,6 @@ ParallelAuthenticator::ParallelAuthenticator(LoginStatusConsumer* consumer) already_reported_success_(false), owner_is_verified_(false), user_can_login_(false), - using_oauth_(true), remove_user_data_on_failure_(false), delayed_login_failure_(NULL) { } @@ -223,19 +222,11 @@ void ParallelAuthenticator::AuthenticateToLogin( // Reset the verified flag. owner_is_verified_ = false; - CryptohomeLibrary::Get()->GetSystemSalt( + SystemSaltGetter::Get()->GetSystemSalt( base::Bind(&Mount, current_state_.get(), scoped_refptr<ParallelAuthenticator>(this), cryptohome::MOUNT_FLAGS_NONE)); - // ClientLogin authentication check should happen immediately here. - // We should not try OAuthLogin check until the profile loads. - if (!using_oauth_) { - // Initiate ClientLogin-based post authentication. - current_online_.reset(new OnlineAttempt(current_state_.get(), - this)); - current_online_->Initiate(profile); - } } void ParallelAuthenticator::CompleteLogin(Profile* profile, @@ -252,29 +243,18 @@ void ParallelAuthenticator::CompleteLogin(Profile* profile, // Reset the verified flag. owner_is_verified_ = false; - CryptohomeLibrary::Get()->GetSystemSalt( + SystemSaltGetter::Get()->GetSystemSalt( base::Bind(&Mount, current_state_.get(), scoped_refptr<ParallelAuthenticator>(this), cryptohome::MOUNT_FLAGS_NONE)); - if (!using_oauth_) { - // Test automation needs to disable oauth, but that leads to other - // services not being able to fetch a token, leading to browser crashes. - // So initiate ClientLogin-based post authentication. - // TODO(xiyuan): This should not be required. - // Context: http://crbug.com/201374 - current_online_.reset(new OnlineAttempt(current_state_.get(), - this)); - current_online_->Initiate(profile); - } else { - // For login completion from extension, we just need to resolve the current - // auth attempt state, the rest of OAuth related tasks will be done in - // parallel. - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&ParallelAuthenticator::ResolveLoginCompletionStatus, this)); - } + // For login completion from extension, we just need to resolve the current + // auth attempt state, the rest of OAuth related tasks will be done in + // parallel. + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&ParallelAuthenticator::ResolveLoginCompletionStatus, this)); } void ParallelAuthenticator::AuthenticateToUnlock( @@ -285,7 +265,7 @@ void ParallelAuthenticator::AuthenticateToUnlock( user_context.password)); remove_user_data_on_failure_ = false; check_key_attempted_ = true; - CryptohomeLibrary::Get()->GetSystemSalt( + SystemSaltGetter::Get()->GetSystemSalt( base::Bind(&CheckKey, current_state_.get(), scoped_refptr<ParallelAuthenticator>(this))); @@ -302,7 +282,7 @@ void ParallelAuthenticator::LoginAsLocallyManagedUser( User::USER_TYPE_LOCALLY_MANAGED, false)); remove_user_data_on_failure_ = false; - CryptohomeLibrary::Get()->GetSystemSalt( + SystemSaltGetter::Get()->GetSystemSalt( base::Bind(&Mount, current_state_.get(), scoped_refptr<ParallelAuthenticator>(this), @@ -355,7 +335,7 @@ void ParallelAuthenticator::LoginAsPublicAccount(const std::string& username) { false)); remove_user_data_on_failure_ = false; ephemeral_mount_attempted_ = true; - CryptohomeLibrary::Get()->GetSystemSalt( + SystemSaltGetter::Get()->GetSystemSalt( base::Bind(&Mount, current_state_.get(), scoped_refptr<ParallelAuthenticator>(this), @@ -392,7 +372,7 @@ void ParallelAuthenticator::OnRetailModeLoginSuccess() { consumer_->OnRetailModeLoginSuccess(current_state_->user_context); } -void ParallelAuthenticator::OnLoginSuccess(bool request_pending) { +void ParallelAuthenticator::OnLoginSuccess() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); VLOG(1) << "Login success"; // Send notification of success @@ -406,9 +386,7 @@ void ParallelAuthenticator::OnLoginSuccess(bool request_pending) { already_reported_success_ = true; } if (consumer_) - consumer_->OnLoginSuccess(current_state_->user_context, - request_pending, - using_oauth_); + consumer_->OnLoginSuccess(current_state_->user_context); } void ParallelAuthenticator::OnOffTheRecordLoginSuccess() { @@ -455,7 +433,7 @@ void ParallelAuthenticator::RecoverEncryptedData( const std::string& old_password) { migrate_attempted_ = true; current_state_->ResetCryptohomeStatus(); - CryptohomeLibrary::Get()->GetSystemSalt( + SystemSaltGetter::Get()->GetSystemSalt( base::Bind(&Migrate, current_state_.get(), scoped_refptr<ParallelAuthenticator>(this), @@ -518,7 +496,6 @@ void ParallelAuthenticator::OnOwnershipChecked(bool is_owner) { void ParallelAuthenticator::Resolve() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - bool request_pending = false; int mount_flags = cryptohome::MOUNT_FLAGS_NONE; ParallelAuthenticator::AuthState state = ResolveState(); VLOG(1) << "Resolved state to: " << state; @@ -582,7 +559,7 @@ void ParallelAuthenticator::Resolve() { mount_flags |= cryptohome::CREATE_IF_MISSING; case RECOVER_MOUNT: current_state_->ResetCryptohomeStatus(); - CryptohomeLibrary::Get()->GetSystemSalt( + SystemSaltGetter::Get()->GetSystemSalt( base::Bind(&Mount, current_state_.get(), scoped_refptr<ParallelAuthenticator>(this), @@ -600,14 +577,6 @@ void ParallelAuthenticator::Resolve() { break; case OFFLINE_LOGIN: VLOG(2) << "Offline login"; - // Marking request_pending to false when using OAuth because OAuth related - // tasks are performed after user profile is mounted and are not performed - // by ParallelAuthenticator. - // TODO(xiyuan): Revert this when we support Gaia in lock screen and - // start to use ParallelAuthenticator's VerifyOAuth1AccessToken again. - request_pending = using_oauth_ ? - false : - !current_state_->online_complete(); // Fall through. case UNLOCK: VLOG(2) << "Unlock"; @@ -616,12 +585,11 @@ void ParallelAuthenticator::Resolve() { VLOG(2) << "Online login"; BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - base::Bind(&ParallelAuthenticator::OnLoginSuccess, this, - request_pending)); + base::Bind(&ParallelAuthenticator::OnLoginSuccess, this)); break; case DEMO_LOGIN: VLOG(2) << "Retail mode login"; - using_oauth_ = false; + current_state_->user_context.using_oauth = false; BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&ParallelAuthenticator::OnRetailModeLoginSuccess, this)); @@ -633,18 +601,16 @@ void ParallelAuthenticator::Resolve() { break; case KIOSK_ACCOUNT_LOGIN: case PUBLIC_ACCOUNT_LOGIN: - using_oauth_ = false; + current_state_->user_context.using_oauth = false; BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - base::Bind(&ParallelAuthenticator::OnLoginSuccess, this, false)); + base::Bind(&ParallelAuthenticator::OnLoginSuccess, this)); break; case LOCALLY_MANAGED_USER_LOGIN: - using_oauth_ = false; - // TODO(nkostylev): Figure out whether there's need to call - // a separate success method here. + current_state_->user_context.using_oauth = false; BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - base::Bind(&ParallelAuthenticator::OnLoginSuccess, this, false)); + base::Bind(&ParallelAuthenticator::OnLoginSuccess, this)); break; case LOGIN_FAILED: current_state_->ResetCryptohomeStatus(); @@ -761,12 +727,10 @@ ParallelAuthenticator::ResolveCryptohomeFailureState() { return FAILED_TPM; } - // Return intermediate states in the following cases: - // 1. When there is a parallel online attempt to resolve them later; - // This is the case with legacy ClientLogin flow; - // 2. When there is an online result to use; - // This is the case after user finishes Gaia login; - if (current_online_.get() || current_state_->online_complete()) { + // Return intermediate states in the following case: + // when there is an online result to use; + // This is the case after user finishes Gaia login; + if (current_state_->online_complete()) { if (current_state_->cryptohome_code() == cryptohome::MOUNT_ERROR_KEY_FAILURE) { // If we tried a mount but they used the wrong key, we may need to diff --git a/chrome/browser/chromeos/login/parallel_authenticator.h b/chrome/browser/chromeos/login/parallel_authenticator.h index 24a6ed1223..05e887e419 100644 --- a/chrome/browser/chromeos/login/parallel_authenticator.h +++ b/chrome/browser/chromeos/login/parallel_authenticator.h @@ -15,7 +15,6 @@ #include "chrome/browser/chromeos/login/auth_attempt_state.h" #include "chrome/browser/chromeos/login/auth_attempt_state_resolver.h" #include "chrome/browser/chromeos/login/authenticator.h" -#include "chrome/browser/chromeos/login/online_attempt.h" #include "chrome/browser/chromeos/login/test_attempt_state.h" #include "chrome/browser/chromeos/settings/device_settings_service.h" #include "google_apis/gaia/gaia_auth_consumer.h" @@ -111,8 +110,7 @@ class ParallelAuthenticator : public Authenticator, // user_context. This will never contact the server even if it's online. // The auth result is sent to LoginStatusConsumer in a same way as // AuthenticateToLogin does. - virtual void AuthenticateToUnlock( - const UserContext& user_context) OVERRIDE; + virtual void AuthenticateToUnlock(const UserContext& user_context) OVERRIDE; // Initiates locally managed user login. // Creates cryptohome if missing or mounts existing one and @@ -141,7 +139,7 @@ class ParallelAuthenticator : public Authenticator, // These methods must be called on the UI thread, as they make DBus calls // and also call back to the login UI. virtual void OnRetailModeLoginSuccess() OVERRIDE; - virtual void OnLoginSuccess(bool request_pending) OVERRIDE; + virtual void OnLoginSuccess() OVERRIDE; virtual void OnLoginFailure(const LoginFailure& error) OVERRIDE; virtual void RecoverEncryptedData( const std::string& old_password) OVERRIDE; @@ -200,21 +198,11 @@ class ParallelAuthenticator : public Authenticator, // Must be called on the IO thread. AuthState ResolveOnlineSuccessState(AuthState offline_state); - // Used to disable oauth, used for testing. - void set_using_oauth(bool value) { - using_oauth_ = value; - } - // Used for testing. void set_attempt_state(TestAttemptState* new_state) { // takes ownership. current_state_.reset(new_state); } - // Sets an online attempt for testing. - void set_online_attempt(OnlineAttempt* attempt) { - current_online_.reset(attempt); - } - // Used for testing to set the expected state of an owner check. void SetOwnerState(bool owner_check_finished, bool check_result); @@ -231,7 +219,6 @@ class ParallelAuthenticator : public Authenticator, void ResolveLoginCompletionStatus(); scoped_ptr<AuthAttemptState> current_state_; - scoped_ptr<OnlineAttempt> current_online_; bool migrate_attempted_; bool remove_attempted_; bool resync_attempted_; @@ -250,9 +237,6 @@ class ParallelAuthenticator : public Authenticator, bool owner_is_verified_; bool user_can_login_; - // True if we use OAuth-based authentication flow. - bool using_oauth_; - // Flag indicating to delete the user's cryptohome the login fails. bool remove_user_data_on_failure_; diff --git a/chrome/browser/chromeos/login/parallel_authenticator_unittest.cc b/chrome/browser/chromeos/login/parallel_authenticator_unittest.cc index fcbb9c7188..8b8ab4e8fb 100644 --- a/chrome/browser/chromeos/login/parallel_authenticator_unittest.cc +++ b/chrome/browser/chromeos/login/parallel_authenticator_unittest.cc @@ -24,10 +24,10 @@ #include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" #include "chrome/test/base/testing_profile.h" #include "chromeos/chromeos_switches.h" -#include "chromeos/cryptohome/cryptohome_library.h" #include "chromeos/cryptohome/mock_async_method_caller.h" +#include "chromeos/cryptohome/system_salt_getter.h" #include "chromeos/dbus/fake_cryptohome_client.h" -#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" +#include "chromeos/dbus/fake_dbus_thread_manager.h" #include "content/public/test/test_browser_thread_bundle.h" #include "google_apis/gaia/mock_url_fetcher_factory.h" #include "net/base/net_errors.h" @@ -43,14 +43,6 @@ using ::testing::_; namespace chromeos { -class TestOnlineAttempt : public OnlineAttempt { - public: - TestOnlineAttempt(AuthAttemptState* state, - AuthAttemptStateResolver* resolver) - : OnlineAttempt(state, resolver) { - } -}; - class ParallelAuthenticatorTest : public testing::Test { public: ParallelAuthenticatorTest() @@ -58,11 +50,11 @@ class ParallelAuthenticatorTest : public testing::Test { password_("fakepass"), hash_ascii_(ParallelAuthenticator::HashPassword( password_, - CryptohomeLibrary::ConvertRawSaltToHexString( + SystemSaltGetter::ConvertRawSaltToHexString( FakeCryptohomeClient::GetStubSystemSalt()))), user_manager_enabler_(new MockUserManager), mock_caller_(NULL), - mock_dbus_thread_manager_(new MockDBusThreadManagerWithoutGMock) { + fake_dbus_thread_manager_(new FakeDBusThreadManager) { } virtual ~ParallelAuthenticatorTest() { @@ -75,9 +67,9 @@ class ParallelAuthenticatorTest : public testing::Test { mock_caller_ = new cryptohome::MockAsyncMethodCaller; cryptohome::AsyncMethodCaller::InitializeForTesting(mock_caller_); - // Ownership of mock_dbus_thread_manager_ is taken. - DBusThreadManager::InitializeForTesting(mock_dbus_thread_manager_); - CryptohomeLibrary::Initialize(); + // Ownership of fake_dbus_thread_manager_ is taken. + DBusThreadManager::InitializeForTesting(fake_dbus_thread_manager_); + SystemSaltGetter::Initialize(); auth_ = new ParallelAuthenticator(&consumer_); state_.reset(new TestAttemptState(UserContext(username_, @@ -91,7 +83,7 @@ class ParallelAuthenticatorTest : public testing::Test { // Tears down the test fixture. virtual void TearDown() { - CryptohomeLibrary::Shutdown(); + SystemSaltGetter::Shutdown(); DBusThreadManager::Shutdown(); cryptohome::AsyncMethodCaller::Shutdown(); @@ -124,7 +116,7 @@ class ParallelAuthenticatorTest : public testing::Test { // Allow test to fail and exit gracefully, even if OnLoginSuccess() // wasn't supposed to happen. void FailOnLoginSuccess() { - ON_CALL(consumer_, OnLoginSuccess(_, _, _)) + ON_CALL(consumer_, OnLoginSuccess(_)) .WillByDefault(Invoke(MockConsumer::OnSuccessQuitAndFail)); } @@ -154,9 +146,8 @@ class ParallelAuthenticatorTest : public testing::Test { EXPECT_CALL(consumer_, OnLoginSuccess(UserContext(username, password, std::string(), - username_hash_), - pending, - true /* using_oauth */)) + username_hash_, + true /* using_oauth */))) .WillOnce(Invoke(MockConsumer::OnSuccessQuit)) .RetiresOnSaturation(); } @@ -192,10 +183,6 @@ class ParallelAuthenticatorTest : public testing::Test { auth_->SetOwnerState(owner_check_finished, check_result); } - void FakeOnlineAttempt() { - auth_->set_online_attempt(new TestOnlineAttempt(state_.get(), auth_.get())); - } - content::TestBrowserThreadBundle thread_bundle_; std::string username_; @@ -213,20 +200,20 @@ class ParallelAuthenticatorTest : public testing::Test { MockConsumer consumer_; scoped_refptr<ParallelAuthenticator> auth_; scoped_ptr<TestAttemptState> state_; - MockDBusThreadManagerWithoutGMock* mock_dbus_thread_manager_; + FakeDBusThreadManager* fake_dbus_thread_manager_; }; TEST_F(ParallelAuthenticatorTest, OnLoginSuccess) { EXPECT_CALL(consumer_, OnLoginSuccess(UserContext(username_, password_, std::string(), - username_hash_), - false, true /* using oauth */)) + username_hash_, + true /* using oauth */))) .Times(1) .RetiresOnSaturation(); SetAttemptState(auth_.get(), state_.release()); - auth_->OnLoginSuccess(false); + auth_->OnLoginSuccess(); } TEST_F(ParallelAuthenticatorTest, OnPasswordChangeDetected) { @@ -242,17 +229,6 @@ TEST_F(ParallelAuthenticatorTest, ResolveNothingDone) { SetAndResolveState(auth_.get(), state_.release())); } -TEST_F(ParallelAuthenticatorTest, ResolvePossiblePwChange) { - // Set a fake online attempt so that we return intermediate cryptohome state. - FakeOnlineAttempt(); - - // Set up state as though a cryptohome mount attempt has occurred - // and been rejected. - state_->PresetCryptohomeStatus(false, cryptohome::MOUNT_ERROR_KEY_FAILURE); - - EXPECT_EQ(ParallelAuthenticator::POSSIBLE_PW_CHANGE, - SetAndResolveState(auth_.get(), state_.release())); -} TEST_F(ParallelAuthenticatorTest, ResolvePossiblePwChangeToFailedMount) { // Set up state as though a cryptohome mount attempt has occurred @@ -314,7 +290,7 @@ TEST_F(ParallelAuthenticatorTest, ResolveOwnerNeededFailedMount) { ExpectLoginFailure(failure); FakeCryptohomeClient* fake_cryptohome_client = - mock_dbus_thread_manager_->fake_cryptohome_client(); + fake_dbus_thread_manager_->fake_cryptohome_client(); fake_cryptohome_client->set_unmount_result(true); CrosSettingsProvider* device_settings_provider; @@ -539,19 +515,6 @@ TEST_F(ParallelAuthenticatorTest, DriveDataRecoverButFail) { base::MessageLoop::current()->Run(); } -TEST_F(ParallelAuthenticatorTest, ResolveNoMount) { - // Set a fake online attempt so that we return intermediate cryptohome state. - FakeOnlineAttempt(); - - // Set up state as though a cryptohome mount attempt has occurred - // and been rejected because the user doesn't exist. - state_->PresetCryptohomeStatus(false, - cryptohome::MOUNT_ERROR_USER_DOES_NOT_EXIST); - - EXPECT_EQ(ParallelAuthenticator::NO_MOUNT, - SetAndResolveState(auth_.get(), state_.release())); -} - TEST_F(ParallelAuthenticatorTest, ResolveNoMountToFailedMount) { // Set up state as though a cryptohome mount attempt has occurred // and been rejected because the user doesn't exist. diff --git a/chrome/browser/chromeos/login/screen_locker.cc b/chrome/browser/chromeos/login/screen_locker.cc index 5410f80c04..6fbdd0d83b 100644 --- a/chrome/browser/chromeos/login/screen_locker.cc +++ b/chrome/browser/chromeos/login/screen_locker.cc @@ -186,10 +186,7 @@ void ScreenLocker::OnLoginFailure(const LoginFailure& error) { login_status_consumer_->OnLoginFailure(error); } -void ScreenLocker::OnLoginSuccess( - const UserContext& user_context, - bool pending_requests, - bool using_oauth) { +void ScreenLocker::OnLoginSuccess(const UserContext& user_context) { incorrect_passwords_count_ = 0; if (authentication_start_time_.is_null()) { if (!user_context.username.empty()) @@ -227,9 +224,7 @@ void ScreenLocker::OnLoginSuccess( } authentication_capture_.reset(new AuthenticationParametersCapture()); - authentication_capture_->username = user_context.username; - authentication_capture_->pending_requests = pending_requests; - authentication_capture_->using_oauth = using_oauth; + authentication_capture_->user_context = user_context; // Add guard for case when something get broken in call chain to unlock // for sure. @@ -251,11 +246,11 @@ void ScreenLocker::UnlockOnLoginSuccess() { if (login_status_consumer_) { login_status_consumer_->OnLoginSuccess( - UserContext(authentication_capture_->username, - std::string(), // password - std::string()), // auth_code - authentication_capture_->pending_requests, - authentication_capture_->using_oauth); + UserContext(authentication_capture_->user_context.username, + authentication_capture_->user_context.password, + authentication_capture_->user_context.auth_code, + authentication_capture_->user_context.username_hash, + authentication_capture_->user_context.using_oauth)); } authentication_capture_.reset(); weak_factory_.InvalidateWeakPtrs(); diff --git a/chrome/browser/chromeos/login/screen_locker.h b/chrome/browser/chromeos/login/screen_locker.h index aabe07d2b2..f73a365d23 100644 --- a/chrome/browser/chromeos/login/screen_locker.h +++ b/chrome/browser/chromeos/login/screen_locker.h @@ -52,9 +52,7 @@ class ScreenLocker : public LoginStatusConsumer { // LoginStatusConsumer implements: virtual void OnLoginFailure(const chromeos::LoginFailure& error) OVERRIDE; - virtual void OnLoginSuccess(const UserContext& user_context, - bool pending_requests, - bool using_oauth) OVERRIDE; + virtual void OnLoginSuccess(const UserContext& user_context) OVERRIDE; // Does actual unlocking once authentication is successful and all blocking // animations are done. @@ -120,9 +118,7 @@ class ScreenLocker : public LoginStatusConsumer { friend class ScreenLockerDelegate; struct AuthenticationParametersCapture { - std::string username; - bool pending_requests; - bool using_oauth; + UserContext user_context; }; virtual ~ScreenLocker(); diff --git a/chrome/browser/chromeos/login/screen_locker_browsertest.cc b/chrome/browser/chromeos/login/screen_locker_browsertest.cc index df3c0a70ef..0b3ad8bf20 100644 --- a/chrome/browser/chromeos/login/screen_locker_browsertest.cc +++ b/chrome/browser/chromeos/login/screen_locker_browsertest.cc @@ -19,8 +19,8 @@ #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" #include "chromeos/chromeos_switches.h" +#include "chromeos/dbus/fake_dbus_thread_manager.h" #include "chromeos/dbus/fake_session_manager_client.h" -#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" #include "content/public/browser/notification_service.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -116,12 +116,12 @@ class ScreenLockerTest : public InProcessBrowserTest { private: virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { - MockDBusThreadManagerWithoutGMock* mock_dbus_thread_manager = - new MockDBusThreadManagerWithoutGMock; - DBusThreadManager::InitializeForTesting(mock_dbus_thread_manager); + FakeDBusThreadManager* fake_dbus_thread_manager = + new FakeDBusThreadManager; + DBusThreadManager::InitializeForTesting(fake_dbus_thread_manager); InProcessBrowserTest::SetUpInProcessBrowserTestFixture(); fake_session_manager_client_ = - mock_dbus_thread_manager->fake_session_manager_client(); + fake_dbus_thread_manager->fake_session_manager_client(); zero_duration_mode_.reset(new ui::ScopedAnimationDurationScaleMode( ui::ScopedAnimationDurationScaleMode::ZERO_DURATION)); } diff --git a/chrome/browser/chromeos/login/screen_locker_tester.cc b/chrome/browser/chromeos/login/screen_locker_tester.cc index 33ce5b124b..928931efe4 100644 --- a/chrome/browser/chromeos/login/screen_locker_tester.cc +++ b/chrome/browser/chromeos/login/screen_locker_tester.cc @@ -44,9 +44,7 @@ class LoginAttemptObserver : public chromeos::LoginStatusConsumer { } virtual void OnLoginSuccess( - const chromeos::UserContext& credentials, - bool pending_requests, - bool using_oauth) OVERRIDE { + const chromeos::UserContext& credentials) OVERRIDE { LoginAttempted(); } diff --git a/chrome/browser/chromeos/login/screens/network_screen_browsertest.cc b/chrome/browser/chromeos/login/screens/network_screen_browsertest.cc index a69145b72b..94af181b81 100644 --- a/chrome/browser/chromeos/login/screens/network_screen_browsertest.cc +++ b/chrome/browser/chromeos/login/screens/network_screen_browsertest.cc @@ -10,8 +10,8 @@ #include "chrome/browser/chromeos/login/wizard_controller.h" #include "chrome/browser/chromeos/login/wizard_in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" +#include "chromeos/dbus/fake_dbus_thread_manager.h" #include "chromeos/dbus/fake_session_manager_client.h" -#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" #include "content/public/test/test_utils.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -53,11 +53,11 @@ class NetworkScreenTest : public WizardInProcessBrowserTest { virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { WizardInProcessBrowserTest::SetUpInProcessBrowserTestFixture(); - MockDBusThreadManagerWithoutGMock* mock_dbus_thread_manager = - new MockDBusThreadManagerWithoutGMock; - DBusThreadManager::InitializeForTesting(mock_dbus_thread_manager); + FakeDBusThreadManager* fake_dbus_thread_manager = + new FakeDBusThreadManager; + DBusThreadManager::InitializeForTesting(fake_dbus_thread_manager); fake_session_manager_client_ = - mock_dbus_thread_manager->fake_session_manager_client(); + fake_dbus_thread_manager->fake_session_manager_client(); } virtual void SetUpOnMainThread() OVERRIDE { diff --git a/chrome/browser/chromeos/login/screens/update_screen.cc b/chrome/browser/chromeos/login/screens/update_screen.cc index 4469c120e0..8e4b07a7a4 100644 --- a/chrome/browser/chromeos/login/screens/update_screen.cc +++ b/chrome/browser/chromeos/login/screens/update_screen.cc @@ -41,19 +41,12 @@ const int kProgressComplete = 100; // Defines what part of update progress does download part takes. const int kDownloadProgressIncrement = 60; -// Considering 10px shadow from each side. -const int kUpdateScreenWidth = 580; -const int kUpdateScreenHeight = 305; - const char kUpdateDeadlineFile[] = "/tmp/update-check-response-deadline"; // Minimum timestep between two consecutive measurements for the // download rate. const base::TimeDelta kMinTimeStep = base::TimeDelta::FromSeconds(1); -// Minimum allowed progress between two consecutive ETAs. -const double kMinProgressStep = 1e-3; - // Smooth factor that is used for the average downloading speed // estimation. // avg_speed = smooth_factor * cur_speed + (1.0 - smooth_factor) * avg_speed. @@ -122,6 +115,7 @@ UpdateScreen::UpdateScreen( UpdateScreen::~UpdateScreen() { DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this); + NetworkPortalDetector::Get()->RemoveObserver(this); GetInstanceSet().erase(this); if (actor_) actor_->SetDelegate(NULL); @@ -258,12 +252,11 @@ void UpdateScreen::OnPortalDetectionCompleted( state.status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE) && is_first_detection_notification_) { is_first_detection_notification_ = false; - NetworkPortalDetector* detector = NetworkPortalDetector::GetInstance(); base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind( base::IgnoreResult(&NetworkPortalDetector::StartDetectionIfIdle), - base::Unretained(detector))); + base::Unretained(NetworkPortalDetector::Get()))); return; } is_first_detection_notification_ = false; @@ -289,12 +282,10 @@ void UpdateScreen::OnPortalDetectionCompleted( } void UpdateScreen::StartNetworkCheck() { - NetworkPortalDetector* detector = NetworkPortalDetector::GetInstance(); - // If portal detector is enabled and portal detection before AU is // allowed, initiate network state check. Otherwise, directly // proceed to update. - if (!NetworkPortalDetector::IsEnabledInCommandLine() || !detector || + if (!NetworkPortalDetector::Get()->IsEnabled() || !IsBlockingUpdateEnabledInCommandLine()) { StartUpdateCheck(); return; @@ -302,7 +293,7 @@ void UpdateScreen::StartNetworkCheck() { state_ = STATE_FIRST_PORTAL_CHECK; is_first_detection_notification_ = true; is_first_portal_notification_ = true; - detector->AddAndFireObserver(this); + NetworkPortalDetector::Get()->AddAndFireObserver(this); } void UpdateScreen::CancelUpdate() { @@ -335,8 +326,7 @@ void UpdateScreen::PrepareToShow() { void UpdateScreen::ExitUpdate(UpdateScreen::ExitReason reason) { DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this); - if (NetworkPortalDetector::GetInstance()) - NetworkPortalDetector::GetInstance()->RemoveObserver(this); + NetworkPortalDetector::Get()->RemoveObserver(this); switch (reason) { case REASON_UPDATE_CANCELED: @@ -488,9 +478,7 @@ ErrorScreen* UpdateScreen::GetErrorScreen() { } void UpdateScreen::StartUpdateCheck() { - NetworkPortalDetector* detector = NetworkPortalDetector::GetInstance(); - if (detector) - detector->RemoveObserver(this); + NetworkPortalDetector::Get()->RemoveObserver(this); if (state_ == STATE_ERROR) HideErrorMessage(); state_ = STATE_UPDATE; diff --git a/chrome/browser/chromeos/login/screens/update_screen_browsertest.cc b/chrome/browser/chromeos/login/screens/update_screen_browsertest.cc index 0935de6f9b..55915ab00c 100644 --- a/chrome/browser/chromeos/login/screens/update_screen_browsertest.cc +++ b/chrome/browser/chromeos/login/screens/update_screen_browsertest.cc @@ -10,10 +10,10 @@ #include "chrome/browser/chromeos/login/wizard_controller.h" #include "chrome/browser/chromeos/login/wizard_in_process_browser_test.h" #include "chrome/browser/chromeos/net/network_portal_detector.h" -#include "chrome/browser/chromeos/net/network_portal_detector_stub.h" +#include "chrome/browser/chromeos/net/network_portal_detector_test_impl.h" #include "chromeos/chromeos_switches.h" +#include "chromeos/dbus/fake_dbus_thread_manager.h" #include "chromeos/dbus/fake_update_engine_client.h" -#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -37,25 +37,24 @@ class UpdateScreenTest : public WizardInProcessBrowserTest { public: UpdateScreenTest() : WizardInProcessBrowserTest("update"), fake_update_engine_client_(NULL), - network_portal_detector_stub_(NULL) { + network_portal_detector_(NULL) { } protected: virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { - MockDBusThreadManagerWithoutGMock* mock_dbus_thread_manager = - new MockDBusThreadManagerWithoutGMock; - DBusThreadManager::InitializeForTesting(mock_dbus_thread_manager); + FakeDBusThreadManager* fake_dbus_thread_manager = + new FakeDBusThreadManager; + DBusThreadManager::InitializeForTesting(fake_dbus_thread_manager); WizardInProcessBrowserTest::SetUpInProcessBrowserTestFixture(); fake_update_engine_client_ - = mock_dbus_thread_manager->fake_update_engine_client(); + = fake_dbus_thread_manager->fake_update_engine_client(); // Setup network portal detector to return online state for both // ethernet and wifi networks. Ethernet is an active network by // default. - network_portal_detector_stub_ = - static_cast<NetworkPortalDetectorStub*>( - NetworkPortalDetector::GetInstance()); + network_portal_detector_ = new NetworkPortalDetectorTestImpl(); + NetworkPortalDetector::InitializeForTesting(network_portal_detector_); NetworkPortalDetector::CaptivePortalState online_state; online_state.status = NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE; online_state.response_code = 204; @@ -87,27 +86,28 @@ class UpdateScreenTest : public WizardInProcessBrowserTest { } virtual void TearDownInProcessBrowserTestFixture() OVERRIDE { + NetworkPortalDetector::Shutdown(); WizardInProcessBrowserTest::TearDownInProcessBrowserTestFixture(); DBusThreadManager::Shutdown(); } void SetDefaultNetworkPath(const std::string& service_path) { - DCHECK(network_portal_detector_stub_); - network_portal_detector_stub_->SetDefaultNetworkPathForTesting( + DCHECK(network_portal_detector_); + network_portal_detector_->SetDefaultNetworkPathForTesting( service_path); } void SetDetectionResults( const std::string& service_path, const NetworkPortalDetector::CaptivePortalState& state) { - DCHECK(network_portal_detector_stub_); - network_portal_detector_stub_->SetDetectionResultsForTesting(service_path, - state); + DCHECK(network_portal_detector_); + network_portal_detector_->SetDetectionResultsForTesting(service_path, + state); } void NotifyPortalDetectionCompleted() { - DCHECK(network_portal_detector_stub_); - network_portal_detector_stub_->NotifyObserversForTesting(); + DCHECK(network_portal_detector_); + network_portal_detector_->NotifyObserversForTesting(); } FakeUpdateEngineClient* fake_update_engine_client_; @@ -115,7 +115,7 @@ class UpdateScreenTest : public WizardInProcessBrowserTest { scoped_ptr<MockErrorScreenActor> mock_error_screen_actor_; scoped_ptr<MockErrorScreen> mock_error_screen_; UpdateScreen* update_screen_; - NetworkPortalDetectorStub* network_portal_detector_stub_; + NetworkPortalDetectorTestImpl* network_portal_detector_; private: DISALLOW_COPY_AND_ASSIGN(UpdateScreenTest); diff --git a/chrome/browser/chromeos/login/startup_utils.cc b/chrome/browser/chromeos/login/startup_utils.cc index a32c9877b4..229d38b3b7 100644 --- a/chrome/browser/chromeos/login/startup_utils.cc +++ b/chrome/browser/chromeos/login/startup_utils.cc @@ -30,10 +30,6 @@ const char kOobeComplete[] = "OobeComplete"; // A boolean pref of the device registered flag (second part after first login). const char kDeviceRegistered[] = "DeviceRegistered"; -// Time in seconds that we wait for the device to reboot. -// If reboot didn't happen, ask user to reboot device manually. -const int kWaitForRebootTimeSec = 3; - // Saves boolean "Local State" preference and forces its persistence to disk. void SaveBoolPreferenceForced(const char* pref_name, bool value) { PrefService* prefs = g_browser_process->local_state(); diff --git a/chrome/browser/chromeos/login/supervised_user_manager.h b/chrome/browser/chromeos/login/supervised_user_manager.h new file mode 100644 index 0000000000..880c989c75 --- /dev/null +++ b/chrome/browser/chromeos/login/supervised_user_manager.h @@ -0,0 +1,88 @@ +// Copyright 2013 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_CHROMEOS_LOGIN_SUPERVISED_USER_MANAGER_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_SUPERVISED_USER_MANAGER_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/strings/string16.h" + +class PrefRegistrySimple; + +namespace chromeos { + +class User; + +// Base class for SupervisedUserManagerImpl - provides a mechanism for getting +// and setting specific values for supervised users, as well as additional +// lookup methods that make sense only for supervised users. +class SupervisedUserManager { + public: + // Registers user manager preferences. + static void RegisterPrefs(PrefRegistrySimple* registry); + + SupervisedUserManager() {} + virtual ~SupervisedUserManager() {} + + // Creates supervised user with given |display_name| and |local_user_id| + // and persists that to user list. Also links this user identified by + // |sync_user_id| to manager with a |manager_id|. + // Returns created user, or existing user if there already + // was locally managed user with such display name. + // TODO(antrim): Refactor into a single struct to have only 1 getter. + virtual const User* CreateUserRecord( + const std::string& manager_id, + const std::string& local_user_id, + const std::string& sync_user_id, + const string16& display_name) = 0; + + // Generates unique user ID for supervised user. + virtual std::string GenerateUserId() = 0; + + // Returns the supervised user with the given |display_name| if found in + // the persistent list. Returns |NULL| otherwise. + virtual const User* FindByDisplayName(const string16& display_name) const = 0; + + // Returns the supervised user with the given |sync_id| if found in + // the persistent list. Returns |NULL| otherwise. + virtual const User* FindBySyncId(const std::string& sync_id) const = 0; + + // Returns sync_user_id for supervised user with |user_id| or empty string if + // such user is not found or it doesn't have user_id defined. + virtual std::string GetUserSyncId(const std::string& user_id) const = 0; + + // Returns the display name for manager of user |user_id| if it is known + // (was previously set by a |SaveUserDisplayName| call). + // Otherwise, returns a manager id. + virtual string16 GetManagerDisplayName(const std::string& user_id) const = 0; + + // Returns the user id for manager of user |user_id| if it is known (user is + // actually a managed user). + // Otherwise, returns an empty string. + virtual std::string GetManagerUserId(const std::string& user_id) const = 0; + + // Returns the display email for manager of user |user_id| if it is known + // (user is actually a managed user). + // Otherwise, returns an empty string. + virtual std::string GetManagerDisplayEmail(const std::string& user_id) + const = 0; + + // Create a record about starting supervised user creation transaction. + virtual void StartCreationTransaction(const string16& display_name) = 0; + + // Add user id to supervised user creation transaction record. + virtual void SetCreationTransactionUserId(const std::string& user_id) = 0; + + // Remove locally managed user creation transaction record. + virtual void CommitCreationTransaction() = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(SupervisedUserManager); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_SUPERVISED_USER_MANAGER_H_ diff --git a/chrome/browser/chromeos/login/supervised_user_manager_impl.cc b/chrome/browser/chromeos/login/supervised_user_manager_impl.cc new file mode 100644 index 0000000000..d5533ec9ea --- /dev/null +++ b/chrome/browser/chromeos/login/supervised_user_manager_impl.cc @@ -0,0 +1,332 @@ +// Copyright 2013 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/chromeos/login/supervised_user_manager_impl.h" + +#include "base/prefs/pref_registry_simple.h" +#include "base/prefs/pref_service.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/chromeos/login/user_manager_impl.h" +#include "chrome/browser/prefs/scoped_user_pref_update.h" +#include "chromeos/settings/cros_settings_names.h" +#include "content/public/browser/browser_thread.h" +#include "google_apis/gaia/gaia_auth_util.h" + +using content::BrowserThread; + +namespace { + +// A map from locally managed user local user id to sync user id. +const char kManagedUserSyncId[] = + "ManagedUserSyncId"; + +// A map from locally managed user id to manager user id. +const char kManagedUserManagers[] = + "ManagedUserManagers"; + +// A map from locally managed user id to manager display name. +const char kManagedUserManagerNames[] = + "ManagedUserManagerNames"; + +// A map from locally managed user id to manager display e-mail. +const char kManagedUserManagerDisplayEmails[] = + "ManagedUserManagerDisplayEmails"; + +// A vector pref of the locally managed accounts defined on this device, that +// had not logged in yet. +const char kLocallyManagedUsersFirstRun[] = "LocallyManagedUsersFirstRun"; + +// A pref of the next id for locally managed users generation. +const char kLocallyManagedUsersNextId[] = + "LocallyManagedUsersNextId"; + +// A pref of the next id for locally managed users generation. +const char kLocallyManagedUserCreationTransactionDisplayName[] = + "LocallyManagedUserCreationTransactionDisplayName"; + +// A pref of the next id for locally managed users generation. +const char kLocallyManagedUserCreationTransactionUserId[] = + "LocallyManagedUserCreationTransactionUserId"; + +} // namespace + +namespace chromeos { + +// static +void SupervisedUserManager::RegisterPrefs(PrefRegistrySimple* registry) { + registry->RegisterListPref(kLocallyManagedUsersFirstRun); + registry->RegisterIntegerPref(kLocallyManagedUsersNextId, 0); + registry->RegisterStringPref( + kLocallyManagedUserCreationTransactionDisplayName, ""); + registry->RegisterStringPref( + kLocallyManagedUserCreationTransactionUserId, ""); + registry->RegisterDictionaryPref(kManagedUserSyncId); + registry->RegisterDictionaryPref(kManagedUserManagers); + registry->RegisterDictionaryPref(kManagedUserManagerNames); + registry->RegisterDictionaryPref(kManagedUserManagerDisplayEmails); +} + +SupervisedUserManagerImpl::SupervisedUserManagerImpl(UserManagerImpl* owner) + : owner_(owner), + cros_settings_(CrosSettings::Get()) { + // SupervisedUserManager instance should be used only on UI thread. + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); +} + +SupervisedUserManagerImpl::~SupervisedUserManagerImpl() { +} + +std::string SupervisedUserManagerImpl::GenerateUserId() { + int counter = g_browser_process->local_state()-> + GetInteger(kLocallyManagedUsersNextId); + std::string id; + bool user_exists; + do { + id = base::StringPrintf("%d@%s", counter, + UserManager::kLocallyManagedUserDomain); + counter++; + user_exists = (NULL != owner_->FindUser(id)); + DCHECK(!user_exists); + if (user_exists) { + LOG(ERROR) << "Supervised user with id " << id << " already exists."; + } + } while (user_exists); + + g_browser_process->local_state()-> + SetInteger(kLocallyManagedUsersNextId, counter); + + g_browser_process->local_state()->CommitPendingWrite(); + return id; +} + +const User* SupervisedUserManagerImpl::CreateUserRecord( + const std::string& manager_id, + const std::string& local_user_id, + const std::string& sync_user_id, + const string16& display_name) { + const User* user = FindByDisplayName(display_name); + DCHECK(!user); + if (user) + return user; + const User* manager = owner_->FindUser(manager_id); + CHECK(manager); + + PrefService* local_state = g_browser_process->local_state(); + + User* new_user = User::CreateLocallyManagedUser(local_user_id); + + owner_->AddUserRecord(new_user); + + ListPrefUpdate prefs_new_users_update(local_state, + kLocallyManagedUsersFirstRun); + DictionaryPrefUpdate sync_id_update(local_state, kManagedUserSyncId); + DictionaryPrefUpdate manager_update(local_state, kManagedUserManagers); + DictionaryPrefUpdate manager_name_update(local_state, + kManagedUserManagerNames); + DictionaryPrefUpdate manager_email_update(local_state, + kManagedUserManagerDisplayEmails); + + prefs_new_users_update->Insert(0, new base::StringValue(local_user_id)); + + sync_id_update->SetWithoutPathExpansion(local_user_id, + new base::StringValue(sync_user_id)); + manager_update->SetWithoutPathExpansion(local_user_id, + new base::StringValue(manager->email())); + manager_name_update->SetWithoutPathExpansion(local_user_id, + new base::StringValue(manager->GetDisplayName())); + manager_email_update->SetWithoutPathExpansion(local_user_id, + new base::StringValue(manager->display_email())); + + owner_->SaveUserDisplayName(local_user_id, display_name); + + g_browser_process->local_state()->CommitPendingWrite(); + return new_user; +} + +std::string SupervisedUserManagerImpl::GetUserSyncId(const std::string& user_id) + const { + PrefService* local_state = g_browser_process->local_state(); + const DictionaryValue* sync_ids = + local_state->GetDictionary(kManagedUserSyncId); + std::string result; + sync_ids->GetStringWithoutPathExpansion(user_id, &result); + return result; +} + +string16 SupervisedUserManagerImpl::GetManagerDisplayName( + const std::string& user_id) const { + PrefService* local_state = g_browser_process->local_state(); + const DictionaryValue* manager_names = + local_state->GetDictionary(kManagedUserManagerNames); + string16 result; + if (manager_names->GetStringWithoutPathExpansion(user_id, &result) && + !result.empty()) + return result; + return UTF8ToUTF16(GetManagerDisplayEmail(user_id)); +} + +std::string SupervisedUserManagerImpl::GetManagerUserId( + const std::string& user_id) const { + PrefService* local_state = g_browser_process->local_state(); + const DictionaryValue* manager_ids = + local_state->GetDictionary(kManagedUserManagers); + std::string result; + manager_ids->GetStringWithoutPathExpansion(user_id, &result); + return result; +} + +std::string SupervisedUserManagerImpl::GetManagerDisplayEmail( + const std::string& user_id) const { + PrefService* local_state = g_browser_process->local_state(); + const DictionaryValue* manager_mails = + local_state->GetDictionary(kManagedUserManagerDisplayEmails); + std::string result; + if (manager_mails->GetStringWithoutPathExpansion(user_id, &result) && + !result.empty()) { + return result; + } + return GetManagerUserId(user_id); +} + +const User* SupervisedUserManagerImpl::FindByDisplayName( + const string16& display_name) const { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + const UserList& users = owner_->GetUsers(); + for (UserList::const_iterator it = users.begin(); it != users.end(); ++it) { + if (((*it)->GetType() == User::USER_TYPE_LOCALLY_MANAGED) && + ((*it)->display_name() == display_name)) { + return *it; + } + } + return NULL; +} + +const User* SupervisedUserManagerImpl::FindBySyncId( + const std::string& sync_id) const { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + const UserList& users = owner_->GetUsers(); + for (UserList::const_iterator it = users.begin(); it != users.end(); ++it) { + if (((*it)->GetType() == User::USER_TYPE_LOCALLY_MANAGED) && + (GetUserSyncId((*it)->email()) == sync_id)) { + return *it; + } + } + return NULL; +} + +void SupervisedUserManagerImpl::StartCreationTransaction( + const string16& display_name) { + g_browser_process->local_state()-> + SetString(kLocallyManagedUserCreationTransactionDisplayName, + UTF16ToASCII(display_name)); + g_browser_process->local_state()->CommitPendingWrite(); +} + +void SupervisedUserManagerImpl::SetCreationTransactionUserId( + const std::string& email) { + g_browser_process->local_state()-> + SetString(kLocallyManagedUserCreationTransactionUserId, + email); + g_browser_process->local_state()->CommitPendingWrite(); +} + +void SupervisedUserManagerImpl::CommitCreationTransaction() { + g_browser_process->local_state()-> + ClearPref(kLocallyManagedUserCreationTransactionDisplayName); + g_browser_process->local_state()-> + ClearPref(kLocallyManagedUserCreationTransactionUserId); + g_browser_process->local_state()->CommitPendingWrite(); +} + +bool SupervisedUserManagerImpl::HasFailedUserCreationTransaction() { + return !(g_browser_process->local_state()-> + GetString(kLocallyManagedUserCreationTransactionDisplayName). + empty()); +} + +void SupervisedUserManagerImpl::RollbackUserCreationTransaction() { + PrefService* prefs = g_browser_process->local_state(); + + std::string display_name = prefs-> + GetString(kLocallyManagedUserCreationTransactionDisplayName); + std::string user_id = prefs-> + GetString(kLocallyManagedUserCreationTransactionUserId); + + LOG(WARNING) << "Cleaning up transaction for " + << display_name << "/" << user_id; + + if (user_id.empty()) { + // Not much to do - just remove transaction. + prefs->ClearPref(kLocallyManagedUserCreationTransactionDisplayName); + return; + } + + if (gaia::ExtractDomainName(user_id) != + UserManager::kLocallyManagedUserDomain) { + LOG(WARNING) << "Clean up transaction for non-locally managed user found :" + << user_id << ", will not remove data"; + prefs->ClearPref(kLocallyManagedUserCreationTransactionDisplayName); + prefs->ClearPref(kLocallyManagedUserCreationTransactionUserId); + return; + } + + owner_->RemoveUser(user_id, NULL); + + prefs->ClearPref(kLocallyManagedUserCreationTransactionDisplayName); + prefs->ClearPref(kLocallyManagedUserCreationTransactionUserId); + prefs->CommitPendingWrite(); +} + +void SupervisedUserManagerImpl::RemoveNonCryptohomeData( + const std::string& user_id) { + PrefService* prefs = g_browser_process->local_state(); + ListPrefUpdate prefs_new_users_update(prefs, kLocallyManagedUsersFirstRun); + prefs_new_users_update->Remove(base::StringValue(user_id), NULL); + + DictionaryPrefUpdate managers_update(prefs, kManagedUserManagers); + managers_update->RemoveWithoutPathExpansion(user_id, NULL); + + DictionaryPrefUpdate manager_names_update(prefs, + kManagedUserManagerNames); + manager_names_update->RemoveWithoutPathExpansion(user_id, NULL); + + DictionaryPrefUpdate manager_emails_update(prefs, + kManagedUserManagerDisplayEmails); + manager_emails_update->RemoveWithoutPathExpansion(user_id, NULL); +} + +bool SupervisedUserManagerImpl::CheckForFirstRun(const std::string& user_id) { + ListPrefUpdate prefs_new_users_update(g_browser_process->local_state(), + kLocallyManagedUsersFirstRun); + return prefs_new_users_update->Remove(base::StringValue(user_id), NULL); +} + +void SupervisedUserManagerImpl::UpdateManagerName(const std::string& manager_id, + const string16& new_display_name) { + PrefService* local_state = g_browser_process->local_state(); + + const DictionaryValue* manager_ids = + local_state->GetDictionary(kManagedUserManagers); + + DictionaryPrefUpdate manager_name_update(local_state, + kManagedUserManagerNames); + for (DictionaryValue::Iterator it(*manager_ids); !it.IsAtEnd(); + it.Advance()) { + std::string user_id; + bool has_manager_id = it.value().GetAsString(&user_id); + DCHECK(has_manager_id); + if (user_id == manager_id) { + manager_name_update->SetWithoutPathExpansion( + it.key(), + new base::StringValue(new_display_name)); + } + } +} + + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/supervised_user_manager_impl.h b/chrome/browser/chromeos/login/supervised_user_manager_impl.h new file mode 100644 index 0000000000..e598006547 --- /dev/null +++ b/chrome/browser/chromeos/login/supervised_user_manager_impl.h @@ -0,0 +1,76 @@ +// Copyright 2013 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_CHROMEOS_LOGIN_SUPERVISED_USER_MANAGER_IMPL_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_SUPERVISED_USER_MANAGER_IMPL_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "chrome/browser/chromeos/login/supervised_user_manager.h" + +namespace chromeos { + +class CrosSettings; +class UserManagerImpl; + +// Implementation of the UserManager. +class SupervisedUserManagerImpl + : public SupervisedUserManager { + public: + virtual ~SupervisedUserManagerImpl(); + + virtual const User* CreateUserRecord( + const std::string& manager_id, + const std::string& local_user_id, + const std::string& sync_user_id, + const string16& display_name) OVERRIDE; + virtual std::string GenerateUserId() OVERRIDE; + virtual const User* FindByDisplayName(const string16& display_name) const + OVERRIDE; + virtual const User* FindBySyncId(const std::string& sync_id) const OVERRIDE; + virtual std::string GetUserSyncId(const std::string& user_id) const OVERRIDE; + virtual string16 GetManagerDisplayName(const std::string& user_id) const + OVERRIDE; + virtual std::string GetManagerUserId(const std::string& user_id) const + OVERRIDE; + virtual std::string GetManagerDisplayEmail(const std::string& user_id) const + OVERRIDE; + virtual void StartCreationTransaction(const string16& display_name) OVERRIDE; + virtual void SetCreationTransactionUserId(const std::string& user_id) + OVERRIDE; + virtual void CommitCreationTransaction() OVERRIDE; + + private: + friend class UserManager; + friend class UserManagerImpl; + + explicit SupervisedUserManagerImpl(UserManagerImpl* owner); + + // Returns true if there is non-committed user creation transaction. + bool HasFailedUserCreationTransaction(); + + // Attempts to clean up data that could be left from failed user creation. + void RollbackUserCreationTransaction(); + + void RemoveNonCryptohomeData(const std::string& user_id); + + bool CheckForFirstRun(const std::string& user_id); + + // Update name if this user is manager of some managed users. + void UpdateManagerName(const std::string& manager_id, + const string16& new_display_name); + + UserManagerImpl* owner_; + + // Interface to the signed settings store. + CrosSettings* cros_settings_; + + DISALLOW_COPY_AND_ASSIGN(SupervisedUserManagerImpl); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_SUPERVISED_USER_MANAGER_IMPL_H_ diff --git a/chrome/browser/chromeos/login/test_login_utils.cc b/chrome/browser/chromeos/login/test_login_utils.cc index 74cca19590..750e7735e6 100644 --- a/chrome/browser/chromeos/login/test_login_utils.cc +++ b/chrome/browser/chromeos/login/test_login_utils.cc @@ -20,7 +20,6 @@ TestLoginUtils::~TestLoginUtils() {} void TestLoginUtils::PrepareProfile( const UserContext& credentials, const std::string& display_email, - bool using_oauth, bool has_cookies, bool has_active_session, Delegate* delegate) { diff --git a/chrome/browser/chromeos/login/test_login_utils.h b/chrome/browser/chromeos/login/test_login_utils.h index f1fd4fb849..9f27300727 100644 --- a/chrome/browser/chromeos/login/test_login_utils.h +++ b/chrome/browser/chromeos/login/test_login_utils.h @@ -29,7 +29,6 @@ class TestLoginUtils : public LoginUtils { LoginDisplayHost* login_host) OVERRIDE {} virtual void PrepareProfile(const UserContext& credentials, const std::string& display_email, - bool using_oauth, bool has_cookies, bool has_active_session, Delegate* delegate) OVERRIDE; diff --git a/chrome/browser/chromeos/login/user.cc b/chrome/browser/chromeos/login/user.cc index e2cedfc23d..33b38e27ec 100644 --- a/chrome/browser/chromeos/login/user.cc +++ b/chrome/browser/chromeos/login/user.cc @@ -104,7 +104,7 @@ class PublicAccountUser : public User { DISALLOW_COPY_AND_ASSIGN(PublicAccountUser); }; -UserContext::UserContext() { +UserContext::UserContext() : using_oauth(true) { } UserContext::UserContext(const std::string& username, @@ -112,7 +112,8 @@ UserContext::UserContext(const std::string& username, const std::string& auth_code) : username(username), password(password), - auth_code(auth_code) { + auth_code(auth_code), + using_oauth(true) { } UserContext::UserContext(const std::string& username, @@ -122,7 +123,20 @@ UserContext::UserContext(const std::string& username, : username(username), password(password), auth_code(auth_code), - username_hash(username_hash) { + username_hash(username_hash), + using_oauth(true) { +} + +UserContext::UserContext(const std::string& username, + const std::string& password, + const std::string& auth_code, + const std::string& username_hash, + bool using_oauth) + : username(username), + password(password), + auth_code(auth_code), + username_hash(username_hash), + using_oauth(using_oauth) { } UserContext::~UserContext() { @@ -132,7 +146,8 @@ bool UserContext::operator==(const UserContext& context) const { return context.username == username && context.password == password && context.auth_code == auth_code && - context.username_hash == username_hash; + context.username_hash == username_hash && + context.using_oauth == using_oauth; } string16 User::GetDisplayName() const { diff --git a/chrome/browser/chromeos/login/user.h b/chrome/browser/chromeos/login/user.h index 60b8670e4f..c8a15b5dd1 100644 --- a/chrome/browser/chromeos/login/user.h +++ b/chrome/browser/chromeos/login/user.h @@ -32,12 +32,18 @@ struct UserContext { const std::string& password, const std::string& auth_code, const std::string& username_hash); + UserContext(const std::string& username, + const std::string& password, + const std::string& auth_code, + const std::string& username_hash, + bool using_oauth); virtual ~UserContext(); bool operator==(const UserContext& context) const; std::string username; std::string password; std::string auth_code; std::string username_hash; + bool using_oauth; }; // A class representing information about a previously logged in user. @@ -163,6 +169,7 @@ class User { virtual bool is_active() const; protected: + friend class SupervisedUserManagerImpl; friend class UserManagerImpl; friend class UserImageManagerImpl; // For testing: diff --git a/chrome/browser/chromeos/login/user_manager.h b/chrome/browser/chromeos/login/user_manager.h index 139fcb2287..4e59d378b6 100644 --- a/chrome/browser/chromeos/login/user_manager.h +++ b/chrome/browser/chromeos/login/user_manager.h @@ -16,6 +16,7 @@ namespace chromeos { class RemoveUserDelegate; class UserImageManager; +class SupervisedUserManager; // Base class for UserManagerImpl - provides a mechanism for discovering users // who have logged into this Chrome OS device before and updating that list. @@ -102,6 +103,7 @@ class UserManager { virtual ~UserManager(); virtual UserImageManager* GetUserImageManager() = 0; + virtual SupervisedUserManager* GetSupervisedUserManager() = 0; // Returns a list of users who have logged into this device previously. This // is sorted by last login date with the most recent user at the beginning. @@ -125,17 +127,17 @@ class UserManager { // no owner for the device. virtual const std::string& GetOwnerEmail() = 0; - // Indicates that a user with the given |email| has just logged in. The + // Indicates that a user with the given |user_id| has just logged in. The // persistent list is updated accordingly if the user is not ephemeral. // |browser_restart| is true when reloading Chrome after crash to distinguish // from normal sign in flow. // |username_hash| is used to identify homedir mount point. - virtual void UserLoggedIn(const std::string& email, + virtual void UserLoggedIn(const std::string& user_id, const std::string& username_hash, bool browser_restart) = 0; - // Switches to active user identified by |email|. User has to be logged in. - virtual void SwitchActiveUser(const std::string& email) = 0; + // Switches to active user identified by |user_id|. User has to be logged in. + virtual void SwitchActiveUser(const std::string& user_id) = 0; // Called when browser session is started i.e. after // browser_creator.LaunchBrowser(...) was called after user sign in. @@ -153,48 +155,23 @@ class UserManager { // and notifies observers. virtual void RestoreActiveSessions() = 0; - // Creates locally managed user with given |display_name| and|local_user_id| - // and persists that to user list. Also links this user identified by - // |sync_user_id| to manager with a |manager_id|. - // Returns created user, or existing user if there already - // was locally managed user with such display name. - // TODO(antrim): Refactor into a single struct to have only 1 getter. - virtual const User* CreateLocallyManagedUserRecord( - const std::string& manager_id, - const std::string& local_user_id, - const std::string& sync_user_id, - const string16& display_name) = 0; - - // Generates unique username for locally managed user. - virtual std::string GenerateUniqueLocallyManagedUserId() = 0; - // Removes the user from the device. Note, it will verify that the given user // isn't the owner, so calling this method for the owner will take no effect. // Note, |delegate| can be NULL. - virtual void RemoveUser(const std::string& email, + virtual void RemoveUser(const std::string& user_id, RemoveUserDelegate* delegate) = 0; // Removes the user from the persistent list only. Also removes the user's // picture. - virtual void RemoveUserFromList(const std::string& email) = 0; + virtual void RemoveUserFromList(const std::string& user_id) = 0; - // Returns true if a user with the given email address is found in the - // persistent list or currently logged in as ephemeral. - virtual bool IsKnownUser(const std::string& email) const = 0; + // Returns true if a user with the given user id is found in the persistent + // list or currently logged in as ephemeral. + virtual bool IsKnownUser(const std::string& user_id) const = 0; - // Returns the user with the given email address if found in the persistent + // Returns the user with the given user id if found in the persistent // list or currently logged in as ephemeral. Returns |NULL| otherwise. - virtual const User* FindUser(const std::string& email) const = 0; - - // Returns the locally managed user with the given |display_name| if found in - // the persistent list. Returns |NULL| otherwise. - virtual const User* FindLocallyManagedUser( - const string16& display_name) const = 0; - - // Returns the locally managed user with the given |sync_id| if found in - // the persistent list. Returns |NULL| otherwise. - virtual const User* FindLocallyManagedUserBySyncId( - const std::string& sync_id) const = 0; + virtual const User* FindUser(const std::string& user_id) const = 0; // Returns the logged-in user. // TODO(nkostylev): Deprecate this call, move clients to GetActiveUser(). @@ -217,59 +194,35 @@ class UserManager { // Saves user's oauth token status in local state preferences. virtual void SaveUserOAuthStatus( - const std::string& username, + const std::string& user_id, User::OAuthTokenStatus oauth_token_status) = 0; // Saves user's displayed name in local state preferences. // Ignored If there is no such user. - virtual void SaveUserDisplayName(const std::string& username, + virtual void SaveUserDisplayName(const std::string& user_id, const string16& display_name) = 0; // Updates data upon User Account download. - virtual void UpdateUserAccountData(const std::string& username, + virtual void UpdateUserAccountData(const std::string& user_id, const string16& display_name, const std::string& locale) = 0; - // Returns the display name for user |username| if it is known (was + // Returns the display name for user |user_id| if it is known (was // previously set by a |SaveUserDisplayName| call). // Otherwise, returns an empty string. virtual string16 GetUserDisplayName( - const std::string& username) const = 0; + const std::string& user_id) const = 0; // Saves user's displayed (non-canonical) email in local state preferences. // Ignored If there is no such user. - virtual void SaveUserDisplayEmail(const std::string& username, + virtual void SaveUserDisplayEmail(const std::string& user_id, const std::string& display_email) = 0; - // Returns the display email for user |username| if it is known (was + // Returns the display email for user |user_id| if it is known (was // previously set by a |SaveUserDisplayEmail| call). - // Otherwise, returns |username| itself. + // Otherwise, returns |user_id| itself. virtual std::string GetUserDisplayEmail( - const std::string& username) const = 0; - - // Returns sync_user_id for locally managed user with |managed_user_id| or - // empty string if such user is not found or it doesn't have - // sync_user_id defined. - virtual std::string GetManagedUserSyncId( - const std::string& managed_user_id) const = 0; - - // Returns the display name for manager of user |managed_user_id| if it is - // known (was previously set by a |SaveUserDisplayName| call). - // Otherwise, returns a manager id. - virtual string16 GetManagerDisplayNameForManagedUser( - const std::string& managed_user_id) const = 0; - - // Returns the user id for manager of user |managed_user_id| if it is known - // (user is actually a managed user). - // Otherwise, returns an empty string. - virtual std::string GetManagerUserIdForManagedUser( - const std::string& managed_user_id) const = 0; - - // Returns the display email for manager of user |managed_user_id| if it is - // known (user is actually a managed user). - // Otherwise, returns an empty string. - virtual std::string GetManagerDisplayEmailForManagedUser( - const std::string& managed_user_id) const = 0; + const std::string& user_id) const = 0; // Returns true if current user is an owner. virtual bool IsCurrentUserOwner() const = 0; @@ -323,41 +276,30 @@ class UserManager { // user's session. virtual bool HasBrowserRestarted() const = 0; - // Returns true if data stored or cached for the user with the given email + // Returns true if data stored or cached for the user with the given user id // address outside that user's cryptohome (wallpaper, avatar, OAuth token // status, display name, display email) is to be treated as ephemeral. virtual bool IsUserNonCryptohomeDataEphemeral( - const std::string& email) const = 0; - - // Create a record about starting locally managed user creation transaction. - virtual void StartLocallyManagedUserCreationTransaction( - const string16& display_name) = 0; - - // Add user id to locally managed user creation transaction record. - virtual void SetLocallyManagedUserCreationTransactionUserId( - const std::string& email) = 0; - - // Remove locally managed user creation transaction record. - virtual void CommitLocallyManagedUserCreationTransaction() = 0; + const std::string& user_id) const = 0; - // Method that allows to set |flow| for user identified by |email|. + // Method that allows to set |flow| for user identified by |user_id|. // Flow should be set before login attempt. // Takes ownership of the |flow|, |flow| will be deleted in case of login // failure. - virtual void SetUserFlow(const std::string& email, UserFlow* flow) = 0; + virtual void SetUserFlow(const std::string& user_id, UserFlow* flow) = 0; // Return user flow for current user. Returns instance of DefaultUserFlow if // no flow was defined for current user, or user is not logged in. // Returned value should not be cached. virtual UserFlow* GetCurrentUserFlow() const = 0; - // Return user flow for user identified by |email|. Returns instance of + // Return user flow for user identified by |user_id|. Returns instance of // DefaultUserFlow if no flow was defined for user. // Returned value should not be cached. - virtual UserFlow* GetUserFlow(const std::string& email) const = 0; + virtual UserFlow* GetUserFlow(const std::string& user_id) const = 0; - // Resets user flow for user identified by |email|. - virtual void ResetUserFlow(const std::string& email) = 0; + // Resets user flow for user identified by |user_id|. + virtual void ResetUserFlow(const std::string& user_id) = 0; // Gets/sets chrome oauth client id and secret for kiosk app mode. The default // values can be overridden with kiosk auth file. @@ -379,8 +321,9 @@ class UserManager { // Returns true if locally managed users allowed. virtual bool AreLocallyManagedUsersAllowed() const = 0; - // Returns profile dir for the user identified by |email|. - virtual base::FilePath GetUserProfileDir(const std::string& email) const = 0; + // Returns profile dir for the user identified by |user_id|. + virtual base::FilePath GetUserProfileDir(const std::string& user_id) + const = 0; // Changes browser locale (selects best suitable locale from different // user settings). diff --git a/chrome/browser/chromeos/login/user_manager_impl.cc b/chrome/browser/chromeos/login/user_manager_impl.cc index 2066d8ea36..e332863a81 100644 --- a/chrome/browser/chromeos/login/user_manager_impl.cc +++ b/chrome/browser/chromeos/login/user_manager_impl.cc @@ -35,6 +35,7 @@ #include "chrome/browser/chromeos/login/multi_profile_first_run_notification.h" #include "chrome/browser/chromeos/login/multi_profile_user_controller.h" #include "chrome/browser/chromeos/login/remove_user_delegate.h" +#include "chrome/browser/chromeos/login/supervised_user_manager_impl.h" #include "chrome/browser/chromeos/login/user_image_manager_impl.h" #include "chrome/browser/chromeos/login/wizard_controller.h" #include "chrome/browser/chromeos/policy/device_local_account.h" @@ -69,13 +70,13 @@ using content::BrowserThread; namespace chromeos { struct UpdateUserAccountDataCallbackData { - UpdateUserAccountDataCallbackData(const std::string& username, + UpdateUserAccountDataCallbackData(const std::string& user_id, const string16& display_name, const std::string& raw_locale) - : username_(username), + : user_id_(user_id), display_name_(display_name), raw_locale_(raw_locale) {} - std::string username_; + std::string user_id_; string16 display_name_; std::string raw_locale_; std::string resolved_locale_; @@ -90,38 +91,6 @@ const char kRegularUsers[] = "LoggedInUsers"; // A vector pref of the public accounts defined on this device. const char kPublicAccounts[] = "PublicAccounts"; -// A map from locally managed user local user id to sync user id. -const char kManagedUserSyncId[] = - "ManagedUserSyncId"; - -// A map from locally managed user id to manager user id. -const char kManagedUserManagers[] = - "ManagedUserManagers"; - -// A map from locally managed user id to manager display name. -const char kManagedUserManagerNames[] = - "ManagedUserManagerNames"; - -// A map from locally managed user id to manager display e-mail. -const char kManagedUserManagerDisplayEmails[] = - "ManagedUserManagerDisplayEmails"; - -// A vector pref of the locally managed accounts defined on this device, that -// had not logged in yet. -const char kLocallyManagedUsersFirstRun[] = "LocallyManagedUsersFirstRun"; - -// A pref of the next id for locally managed users generation. -const char kLocallyManagedUsersNextId[] = - "LocallyManagedUsersNextId"; - -// A pref of the next id for locally managed users generation. -const char kLocallyManagedUserCreationTransactionDisplayName[] = - "LocallyManagedUserCreationTransactionDisplayName"; - -// A pref of the next id for locally managed users generation. -const char kLocallyManagedUserCreationTransactionUserId[] = - "LocallyManagedUserCreationTransactionUserId"; - // A string pref that gets set when a public account is removed but a user is // currently logged into that account, requiring the account's data to be // removed after logout. @@ -238,22 +207,12 @@ static void UpdateUserAccountDataImplCheckAndResolveLocale( void UserManager::RegisterPrefs(PrefRegistrySimple* registry) { registry->RegisterListPref(kRegularUsers); registry->RegisterListPref(kPublicAccounts); - registry->RegisterListPref(kLocallyManagedUsersFirstRun); - registry->RegisterIntegerPref(kLocallyManagedUsersNextId, 0); registry->RegisterStringPref(kPublicAccountPendingDataRemoval, ""); - registry->RegisterStringPref( - kLocallyManagedUserCreationTransactionDisplayName, ""); - registry->RegisterStringPref( - kLocallyManagedUserCreationTransactionUserId, ""); registry->RegisterStringPref(kLastLoggedInRegularUser, ""); registry->RegisterDictionaryPref(kUserOAuthTokenStatus); registry->RegisterDictionaryPref(kUserDisplayName); registry->RegisterDictionaryPref(kUserDisplayEmail); - registry->RegisterDictionaryPref(kManagedUserSyncId); - registry->RegisterDictionaryPref(kManagedUserManagers); - registry->RegisterDictionaryPref(kManagedUserManagerNames); - registry->RegisterDictionaryPref(kManagedUserManagerDisplayEmails); - + SupervisedUserManager::RegisterPrefs(registry); SessionLengthLimiter::RegisterPrefs(registry); } @@ -270,6 +229,7 @@ UserManagerImpl::UserManagerImpl() is_current_user_ephemeral_regular_user_(false), ephemeral_users_enabled_(false), user_image_manager_(new UserImageManagerImpl), + supervised_user_manager_(new SupervisedUserManagerImpl(this)), manager_creation_time_(base::TimeTicks::Now()), multi_profile_first_run_notification_( new MultiProfileFirstRunNotification) { @@ -329,6 +289,10 @@ UserImageManager* UserManagerImpl::GetUserImageManager() { return user_image_manager_.get(); } +SupervisedUserManager* UserManagerImpl::GetSupervisedUserManager() { + return supervised_user_manager_.get(); +} + const UserList& UserManagerImpl::GetUsers() const { const_cast<UserManagerImpl*>(this)->EnsureUsersLoaded(); return users_; @@ -377,7 +341,7 @@ const std::string& UserManagerImpl::GetOwnerEmail() { return owner_email_; } -void UserManagerImpl::UserLoggedIn(const std::string& email, +void UserManagerImpl::UserLoggedIn(const std::string& user_id, const std::string& username_hash, bool browser_restart) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -385,7 +349,7 @@ void UserManagerImpl::UserLoggedIn(const std::string& email, if (!CommandLine::ForCurrentProcess()->HasSwitch(::switches::kMultiProfiles)) DCHECK(!IsUserLoggedIn()); - User* user = FindUserInListAndModify(email); + User* user = FindUserInListAndModify(user_id); if (active_user_ && user) { user->set_is_logged_in(true); user->set_username_hash(username_hash); @@ -400,32 +364,32 @@ void UserManagerImpl::UserLoggedIn(const std::string& email, } policy::DeviceLocalAccount::Type device_local_account_type; - if (email == UserManager::kGuestUserName) { + if (user_id == UserManager::kGuestUserName) { GuestUserLoggedIn(); - } else if (email == UserManager::kRetailModeUserName) { + } else if (user_id == UserManager::kRetailModeUserName) { RetailModeUserLoggedIn(); - } else if (policy::IsDeviceLocalAccountUser(email, + } else if (policy::IsDeviceLocalAccountUser(user_id, &device_local_account_type) && device_local_account_type == policy::DeviceLocalAccount::TYPE_KIOSK_APP) { - KioskAppLoggedIn(email); + KioskAppLoggedIn(user_id); } else { EnsureUsersLoaded(); if (user && user->GetType() == User::USER_TYPE_PUBLIC_ACCOUNT) { PublicAccountUserLoggedIn(user); } else if ((user && user->GetType() == User::USER_TYPE_LOCALLY_MANAGED) || - (!user && gaia::ExtractDomainName(email) == + (!user && gaia::ExtractDomainName(user_id) == UserManager::kLocallyManagedUserDomain)) { - LocallyManagedUserLoggedIn(email); - } else if (browser_restart && email == g_browser_process->local_state()-> + LocallyManagedUserLoggedIn(user_id); + } else if (browser_restart && user_id == g_browser_process->local_state()-> GetString(kPublicAccountPendingDataRemoval)) { - PublicAccountUserLoggedIn(User::CreatePublicAccountUser(email)); - } else if (email != owner_email_ && !user && + PublicAccountUserLoggedIn(User::CreatePublicAccountUser(user_id)); + } else if (user_id != owner_email_ && !user && (AreEphemeralUsersEnabled() || browser_restart)) { - RegularUserLoggedInAsEphemeral(email); + RegularUserLoggedInAsEphemeral(user_id); } else { - RegularUserLoggedIn(email); + RegularUserLoggedIn(user_id); } // Initialize the session length limiter and start it only if @@ -445,7 +409,7 @@ void UserManagerImpl::UserLoggedIn(const std::string& email, if (!primary_user_) { primary_user_ = active_user_; if (primary_user_->GetType() == User::USER_TYPE_REGULAR) - SendRegularUserLoginMetrics(email); + SendRegularUserLoginMetrics(user_id); } UMA_HISTOGRAM_ENUMERATION("UserManager.LoginUserType", @@ -457,16 +421,16 @@ void UserManagerImpl::UserLoggedIn(const std::string& email, } g_browser_process->local_state()->SetString(kLastLoggedInRegularUser, - (active_user_->GetType() == User::USER_TYPE_REGULAR) ? email : ""); + (active_user_->GetType() == User::USER_TYPE_REGULAR) ? user_id : ""); NotifyOnLogin(); } -void UserManagerImpl::SwitchActiveUser(const std::string& email) { +void UserManagerImpl::SwitchActiveUser(const std::string& user_id) { if (!CommandLine::ForCurrentProcess()->HasSwitch(::switches::kMultiProfiles)) return; - User* user = FindUserAndModify(email); + User* user = FindUserAndModify(user_id); if (!user) { NOTREACHED() << "Switching to a non-existing user"; return; @@ -520,122 +484,11 @@ void UserManagerImpl::SessionStarted() { } } -std::string UserManagerImpl::GenerateUniqueLocallyManagedUserId() { - int counter = g_browser_process->local_state()-> - GetInteger(kLocallyManagedUsersNextId); - std::string id; - bool user_exists; - do { - id = base::StringPrintf("%d@%s", counter, kLocallyManagedUserDomain); - counter++; - user_exists = (NULL != FindUser(id)); - DCHECK(!user_exists); - if (user_exists) { - LOG(ERROR) << "Locally managed user with id " << id << " already exists."; - } - } while (user_exists); - - g_browser_process->local_state()-> - SetInteger(kLocallyManagedUsersNextId, counter); - - g_browser_process->local_state()->CommitPendingWrite(); - return id; -} - -const User* UserManagerImpl::CreateLocallyManagedUserRecord( - const std::string& manager_id, - const std::string& local_user_id, - const std::string& sync_user_id, - const string16& display_name) { - const User* user = FindLocallyManagedUser(display_name); - DCHECK(!user); - if (user) - return user; - - PrefService* local_state = g_browser_process->local_state(); - - User* new_user = User::CreateLocallyManagedUser(local_user_id); - ListPrefUpdate prefs_users_update(local_state, kRegularUsers); - prefs_users_update->Insert(0, new base::StringValue(local_user_id)); - ListPrefUpdate prefs_new_users_update(local_state, - kLocallyManagedUsersFirstRun); - prefs_new_users_update->Insert(0, new base::StringValue(local_user_id)); - users_.insert(users_.begin(), new_user); - - const User* manager = FindUser(manager_id); - CHECK(manager); - - DictionaryPrefUpdate sync_id_update(local_state, kManagedUserSyncId); - DictionaryPrefUpdate manager_update(local_state, kManagedUserManagers); - DictionaryPrefUpdate manager_name_update(local_state, - kManagedUserManagerNames); - DictionaryPrefUpdate manager_email_update(local_state, - kManagedUserManagerDisplayEmails); - sync_id_update->SetWithoutPathExpansion(local_user_id, - new base::StringValue(sync_user_id)); - manager_update->SetWithoutPathExpansion(local_user_id, - new base::StringValue(manager->email())); - manager_name_update->SetWithoutPathExpansion(local_user_id, - new base::StringValue(manager->GetDisplayName())); - manager_email_update->SetWithoutPathExpansion(local_user_id, - new base::StringValue(manager->display_email())); - - SaveUserDisplayName(local_user_id, display_name); - g_browser_process->local_state()->CommitPendingWrite(); - return new_user; -} - -std::string UserManagerImpl::GetManagedUserSyncId( - const std::string& managed_user_id) const { - PrefService* local_state = g_browser_process->local_state(); - const DictionaryValue* sync_user_ids = - local_state->GetDictionary(kManagedUserSyncId); - std::string result; - sync_user_ids->GetStringWithoutPathExpansion(managed_user_id, &result); - return result; -} - -string16 UserManagerImpl::GetManagerDisplayNameForManagedUser( - const std::string& managed_user_id) const { - PrefService* local_state = g_browser_process->local_state(); - - const DictionaryValue* manager_names = - local_state->GetDictionary(kManagedUserManagerNames); - string16 result; - if (manager_names->GetStringWithoutPathExpansion(managed_user_id, &result) && - !result.empty()) - return result; - return UTF8ToUTF16(GetManagerDisplayEmailForManagedUser(managed_user_id)); -} - -std::string UserManagerImpl::GetManagerUserIdForManagedUser( - const std::string& managed_user_id) const { - PrefService* local_state = g_browser_process->local_state(); - const DictionaryValue* manager_ids = - local_state->GetDictionary(kManagedUserManagers); - std::string result; - manager_ids->GetStringWithoutPathExpansion(managed_user_id, &result); - return result; -} - -std::string UserManagerImpl::GetManagerDisplayEmailForManagedUser( - const std::string& managed_user_id) const { - PrefService* local_state = g_browser_process->local_state(); - const DictionaryValue* manager_mails = - local_state->GetDictionary(kManagedUserManagerDisplayEmails); - std::string result; - if (manager_mails->GetStringWithoutPathExpansion(managed_user_id, &result) && - !result.empty()) { - return result; - } - return GetManagerUserIdForManagedUser(managed_user_id); -} - -void UserManagerImpl::RemoveUser(const std::string& email, +void UserManagerImpl::RemoveUser(const std::string& user_id, RemoveUserDelegate* delegate) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - const User* user = FindUser(email); + const User* user = FindUser(user_id); if (!user || (user->GetType() != User::USER_TYPE_REGULAR && user->GetType() != User::USER_TYPE_LOCALLY_MANAGED)) return; @@ -651,57 +504,32 @@ void UserManagerImpl::RemoveUser(const std::string& email, // Sanity check: do not allow any of the the logged in users to be removed. for (UserList::const_iterator it = logged_in_users_.begin(); it != logged_in_users_.end(); ++it) { - if ((*it)->email() == email) + if ((*it)->email() == user_id) return; } - RemoveUserInternal(email, delegate); + RemoveUserInternal(user_id, delegate); } -void UserManagerImpl::RemoveUserFromList(const std::string& email) { +void UserManagerImpl::RemoveUserFromList(const std::string& user_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); EnsureUsersLoaded(); - RemoveNonCryptohomeData(email); - delete RemoveRegularOrLocallyManagedUserFromList(email); + RemoveNonCryptohomeData(user_id); + User* user = RemoveRegularOrLocallyManagedUserFromList(user_id); + delete user; // Make sure that new data is persisted to Local State. g_browser_process->local_state()->CommitPendingWrite(); } -bool UserManagerImpl::IsKnownUser(const std::string& email) const { - return FindUser(email) != NULL; +bool UserManagerImpl::IsKnownUser(const std::string& user_id) const { + return FindUser(user_id) != NULL; } -const User* UserManagerImpl::FindUser(const std::string& email) const { +const User* UserManagerImpl::FindUser(const std::string& user_id) const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (active_user_ && active_user_->email() == email) + if (active_user_ && active_user_->email() == user_id) return active_user_; - return FindUserInList(email); -} - -const User* UserManagerImpl::FindLocallyManagedUser( - const string16& display_name) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - const UserList& users = GetUsers(); - for (UserList::const_iterator it = users.begin(); it != users.end(); ++it) { - if (((*it)->GetType() == User::USER_TYPE_LOCALLY_MANAGED) && - ((*it)->display_name() == display_name)) { - return *it; - } - } - return NULL; -} - -const User* UserManagerImpl::FindLocallyManagedUserBySyncId( - const std::string& sync_id) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - const UserList& users = GetUsers(); - for (UserList::const_iterator it = users.begin(); it != users.end(); ++it) { - if (((*it)->GetType() == User::USER_TYPE_LOCALLY_MANAGED) && - (GetManagedUserSyncId((*it)->email()) == sync_id)) { - return *it; - } - } - return NULL; + return FindUserInList(user_id); } const User* UserManagerImpl::GetLoggedInUser() const { @@ -746,31 +574,31 @@ User* UserManagerImpl::GetUserByProfile(Profile* profile) const { } void UserManagerImpl::SaveUserOAuthStatus( - const std::string& username, + const std::string& user_id, User::OAuthTokenStatus oauth_token_status) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DVLOG(1) << "Saving user OAuth token status in Local State"; - User* user = FindUserAndModify(username); + User* user = FindUserAndModify(user_id); if (user) user->set_oauth_token_status(oauth_token_status); - GetUserFlow(username)->HandleOAuthTokenStatusChange(oauth_token_status); + GetUserFlow(user_id)->HandleOAuthTokenStatusChange(oauth_token_status); // Do not update local store if data stored or cached outside the user's // cryptohome is to be treated as ephemeral. - if (IsUserNonCryptohomeDataEphemeral(username)) + if (IsUserNonCryptohomeDataEphemeral(user_id)) return; PrefService* local_state = g_browser_process->local_state(); DictionaryPrefUpdate oauth_status_update(local_state, kUserOAuthTokenStatus); - oauth_status_update->SetWithoutPathExpansion(username, + oauth_status_update->SetWithoutPathExpansion(user_id, new base::FundamentalValue(static_cast<int>(oauth_token_status))); } User::OAuthTokenStatus UserManagerImpl::LoadUserOAuthStatus( - const std::string& username) const { + const std::string& user_id) const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); PrefService* local_state = g_browser_process->local_state(); @@ -779,11 +607,11 @@ User::OAuthTokenStatus UserManagerImpl::LoadUserOAuthStatus( int oauth_token_status = User::OAUTH_TOKEN_STATUS_UNKNOWN; if (prefs_oauth_status && prefs_oauth_status->GetIntegerWithoutPathExpansion( - username, &oauth_token_status)) { + user_id, &oauth_token_status)) { User::OAuthTokenStatus result = static_cast<User::OAuthTokenStatus>(oauth_token_status); if (result == User::OAUTH2_TOKEN_STATUS_INVALID) - GetUserFlow(username)->HandleOAuthTokenStatusChange(result); + GetUserFlow(user_id)->HandleOAuthTokenStatusChange(result); return result; } return User::OAUTH_TOKEN_STATUS_UNKNOWN; @@ -834,30 +662,14 @@ void UserManagerImpl::UpdateUserAccountDataImplCallback( username, new base::StringValue(display_name)); - // Update name if this user is manager of some managed users. - const DictionaryValue* manager_ids = - local_state->GetDictionary(kManagedUserManagers); - - DictionaryPrefUpdate manager_name_update(local_state, - kManagedUserManagerNames); - for (DictionaryValue::Iterator it(*manager_ids); !it.IsAtEnd(); - it.Advance()) { - std::string manager_id; - bool has_manager_id = it.value().GetAsString(&manager_id); - DCHECK(has_manager_id); - if (manager_id == username) { - manager_name_update->SetWithoutPathExpansion( - it.key(), - new base::StringValue(display_name)); - } - } + supervised_user_manager_->UpdateManagerName(username, display_name); } // Proxy for the previous call. void UserManagerImpl::UpdateUserAccountDataImplCallbackDecorator( const scoped_ptr<UpdateUserAccountDataCallbackData>& data) { UpdateUserAccountDataImplCallback( - data->username_, data->display_name_, &(data->resolved_locale_)); + data->user_id_, data->display_name_, &(data->resolved_locale_)); } void UserManagerImpl::UpdateUserAccountDataImpl(const std::string& username, @@ -1134,19 +946,19 @@ bool UserManagerImpl::HasBrowserRestarted() const { } bool UserManagerImpl::IsUserNonCryptohomeDataEphemeral( - const std::string& email) const { + const std::string& user_id) const { // Data belonging to the guest, retail mode and stub users is always // ephemeral. - if (email == UserManager::kGuestUserName || - email == UserManager::kRetailModeUserName || - email == kStubUser) { + if (user_id == UserManager::kGuestUserName || + user_id == UserManager::kRetailModeUserName || + user_id == kStubUser) { return true; } // Data belonging to the owner, anyone found on the user list and obsolete // public accounts whose data has not been removed yet is not ephemeral. - if (email == owner_email_ || FindUserInList(email) || - email == g_browser_process->local_state()-> + if (user_id == owner_email_ || FindUserInList(user_id) || + user_id == g_browser_process->local_state()-> GetString(kPublicAccountPendingDataRemoval)) { return false; } @@ -1156,7 +968,7 @@ bool UserManagerImpl::IsUserNonCryptohomeDataEphemeral( // was enabled. // - or - // b) The user logged into any other account type. - if (IsUserLoggedIn() && (email == GetLoggedInUser()->email()) && + if (IsUserLoggedIn() && (user_id == GetLoggedInUser()->email()) && (is_current_user_ephemeral_regular_user_ || !IsLoggedInAsRegularUser())) { return true; } @@ -1224,8 +1036,8 @@ void UserManagerImpl::EnsureUsersLoaded() { users_loaded_ = true; // Clean up user list first. - if (HasFailedLocallyManagedUserCreationTransaction()) - RollbackLocallyManagedUserCreationTransaction(); + if (supervised_user_manager_->HasFailedUserCreationTransaction()) + supervised_user_manager_->RollbackUserCreationTransaction(); PrefService* local_state = g_browser_process->local_state(); const ListValue* prefs_regular_users = local_state->GetList(kRegularUsers); @@ -1336,26 +1148,26 @@ UserList& UserManagerImpl::GetUsersAndModify() { return users_; } -User* UserManagerImpl::FindUserAndModify(const std::string& email) { +User* UserManagerImpl::FindUserAndModify(const std::string& user_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (active_user_ && active_user_->email() == email) + if (active_user_ && active_user_->email() == user_id) return active_user_; - return FindUserInListAndModify(email); + return FindUserInListAndModify(user_id); } -const User* UserManagerImpl::FindUserInList(const std::string& email) const { +const User* UserManagerImpl::FindUserInList(const std::string& user_id) const { const UserList& users = GetUsers(); for (UserList::const_iterator it = users.begin(); it != users.end(); ++it) { - if ((*it)->email() == email) + if ((*it)->email() == user_id) return *it; } return NULL; } -User* UserManagerImpl::FindUserInListAndModify(const std::string& email) { +User* UserManagerImpl::FindUserInListAndModify(const std::string& user_id) { UserList& users = GetUsersAndModify(); for (UserList::iterator it = users.begin(); it != users.end(); ++it) { - if ((*it)->email() == email) + if ((*it)->email() == user_id) return *it; } return NULL; @@ -1373,63 +1185,66 @@ void UserManagerImpl::GuestUserLoggedIn() { false); } -void UserManagerImpl::RegularUserLoggedIn(const std::string& email) { +void UserManagerImpl::AddUserRecord(User* user) { + // Add the user to the front of the user list. + ListPrefUpdate prefs_users_update(g_browser_process->local_state(), + kRegularUsers); + prefs_users_update->Insert(0, new base::StringValue(user->email())); + users_.insert(users_.begin(), user); +} + +void UserManagerImpl::RegularUserLoggedIn(const std::string& user_id) { // Remove the user from the user list. - active_user_ = RemoveRegularOrLocallyManagedUserFromList(email); + active_user_ = RemoveRegularOrLocallyManagedUserFromList(user_id); // If the user was not found on the user list, create a new user. is_current_user_new_ = !active_user_; if (!active_user_) { - active_user_ = User::CreateRegularUser(email); - active_user_->set_oauth_token_status(LoadUserOAuthStatus(email)); + active_user_ = User::CreateRegularUser(user_id); + active_user_->set_oauth_token_status(LoadUserOAuthStatus(user_id)); SaveUserDisplayName(active_user_->email(), UTF8ToUTF16(active_user_->GetAccountName(true))); - WallpaperManager::Get()->SetInitialUserWallpaper(email, true); + WallpaperManager::Get()->SetInitialUserWallpaper(user_id, true); } - // Add the user to the front of the user list. - ListPrefUpdate prefs_users_update(g_browser_process->local_state(), - kRegularUsers); - prefs_users_update->Insert(0, new base::StringValue(email)); - users_.insert(users_.begin(), active_user_); + AddUserRecord(active_user_); - user_image_manager_->UserLoggedIn(email, is_current_user_new_, false); + user_image_manager_->UserLoggedIn(user_id, is_current_user_new_, false); WallpaperManager::Get()->EnsureLoggedInUserWallpaperLoaded(); - default_pinned_apps_field_trial::SetupForUser(email, is_current_user_new_); + default_pinned_apps_field_trial::SetupForUser(user_id, is_current_user_new_); // Make sure that new data is persisted to Local State. g_browser_process->local_state()->CommitPendingWrite(); } -void UserManagerImpl::RegularUserLoggedInAsEphemeral(const std::string& email) { +void UserManagerImpl::RegularUserLoggedInAsEphemeral( + const std::string& user_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); is_current_user_new_ = true; is_current_user_ephemeral_regular_user_ = true; - active_user_ = User::CreateRegularUser(email); - user_image_manager_->UserLoggedIn(email, is_current_user_new_, false); - WallpaperManager::Get()->SetInitialUserWallpaper(email, false); + active_user_ = User::CreateRegularUser(user_id); + user_image_manager_->UserLoggedIn(user_id, is_current_user_new_, false); + WallpaperManager::Get()->SetInitialUserWallpaper(user_id, false); } void UserManagerImpl::LocallyManagedUserLoggedIn( - const std::string& username) { + const std::string& user_id) { // TODO(nkostylev): Refactor, share code with RegularUserLoggedIn(). // Remove the user from the user list. - active_user_ = RemoveRegularOrLocallyManagedUserFromList(username); + active_user_ = RemoveRegularOrLocallyManagedUserFromList(user_id); // If the user was not found on the user list, create a new user. if (!active_user_) { is_current_user_new_ = true; - active_user_ = User::CreateLocallyManagedUser(username); + active_user_ = User::CreateLocallyManagedUser(user_id); // Leaving OAuth token status at the default state = unknown. - WallpaperManager::Get()->SetInitialUserWallpaper(username, true); + WallpaperManager::Get()->SetInitialUserWallpaper(user_id, true); } else { - ListPrefUpdate prefs_new_users_update(g_browser_process->local_state(), - kLocallyManagedUsersFirstRun); - if (prefs_new_users_update->Remove(base::StringValue(username), NULL)) { + if (supervised_user_manager_->CheckForFirstRun(user_id)) { is_current_user_new_ = true; - WallpaperManager::Get()->SetInitialUserWallpaper(username, true); + WallpaperManager::Get()->SetInitialUserWallpaper(user_id, true); } else { is_current_user_new_ = false; } @@ -1438,7 +1253,7 @@ void UserManagerImpl::LocallyManagedUserLoggedIn( // Add the user to the front of the user list. ListPrefUpdate prefs_users_update(g_browser_process->local_state(), kRegularUsers); - prefs_users_update->Insert(0, new base::StringValue(username)); + prefs_users_update->Insert(0, new base::StringValue(user_id)); users_.insert(users_.begin(), active_user_); // Now that user is in the list, save display name. @@ -1447,7 +1262,7 @@ void UserManagerImpl::LocallyManagedUserLoggedIn( active_user_->GetDisplayName()); } - user_image_manager_->UserLoggedIn(username, is_current_user_new_, true); + user_image_manager_->UserLoggedIn(user_id, is_current_user_new_, true); WallpaperManager::Get()->EnsureLoggedInUserWallpaperLoaded(); // Make sure that new data is persisted to Local State. @@ -1548,37 +1363,25 @@ void UserManagerImpl::UpdateOwnership() { SetCurrentUserIsOwner(is_owner); } -void UserManagerImpl::RemoveNonCryptohomeData(const std::string& email) { - WallpaperManager::Get()->RemoveUserWallpaperInfo(email); - user_image_manager_->DeleteUserImage(email); +void UserManagerImpl::RemoveNonCryptohomeData(const std::string& user_id) { + WallpaperManager::Get()->RemoveUserWallpaperInfo(user_id); + user_image_manager_->DeleteUserImage(user_id); PrefService* prefs = g_browser_process->local_state(); DictionaryPrefUpdate prefs_oauth_update(prefs, kUserOAuthTokenStatus); int oauth_status; - prefs_oauth_update->GetIntegerWithoutPathExpansion(email, &oauth_status); - prefs_oauth_update->RemoveWithoutPathExpansion(email, NULL); + prefs_oauth_update->GetIntegerWithoutPathExpansion(user_id, &oauth_status); + prefs_oauth_update->RemoveWithoutPathExpansion(user_id, NULL); DictionaryPrefUpdate prefs_display_name_update(prefs, kUserDisplayName); - prefs_display_name_update->RemoveWithoutPathExpansion(email, NULL); + prefs_display_name_update->RemoveWithoutPathExpansion(user_id, NULL); DictionaryPrefUpdate prefs_display_email_update(prefs, kUserDisplayEmail); - prefs_display_email_update->RemoveWithoutPathExpansion(email, NULL); - - ListPrefUpdate prefs_new_users_update(prefs, kLocallyManagedUsersFirstRun); - prefs_new_users_update->Remove(base::StringValue(email), NULL); + prefs_display_email_update->RemoveWithoutPathExpansion(user_id, NULL); - DictionaryPrefUpdate managers_update(prefs, kManagedUserManagers); - managers_update->RemoveWithoutPathExpansion(email, NULL); + supervised_user_manager_->RemoveNonCryptohomeData(user_id); - DictionaryPrefUpdate manager_names_update(prefs, - kManagedUserManagerNames); - manager_names_update->RemoveWithoutPathExpansion(email, NULL); - - DictionaryPrefUpdate manager_emails_update(prefs, - kManagedUserManagerDisplayEmails); - manager_emails_update->RemoveWithoutPathExpansion(email, NULL); - - multi_profile_user_controller_->RemoveCachedValue(email); + multi_profile_user_controller_->RemoveCachedValue(user_id); } User* UserManagerImpl::RemoveRegularOrLocallyManagedUserFromList( @@ -1737,74 +1540,6 @@ void UserManagerImpl::UpdatePublicAccountDisplayName( SaveUserDisplayName(username, UTF8ToUTF16(display_name)); } -void UserManagerImpl::StartLocallyManagedUserCreationTransaction( - const string16& display_name) { - g_browser_process->local_state()-> - SetString(kLocallyManagedUserCreationTransactionDisplayName, - UTF16ToASCII(display_name)); - g_browser_process->local_state()->CommitPendingWrite(); -} - -void UserManagerImpl::SetLocallyManagedUserCreationTransactionUserId( - const std::string& email) { - g_browser_process->local_state()-> - SetString(kLocallyManagedUserCreationTransactionUserId, - email); - g_browser_process->local_state()->CommitPendingWrite(); -} - -void UserManagerImpl::CommitLocallyManagedUserCreationTransaction() { - g_browser_process->local_state()-> - ClearPref(kLocallyManagedUserCreationTransactionDisplayName); - g_browser_process->local_state()-> - ClearPref(kLocallyManagedUserCreationTransactionUserId); - g_browser_process->local_state()->CommitPendingWrite(); -} - -bool UserManagerImpl::HasFailedLocallyManagedUserCreationTransaction() { - return !(g_browser_process->local_state()-> - GetString(kLocallyManagedUserCreationTransactionDisplayName). - empty()); -} - -void UserManagerImpl::RollbackLocallyManagedUserCreationTransaction() { - PrefService* prefs = g_browser_process->local_state(); - - std::string display_name = prefs-> - GetString(kLocallyManagedUserCreationTransactionDisplayName); - std::string user_id = prefs-> - GetString(kLocallyManagedUserCreationTransactionUserId); - - LOG(WARNING) << "Cleaning up transaction for " - << display_name << "/" << user_id; - - if (user_id.empty()) { - // Not much to do - just remove transaction. - prefs->ClearPref(kLocallyManagedUserCreationTransactionDisplayName); - return; - } - - if (gaia::ExtractDomainName(user_id) != kLocallyManagedUserDomain) { - LOG(WARNING) << "Clean up transaction for non-locally managed user found :" - << user_id << ", will not remove data"; - prefs->ClearPref(kLocallyManagedUserCreationTransactionDisplayName); - prefs->ClearPref(kLocallyManagedUserCreationTransactionUserId); - return; - } - - ListPrefUpdate prefs_users_update(prefs, kRegularUsers); - prefs_users_update->Remove(base::StringValue(user_id), NULL); - - RemoveNonCryptohomeData(user_id); - - cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove( - user_id, base::Bind(&OnRemoveUserComplete, user_id)); - - prefs->ClearPref(kLocallyManagedUserCreationTransactionDisplayName); - prefs->ClearPref(kLocallyManagedUserCreationTransactionUserId); - prefs->CommitPendingWrite(); -} - UserFlow* UserManagerImpl::GetCurrentUserFlow() const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (!IsUserLoggedIn()) @@ -1812,23 +1547,23 @@ UserFlow* UserManagerImpl::GetCurrentUserFlow() const { return GetUserFlow(GetLoggedInUser()->email()); } -UserFlow* UserManagerImpl::GetUserFlow(const std::string& email) const { +UserFlow* UserManagerImpl::GetUserFlow(const std::string& user_id) const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - FlowMap::const_iterator it = specific_flows_.find(email); + FlowMap::const_iterator it = specific_flows_.find(user_id); if (it != specific_flows_.end()) return it->second; return GetDefaultUserFlow(); } -void UserManagerImpl::SetUserFlow(const std::string& email, UserFlow* flow) { +void UserManagerImpl::SetUserFlow(const std::string& user_id, UserFlow* flow) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - ResetUserFlow(email); - specific_flows_[email] = flow; + ResetUserFlow(user_id); + specific_flows_[user_id] = flow; } -void UserManagerImpl::ResetUserFlow(const std::string& email) { +void UserManagerImpl::ResetUserFlow(const std::string& user_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - FlowMap::iterator it = specific_flows_.find(email); + FlowMap::iterator it = specific_flows_.find(user_id); if (it != specific_flows_.end()) { delete it->second; specific_flows_.erase(it); @@ -1868,14 +1603,14 @@ bool UserManagerImpl::AreLocallyManagedUsersAllowed() const { } base::FilePath UserManagerImpl::GetUserProfileDir( - const std::string& email) const { + const std::string& user_id) const { // TODO(dpolukhin): Remove Chrome OS specific profile path logic from // ProfileManager and use only this function to construct profile path. // TODO(nkostylev): Cleanup profile dir related code paths crbug.com/294233 base::FilePath profile_dir; const CommandLine& command_line = *CommandLine::ForCurrentProcess(); if (command_line.HasSwitch(::switches::kMultiProfiles)) { - const User* user = FindUser(email); + const User* user = FindUser(user_id); if (user && !user->username_hash().empty()) { profile_dir = base::FilePath( chrome::kProfileDirPrefix + user->username_hash()); @@ -2041,9 +1776,9 @@ void UserManagerImpl::RestorePendingUserSessions() { LoginUtils::Get()->PrepareProfile(UserContext(user_id, std::string(), // password std::string(), // auth_code - user_id_hash), + user_id_hash, + false), // using_oauth std::string(), // display_email - false, // using_oauth false, // has_cookies true, // has_active_session this); @@ -2052,7 +1787,7 @@ void UserManagerImpl::RestorePendingUserSessions() { } } -void UserManagerImpl::SendRegularUserLoginMetrics(const std::string& email) { +void UserManagerImpl::SendRegularUserLoginMetrics(const std::string& user_id) { // If this isn't the first time Chrome was run after the system booted, // assume that Chrome was restarted because a previous session ended. if (!CommandLine::ForCurrentProcess()->HasSwitch( @@ -2061,7 +1796,7 @@ void UserManagerImpl::SendRegularUserLoginMetrics(const std::string& email) { g_browser_process->local_state()->GetString(kLastLoggedInRegularUser); const base::TimeDelta time_to_login = base::TimeTicks::Now() - manager_creation_time_; - if (!last_email.empty() && email != last_email && + if (!last_email.empty() && user_id != last_email && time_to_login.InSeconds() <= kLogoutToLoginDelayMaxSec) { UMA_HISTOGRAM_CUSTOM_COUNTS("UserManager.LogoutToLoginDelay", time_to_login.InSeconds(), 0, kLogoutToLoginDelayMaxSec, 50); diff --git a/chrome/browser/chromeos/login/user_manager_impl.h b/chrome/browser/chromeos/login/user_manager_impl.h index 238f0c8be8..df38699fa7 100644 --- a/chrome/browser/chromeos/login/user_manager_impl.h +++ b/chrome/browser/chromeos/login/user_manager_impl.h @@ -39,6 +39,7 @@ namespace chromeos { class MultiProfileFirstRunNotification; class MultiProfileUserController; class RemoveUserDelegate; +class SupervisedUserManagerImpl; class SessionLengthLimiter; struct UpdateUserAccountDataCallbackData; @@ -56,27 +57,24 @@ class UserManagerImpl // UserManager implementation: virtual void Shutdown() OVERRIDE; virtual UserImageManager* GetUserImageManager() OVERRIDE; + virtual SupervisedUserManager* GetSupervisedUserManager() OVERRIDE; virtual const UserList& GetUsers() const OVERRIDE; virtual UserList GetUsersAdmittedForMultiProfile() const OVERRIDE; virtual const UserList& GetLoggedInUsers() const OVERRIDE; virtual const UserList& GetLRULoggedInUsers() OVERRIDE; virtual UserList GetUnlockUsers() const OVERRIDE; virtual const std::string& GetOwnerEmail() OVERRIDE; - virtual void UserLoggedIn(const std::string& email, - const std::string& username_hash, + virtual void UserLoggedIn(const std::string& user_id, + const std::string& user_id_hash, bool browser_restart) OVERRIDE; - virtual void SwitchActiveUser(const std::string& email) OVERRIDE; + virtual void SwitchActiveUser(const std::string& user_id) OVERRIDE; virtual void RestoreActiveSessions() OVERRIDE; virtual void SessionStarted() OVERRIDE; - virtual void RemoveUser(const std::string& email, + virtual void RemoveUser(const std::string& user_id, RemoveUserDelegate* delegate) OVERRIDE; - virtual void RemoveUserFromList(const std::string& email) OVERRIDE; - virtual bool IsKnownUser(const std::string& email) const OVERRIDE; - virtual const User* FindUser(const std::string& email) const OVERRIDE; - virtual const User* FindLocallyManagedUser( - const string16& display_name) const OVERRIDE; - virtual const User* FindLocallyManagedUserBySyncId( - const std::string& sync_id) const OVERRIDE; + virtual void RemoveUserFromList(const std::string& user_id) OVERRIDE; + virtual bool IsKnownUser(const std::string& user_id) const OVERRIDE; + virtual const User* FindUser(const std::string& user_id) const OVERRIDE; virtual const User* GetLoggedInUser() const OVERRIDE; virtual User* GetLoggedInUser() OVERRIDE; virtual const User* GetActiveUser() const OVERRIDE; @@ -84,27 +82,19 @@ class UserManagerImpl virtual const User* GetPrimaryUser() const OVERRIDE; virtual User* GetUserByProfile(Profile* profile) const OVERRIDE; virtual void SaveUserOAuthStatus( - const std::string& username, + const std::string& user_id, User::OAuthTokenStatus oauth_token_status) OVERRIDE; - virtual void SaveUserDisplayName(const std::string& username, + virtual void SaveUserDisplayName(const std::string& user_id, const string16& display_name) OVERRIDE; - virtual void UpdateUserAccountData(const std::string& username, + virtual void UpdateUserAccountData(const std::string& user_id, const string16& display_name, const std::string& locale) OVERRIDE; virtual string16 GetUserDisplayName( - const std::string& username) const OVERRIDE; - virtual void SaveUserDisplayEmail(const std::string& username, + const std::string& user_id) const OVERRIDE; + virtual void SaveUserDisplayEmail(const std::string& user_id, const std::string& display_email) OVERRIDE; virtual std::string GetUserDisplayEmail( - const std::string& username) const OVERRIDE; - virtual std::string GetManagedUserSyncId( - const std::string& managed_user_id) const OVERRIDE; - virtual string16 GetManagerDisplayNameForManagedUser( - const std::string& managed_user_id) const OVERRIDE; - virtual std::string GetManagerUserIdForManagedUser( - const std::string& managed_user_id) const OVERRIDE; - virtual std::string GetManagerDisplayEmailForManagedUser( - const std::string& managed_user_id) const OVERRIDE; + const std::string& user_id) const OVERRIDE; virtual bool IsCurrentUserOwner() const OVERRIDE; virtual bool IsCurrentUserNew() const OVERRIDE; virtual bool IsCurrentUserNonCryptohomeDataEphemeral() const OVERRIDE; @@ -121,7 +111,7 @@ class UserManagerImpl virtual bool UserSessionsRestored() const OVERRIDE; virtual bool HasBrowserRestarted() const OVERRIDE; virtual bool IsUserNonCryptohomeDataEphemeral( - const std::string& email) const OVERRIDE; + const std::string& user_id) const OVERRIDE; virtual void AddObserver(UserManager::Observer* obs) OVERRIDE; virtual void RemoveObserver(UserManager::Observer* obs) OVERRIDE; virtual void AddSessionStateObserver( @@ -129,22 +119,11 @@ class UserManagerImpl virtual void RemoveSessionStateObserver( UserManager::UserSessionStateObserver* obs) OVERRIDE; virtual void NotifyLocalStateChanged() OVERRIDE; - virtual const User* CreateLocallyManagedUserRecord( - const std::string& manager_id, - const std::string& local_user_id, - const std::string& sync_user_id, - const string16& display_name) OVERRIDE; - virtual std::string GenerateUniqueLocallyManagedUserId() OVERRIDE; - virtual void StartLocallyManagedUserCreationTransaction( - const string16& display_name) OVERRIDE; - virtual void SetLocallyManagedUserCreationTransactionUserId( - const std::string& email) OVERRIDE; - virtual void CommitLocallyManagedUserCreationTransaction() OVERRIDE; virtual UserFlow* GetCurrentUserFlow() const OVERRIDE; - virtual UserFlow* GetUserFlow(const std::string& email) const OVERRIDE; - virtual void SetUserFlow(const std::string& email, UserFlow* flow) OVERRIDE; - virtual void ResetUserFlow(const std::string& email) OVERRIDE; + virtual UserFlow* GetUserFlow(const std::string& user_id) const OVERRIDE; + virtual void SetUserFlow(const std::string& user_id, UserFlow* flow) OVERRIDE; + virtual void ResetUserFlow(const std::string& user_id) OVERRIDE; virtual bool GetAppModeChromeClientOAuthInfo( std::string* chrome_client_id, std::string* chrome_client_secret) OVERRIDE; @@ -153,7 +132,7 @@ class UserManagerImpl const std::string& chrome_client_secret) OVERRIDE; virtual bool AreLocallyManagedUsersAllowed() const OVERRIDE; virtual base::FilePath GetUserProfileDir( - const std::string& email) const OVERRIDE; + const std::string& user_id) const OVERRIDE; // content::NotificationObserver implementation. virtual void Observe(int type, @@ -168,6 +147,7 @@ class UserManagerImpl OVERRIDE; private: + friend class SupervisedUserManagerImpl; friend class UserManager; friend class WallpaperManager; friend class UserManagerTest; @@ -199,26 +179,26 @@ class UserManagerImpl // Returns the user with the given email address if found in the persistent // list or currently logged in as ephemeral. Returns |NULL| otherwise. // Same as FindUser but returns non-const pointer to User object. - User* FindUserAndModify(const std::string& email); + User* FindUserAndModify(const std::string& user_id); // Returns the user with the given email address if found in the persistent // list. Returns |NULL| otherwise. - const User* FindUserInList(const std::string& email) const; + const User* FindUserInList(const std::string& user_id) const; // Same as FindUserInList but returns non-const pointer to User object. - User* FindUserInListAndModify(const std::string& email); + User* FindUserInListAndModify(const std::string& user_id); // Indicates that a user just logged in as guest. void GuestUserLoggedIn(); // Indicates that a regular user just logged in. - void RegularUserLoggedIn(const std::string& email); + void RegularUserLoggedIn(const std::string& user_id); // Indicates that a regular user just logged in as ephemeral. - void RegularUserLoggedInAsEphemeral(const std::string& email); + void RegularUserLoggedInAsEphemeral(const std::string& user_id); // Indicates that a locally managed user just logged in. - void LocallyManagedUserLoggedIn(const std::string& username); + void LocallyManagedUserLoggedIn(const std::string& user_id); // Indicates that a user just logged into a public session. void PublicAccountUserLoggedIn(User* user); @@ -234,7 +214,7 @@ class UserManagerImpl void NotifyOnLogin(); // Reads user's oauth token status from local state preferences. - User::OAuthTokenStatus LoadUserOAuthStatus(const std::string& username) const; + User::OAuthTokenStatus LoadUserOAuthStatus(const std::string& user_id) const; void SetCurrentUserIsOwner(bool is_current_user_owner); @@ -243,12 +223,12 @@ class UserManagerImpl // Removes data stored or cached outside the user's cryptohome (wallpaper, // avatar, OAuth token status, display name, display email). - void RemoveNonCryptohomeData(const std::string& email); + void RemoveNonCryptohomeData(const std::string& user_id); // Removes a regular or locally managed user from the user list. // Returns the user if found or NULL otherwise. // Also removes the user from the persistent user list. - User* RemoveRegularOrLocallyManagedUserFromList(const std::string& username); + User* RemoveRegularOrLocallyManagedUserFromList(const std::string& user_id); // If data for a public account is marked as pending removal and the user is // no longer logged into that account, removes the data. @@ -271,7 +251,7 @@ class UserManagerImpl // Updates the display name for public account |username| from policy settings // associated with that username. - void UpdatePublicAccountDisplayName(const std::string& username); + void UpdatePublicAccountDisplayName(const std::string& user_id); // Notifies the UI about a change to the user list. void NotifyUserListChanged(); @@ -291,12 +271,6 @@ class UserManagerImpl // Notifies observers that user pending sessions restore has finished. void NotifyPendingUserSessionsRestoreFinished(); - // Returns true if there is non-committed user creation transaction. - bool HasFailedLocallyManagedUserCreationTransaction(); - - // Attempts to clean up data that could be left from failed user creation. - void RollbackLocallyManagedUserCreationTransaction(); - // Lazily creates default user flow. UserFlow* GetDefaultUserFlow() const; @@ -306,6 +280,10 @@ class UserManagerImpl // Insert |user| at the front of the LRU user list.. void SetLRUUser(User* user); + // Adds |user| to users list, and adds it to front of LRU list. It is assumed + // that there is no user with same id. + void AddUserRecord(User* user); + // Callback to process RetrieveActiveSessions() request results. void OnRestoreActiveSessions( const SessionManagerClient::ActiveSessionsMap& sessions, @@ -318,17 +296,17 @@ class UserManagerImpl void RestorePendingUserSessions(); // Sends metrics in response to a regular user logging in. - void SendRegularUserLoginMetrics(const std::string& email); + void SendRegularUserLoginMetrics(const std::string& user_id); // UpdateUserAccountData() + SaveUserDisplayName() . - void UpdateUserAccountDataImpl(const std::string& username, + void UpdateUserAccountDataImpl(const std::string& user_id, const string16& display_name, const std::string* locale); // Account locale needs to be translated to device locale. // This might be called as callback after FILE thread translates locale. void UpdateUserAccountDataImplCallback( - const std::string& username, + const std::string& user_id, const string16& display_name, const std::string* resolved_account_locale); @@ -424,6 +402,9 @@ class UserManagerImpl // User avatar manager. scoped_ptr<UserImageManagerImpl> user_image_manager_; + // Supervised user manager. + scoped_ptr<SupervisedUserManagerImpl> supervised_user_manager_; + // Session length limiter. scoped_ptr<SessionLengthLimiter> session_length_limiter_; diff --git a/chrome/browser/chromeos/login/wallpaper_manager.cc b/chrome/browser/chromeos/login/wallpaper_manager.cc index 7fafe4dd2a..eead099677 100644 --- a/chrome/browser/chromeos/login/wallpaper_manager.cc +++ b/chrome/browser/chromeos/login/wallpaper_manager.cc @@ -57,9 +57,6 @@ const int kDefaultEncodingQuality = 90; // Deprecated. Will remove this const char after done migration. const char kUserWallpapers[] = "UserWallpapers"; -const int kThumbnailWidth = 128; -const int kThumbnailHeight = 80; - const int kCacheWallpaperDelayMs = 500; // A dictionary pref that maps usernames to wallpaper properties. diff --git a/chrome/browser/chromeos/login/wallpaper_manager_unittest.cc b/chrome/browser/chromeos/login/wallpaper_manager_unittest.cc index 0e3e7a8504..8f3391baa7 100644 --- a/chrome/browser/chromeos/login/wallpaper_manager_unittest.cc +++ b/chrome/browser/chromeos/login/wallpaper_manager_unittest.cc @@ -30,16 +30,9 @@ using namespace ash; -namespace { - const char kTestUser1[] = "test-user@example.com"; const char kTestUser1Hash[] = "test-user@example.com-hash"; -const int kLargeWallpaperResourceId = IDR_AURA_WALLPAPER_DEFAULT_LARGE; -const int kSmallWallpaperResourceId = IDR_AURA_WALLPAPER_DEFAULT_SMALL; - -} // namespace - namespace chromeos { class WallpaperManagerTest : public test::AshTestBase { diff --git a/chrome/browser/chromeos/login/webui_login_view.cc b/chrome/browser/chromeos/login/webui_login_view.cc index 3e14fff2b5..8bd3004463 100644 --- a/chrome/browser/chromeos/login/webui_login_view.cc +++ b/chrome/browser/chromeos/login/webui_login_view.cc @@ -31,7 +31,6 @@ #include "components/web_modal/web_contents_modal_dialog_manager.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/render_view_host.h" -#include "content/public/browser/render_view_host_observer.h" #include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_view.h" @@ -61,39 +60,6 @@ const char kAccelNameDeviceRequisition[] = "device_requisition"; const char kAccelNameDeviceRequisitionRemora[] = "device_requisition_remora"; const char kAccelNameAppLaunchBailout[] = "app_launch_bailout"; -// Observes IPC messages from the FrameSniffer and notifies JS if error -// appears. -class SnifferObserver : public content::RenderViewHostObserver { - public: - SnifferObserver(RenderViewHost* host, content::WebUI* webui) - : content::RenderViewHostObserver(host), webui_(webui) { - DCHECK(webui_); - Send(new ChromeViewMsg_StartFrameSniffer(routing_id(), - UTF8ToUTF16("gaia-frame"))); - } - - virtual ~SnifferObserver() {} - - // IPC::Listener implementation. - virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(SnifferObserver, message) - IPC_MESSAGE_HANDLER(ChromeViewHostMsg_FrameLoadingError, OnError) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; - } - - private: - void OnError(int error) { - base::FundamentalValue error_value(error); - webui_->CallJavascriptFunction("login.GaiaSigninScreen.onFrameError", - error_value); - } - - content::WebUI* webui_; -}; - // A class to change arrow key traversal behavior when it's alive. class ScopedArrowKeyTraversal { public: @@ -201,13 +167,10 @@ void WebUILoginView::Init() { SetDelegate(this); web_contents->SetDelegate(this); + WebContentsObserver::Observe(web_contents); renderer_preferences_util::UpdateFromSystemSettings( web_contents->GetMutableRendererPrefs(), signin_profile); - - registrar_.Add(this, - content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED, - content::Source<WebContents>(web_contents)); } const char* WebUILoginView::GetClassName() const { @@ -362,12 +325,6 @@ void WebUILoginView::Observe(int type, registrar_.RemoveAll(); break; } - case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED: { - RenderViewHost* render_view_host = - content::Details<RenderViewHost>(details).ptr(); - new SnifferObserver(render_view_host, GetWebUI()); - break; - } default: NOTREACHED() << "Unexpected notification " << type; } @@ -434,6 +391,22 @@ void WebUILoginView::RequestMediaAccessPermission( NOTREACHED() << "Media stream not allowed for WebUI"; } +void WebUILoginView::DidFailProvisionalLoad( + int64 frame_id, + const string16& frame_unique_name, + bool is_main_frame, + const GURL& validated_url, + int error_code, + const string16& error_description, + content::RenderViewHost* render_view_host) { + if (frame_unique_name != UTF8ToUTF16("gaia-frame")) + return; + + base::FundamentalValue error_value(-error_code); + GetWebUI()->CallJavascriptFunction("login.GaiaSigninScreen.onFrameError", + error_value); +} + void WebUILoginView::OnLoginPromptVisible() { // If we're hidden than will generate this signal once we're shown. if (is_hidden_ || webui_visible_) { diff --git a/chrome/browser/chromeos/login/webui_login_view.h b/chrome/browser/chromeos/login/webui_login_view.h index 01e378628b..079328020e 100644 --- a/chrome/browser/chromeos/login/webui_login_view.h +++ b/chrome/browser/chromeos/login/webui_login_view.h @@ -16,6 +16,7 @@ #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" #include "content/public/browser/web_contents_delegate.h" +#include "content/public/browser/web_contents_observer.h" #include "ui/views/controls/webview/unhandled_keyboard_event_handler.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" @@ -38,6 +39,7 @@ namespace chromeos { // WebUI based start up and lock screens. It contains a WebView. class WebUILoginView : public views::View, public content::WebContentsDelegate, + public content::WebContentsObserver, public content::NotificationObserver, public ChromeWebModalDialogManagerDelegate, public web_modal::WebContentsModalDialogHost { @@ -135,6 +137,16 @@ class WebUILoginView : public views::View, const content::MediaStreamRequest& request, const content::MediaResponseCallback& callback) OVERRIDE; + // Overridden from content::WebContentsObserver. + virtual void DidFailProvisionalLoad( + int64 frame_id, + const string16& frame_unique_name, + bool is_main_frame, + const GURL& validated_url, + int error_code, + const string16& error_description, + content::RenderViewHost* render_view_host) OVERRIDE; + // Performs series of actions when login prompt is considered // to be ready and visible. // 1. Emits LoginPromptVisible signal if needed diff --git a/chrome/browser/chromeos/login/webui_screen_locker.cc b/chrome/browser/chromeos/login/webui_screen_locker.cc index a61f54a66e..ee31aab878 100644 --- a/chrome/browser/chromeos/login/webui_screen_locker.cc +++ b/chrome/browser/chromeos/login/webui_screen_locker.cc @@ -69,9 +69,6 @@ void WebUIScreenLocker::LockScreen() { LoadURL(GURL(kLoginURL)); lock_window->Grab(); - // Subscribe to crash events. - content::WebContentsObserver::Observe(GetWebContents()); - login_display_.reset(new WebUILoginDisplay(this)); login_display_->set_background_bounds(bounds); login_display_->set_parent_window(GetNativeWindow()); diff --git a/chrome/browser/chromeos/login/webui_screen_locker.h b/chrome/browser/chromeos/login/webui_screen_locker.h index ad49795aab..1d97b36c72 100644 --- a/chrome/browser/chromeos/login/webui_screen_locker.h +++ b/chrome/browser/chromeos/login/webui_screen_locker.h @@ -19,7 +19,6 @@ #include "chromeos/dbus/power_manager_client.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" -#include "content/public/browser/web_contents_observer.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_observer.h" @@ -49,8 +48,7 @@ class WebUIScreenLocker : public WebUILoginView, public LockWindow::Observer, public ash::LockStateObserver, public views::WidgetObserver, - public PowerManagerClient::Observer, - public content::WebContentsObserver { + public PowerManagerClient::Observer { public: explicit WebUIScreenLocker(ScreenLocker* screen_locker); diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc index 5bd9c5f7f1..84c9500938 100644 --- a/chrome/browser/chromeos/login/wizard_controller.cc +++ b/chrome/browser/chromeos/login/wizard_controller.cc @@ -19,7 +19,6 @@ #include "base/prefs/pref_service.h" #include "base/threading/thread_restrictions.h" #include "base/values.h" -#include "chrome/app/breakpad_linux.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" @@ -58,32 +57,19 @@ #include "chromeos/dbus/session_manager_client.h" #include "chromeos/network/network_state_handler.h" #include "chromeos/settings/cros_settings_names.h" +#include "components/breakpad/app/breakpad_linux.h" #include "content/public/browser/browser_thread.h" #include "ui/base/accelerators/accelerator.h" #include "ui/base/l10n/l10n_util.h" using content::BrowserThread; -namespace { - -// A string pref with initial locale set in VPD or manifest. -const char kInitialLocale[] = "intl.initial_locale"; - -// A boolean pref of the OOBE complete flag (first OOBE part before login). -const char kOobeComplete[] = "OobeComplete"; - -// A boolean pref of the device registered flag (second part after first login). -const char kDeviceRegistered[] = "DeviceRegistered"; - -// Time in seconds that we wait for the device to reboot. // If reboot didn't happen, ask user to reboot device manually. const int kWaitForRebootTimeSec = 3; // Interval in ms which is used for smooth screen showing. static int kShowDelayMs = 400; -} // namespace - namespace chromeos { const char WizardController::kNetworkScreenName[] = "network"; @@ -496,7 +482,7 @@ void WizardController::OnEulaAccepted() { #if defined(GOOGLE_CHROME_BUILD) // The crash reporter initialization needs IO to complete. base::ThreadRestrictions::ScopedAllowIO allow_io; - InitCrashReporter(); + breakpad::InitCrashReporter(); #endif } @@ -634,9 +620,7 @@ void WizardController::PerformPostEulaActions() { NetworkStateHandler::kDefaultCheckPortalList); host_->CheckForAutoEnrollment(); host_->PrewarmAuthentication(); - NetworkPortalDetector* detector = NetworkPortalDetector::GetInstance(); - if (NetworkPortalDetector::IsEnabledInCommandLine() && detector) - detector->Enable(true); + NetworkPortalDetector::Get()->Enable(true); } void WizardController::PerformPostUpdateActions() { diff --git a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc index b8a2a958e3..79b278f573 100644 --- a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc +++ b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc @@ -42,8 +42,8 @@ #include "chromeos/chromeos_switches.h" #include "chromeos/chromeos_test_utils.h" #include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_dbus_thread_manager.h" #include "chromeos/dbus/fake_session_manager_client.h" -#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" #include "chromeos/network/network_state_handler.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_utils.h" @@ -352,7 +352,7 @@ IN_PROC_BROWSER_TEST_F(WizardControllerFlowTest, WizardController::default_controller()->GetEnrollmentScreen(); EXPECT_EQ(screen, WizardController::default_controller()->current_screen()); // This is the main expectation: after auto-enrollment, login is resumed. - EXPECT_CALL(mock_consumer, OnLoginSuccess(_, _, _)).Times(1); + EXPECT_CALL(mock_consumer, OnLoginSuccess(_)).Times(1); OnExit(ScreenObserver::ENTERPRISE_AUTO_MAGIC_ENROLLMENT_COMPLETED); // Prevent browser launch when the profile is prepared: browser_shutdown::SetTryingToQuit(true); @@ -408,11 +408,11 @@ class WizardControllerBrokenLocalStateTest : public WizardControllerTest { virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { WizardControllerTest::SetUpInProcessBrowserTestFixture(); - MockDBusThreadManagerWithoutGMock* mock_dbus_thread_manager = - new MockDBusThreadManagerWithoutGMock(); + FakeDBusThreadManager* fake_dbus_thread_manager = + new FakeDBusThreadManager(); fake_session_manager_client_ = - mock_dbus_thread_manager->fake_session_manager_client(); - DBusThreadManager::InitializeForTesting(mock_dbus_thread_manager); + fake_dbus_thread_manager->fake_session_manager_client(); + DBusThreadManager::InitializeForTesting(fake_dbus_thread_manager); } virtual void SetUpOnMainThread() OVERRIDE { diff --git a/chrome/browser/chromeos/mobile_config.cc b/chrome/browser/chromeos/mobile_config.cc index a0503ca800..52d4dbb0d5 100644 --- a/chrome/browser/chromeos/mobile_config.cc +++ b/chrome/browser/chromeos/mobile_config.cc @@ -22,7 +22,6 @@ using content::BrowserThread; namespace { // Config attributes names. -const char kVersionAttr[] = "version"; const char kAcceptedConfigVersion[] = "1.0"; const char kDefaultAttr[] = "default"; @@ -42,7 +41,6 @@ const char kInfoURLAttr[] = "info_url"; const char kNotificationCountAttr[] = "notification_count"; const char kDealExpireDateAttr[] = "expire_date"; const char kLocalizedContentAttr[] = "localized_content"; -const char kNotificationTextAttr[] = "notification_text"; // Initial locale carrier config attributes. const char kInitialLocalesAttr[] = "initial_locales"; diff --git a/chrome/browser/chromeos/net/network_portal_detector.cc b/chrome/browser/chromeos/net/network_portal_detector.cc index 1f7bf5d217..11d4074d06 100644 --- a/chrome/browser/chromeos/net/network_portal_detector.cc +++ b/chrome/browser/chromeos/net/network_portal_detector.cc @@ -8,7 +8,7 @@ #include "base/logging.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/net/network_portal_detector_impl.h" -#include "chrome/browser/chromeos/net/network_portal_detector_stub.h" +#include "chrome/browser/chromeos/net/network_portal_detector_test_impl.h" #include "chrome/common/chrome_switches.h" #include "chromeos/chromeos_switches.h" @@ -17,47 +17,82 @@ namespace chromeos { namespace { NetworkPortalDetector* g_network_portal_detector = NULL; +bool g_network_portal_detector_set_for_testing = false; bool IsTestMode() { return CommandLine::ForCurrentProcess()->HasSwitch(::switches::kTestType); } -} // namespace - -NetworkPortalDetector::NetworkPortalDetector() { +bool IsEnabledInCommandLine() { + return !CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableChromeCaptivePortalDetector); } -NetworkPortalDetector::~NetworkPortalDetector() { +// Stub implementation of NetworkPortalDetector. +class NetworkPortalDetectorStubImpl : public NetworkPortalDetector { + protected: + // NetworkPortalDetector implementation: + virtual void AddObserver(Observer* /* observer */) OVERRIDE {} + virtual void AddAndFireObserver(Observer* observer) OVERRIDE { + if (observer) + observer->OnPortalDetectionCompleted(NULL, CaptivePortalState()); + } + virtual void RemoveObserver(Observer* /* observer */) OVERRIDE {} + virtual CaptivePortalState GetCaptivePortalState( + const NetworkState* /* network */) OVERRIDE { + return CaptivePortalState(); + } + virtual bool IsEnabled() OVERRIDE { return false; } + virtual void Enable(bool /* start_detection */) OVERRIDE {} + virtual bool StartDetectionIfIdle() OVERRIDE { return false; } + virtual void EnableLazyDetection() OVERRIDE {} + virtual void DisableLazyDetection() OVERRIDE {} +}; + +} // namespace + +void NetworkPortalDetector::InitializeForTesting( + NetworkPortalDetector* network_portal_detector) { + CHECK(!g_network_portal_detector) + << "NetworkPortalDetector::InitializeForTesting() is called after " + << "Initialize()"; + CHECK(network_portal_detector); + g_network_portal_detector = network_portal_detector; + g_network_portal_detector_set_for_testing = true; } // static -NetworkPortalDetector* NetworkPortalDetector::CreateInstance() { - DCHECK(!g_network_portal_detector); - CHECK(NetworkPortalDetector::IsEnabledInCommandLine()); - if (IsTestMode()) { - g_network_portal_detector = new NetworkPortalDetectorStub(); +void NetworkPortalDetector::Initialize() { + if (g_network_portal_detector_set_for_testing) + return; + CHECK(!g_network_portal_detector) + << "NetworkPortalDetector::Initialize() is called twice"; + if (!IsEnabledInCommandLine() || IsTestMode()) { + g_network_portal_detector = new NetworkPortalDetectorStubImpl(); } else { CHECK(g_browser_process); CHECK(g_browser_process->system_request_context()); g_network_portal_detector = new NetworkPortalDetectorImpl( g_browser_process->system_request_context()); } - return g_network_portal_detector; } // static -NetworkPortalDetector* NetworkPortalDetector::GetInstance() { - if (!NetworkPortalDetector::IsEnabledInCommandLine()) - return NULL; - if (!g_network_portal_detector) - return CreateInstance(); - return g_network_portal_detector; +void NetworkPortalDetector::Shutdown() { + CHECK(g_network_portal_detector || g_network_portal_detector_set_for_testing) + << "NetworkPortalDetectorImpl::Shutdown() is called " + << "without previous call to Initialize()"; + if (g_network_portal_detector) { + delete g_network_portal_detector; + g_network_portal_detector = NULL; + } } // static -bool NetworkPortalDetector::IsEnabledInCommandLine() { - return !CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableChromeCaptivePortalDetector); +NetworkPortalDetector* NetworkPortalDetector::Get() { + CHECK(g_network_portal_detector) + << "NetworkPortalDetector::Get() called before Initialize()"; + return g_network_portal_detector; } } // namespace chromeos diff --git a/chrome/browser/chromeos/net/network_portal_detector.h b/chrome/browser/chromeos/net/network_portal_detector.h index dd15575ab9..9568b53320 100644 --- a/chrome/browser/chromeos/net/network_portal_detector.h +++ b/chrome/browser/chromeos/net/network_portal_detector.h @@ -53,9 +53,6 @@ class NetworkPortalDetector { virtual ~Observer() {} }; - virtual void Init() = 0; - virtual void Shutdown() = 0; - // Adds |observer| to the observers list. virtual void AddObserver(Observer* observer) = 0; @@ -100,18 +97,24 @@ class NetworkPortalDetector { // Dizables lazy detection mode. virtual void DisableLazyDetection() = 0; + // Initializes network portal detector for testing. The + // |network_portal_detector| will be owned by the internal pointer + // and deleted by Shutdown(). + static void InitializeForTesting( + NetworkPortalDetector* network_portal_detector); + // Creates an instance of the NetworkPortalDetector. - static NetworkPortalDetector* CreateInstance(); + static void Initialize(); - // Gets the instance of the NetworkPortalDetector. - static NetworkPortalDetector* GetInstance(); + // Deletes the instance of the NetworkPortalDetector. + static void Shutdown(); - // Returns true is NetworkPortalDetector service is enabled in command line. - static bool IsEnabledInCommandLine(); + // Gets the instance of the NetworkPortalDetector. + static NetworkPortalDetector* Get(); protected: - NetworkPortalDetector(); - virtual ~NetworkPortalDetector(); + NetworkPortalDetector() {} + virtual ~NetworkPortalDetector() {} private: DISALLOW_COPY_AND_ASSIGN(NetworkPortalDetector); diff --git a/chrome/browser/chromeos/net/network_portal_detector_impl.cc b/chrome/browser/chromeos/net/network_portal_detector_impl.cc index c8c454ffe0..0088b91162 100644 --- a/chrome/browser/chromeos/net/network_portal_detector_impl.cc +++ b/chrome/browser/chromeos/net/network_portal_detector_impl.cc @@ -71,7 +71,8 @@ std::string CaptivePortalStatusString( NetworkPortalDetectorImpl::NetworkPortalDetectorImpl( const scoped_refptr<net::URLRequestContextGetter>& request_context) - : test_url_(CaptivePortalDetector::kDefaultURL), + : state_(STATE_IDLE), + test_url_(CaptivePortalDetector::kDefaultURL), enabled_(false), weak_ptr_factory_(this), attempt_count_(0), @@ -91,20 +92,12 @@ NetworkPortalDetectorImpl::NetworkPortalDetectorImpl( registrar_.Add(this, chrome::NOTIFICATION_AUTH_CANCELLED, content::NotificationService::AllSources()); -} - -NetworkPortalDetectorImpl::~NetworkPortalDetectorImpl() { -} -void NetworkPortalDetectorImpl::Init() { - DCHECK(CalledOnValidThread()); - - state_ = STATE_IDLE; NetworkHandler::Get()->network_state_handler()->AddObserver( this, FROM_HERE); } -void NetworkPortalDetectorImpl::Shutdown() { +NetworkPortalDetectorImpl::~NetworkPortalDetectorImpl() { DCHECK(CalledOnValidThread()); detection_task_.Cancel(); diff --git a/chrome/browser/chromeos/net/network_portal_detector_impl.h b/chrome/browser/chromeos/net/network_portal_detector_impl.h index cf0c4e9537..9c7b68ebaf 100644 --- a/chrome/browser/chromeos/net/network_portal_detector_impl.h +++ b/chrome/browser/chromeos/net/network_portal_detector_impl.h @@ -47,8 +47,6 @@ class NetworkPortalDetectorImpl virtual ~NetworkPortalDetectorImpl(); // NetworkPortalDetector implementation: - virtual void Init() OVERRIDE; - virtual void Shutdown() OVERRIDE; virtual void AddObserver(Observer* observer) OVERRIDE; virtual void AddAndFireObserver(Observer* observer) OVERRIDE; virtual void RemoveObserver(Observer* observer) OVERRIDE; diff --git a/chrome/browser/chromeos/net/network_portal_detector_impl_unittest.cc b/chrome/browser/chromeos/net/network_portal_detector_impl_unittest.cc index 8f3c18af5b..42e1657e39 100644 --- a/chrome/browser/chromeos/net/network_portal_detector_impl_unittest.cc +++ b/chrome/browser/chromeos/net/network_portal_detector_impl_unittest.cc @@ -50,7 +50,6 @@ class NetworkPortalDetectorImplTest profile_.reset(new TestingProfile()); network_portal_detector_.reset( new NetworkPortalDetectorImpl(profile_->GetRequestContext())); - network_portal_detector_->Init(); network_portal_detector_->Enable(false); set_detector(network_portal_detector_->captive_portal_detector_.get()); @@ -60,7 +59,7 @@ class NetworkPortalDetectorImplTest } virtual void TearDown() { - network_portal_detector_->Shutdown(); + network_portal_detector_.reset(); profile_.reset(); NetworkHandler::Shutdown(); DBusThreadManager::Shutdown(); diff --git a/chrome/browser/chromeos/net/network_portal_detector_stub.cc b/chrome/browser/chromeos/net/network_portal_detector_test_impl.cc index 2fd08aa811..473f0d70b1 100644 --- a/chrome/browser/chromeos/net/network_portal_detector_stub.cc +++ b/chrome/browser/chromeos/net/network_portal_detector_test_impl.cc @@ -1,19 +1,19 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Copyright 2013 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/chromeos/net/network_portal_detector_stub.h" +#include "chrome/browser/chromeos/net/network_portal_detector_test_impl.h" #include "chromeos/network/network_state.h" namespace chromeos { -NetworkPortalDetectorStub::NetworkPortalDetectorStub() {} +NetworkPortalDetectorTestImpl::NetworkPortalDetectorTestImpl() {} -NetworkPortalDetectorStub::~NetworkPortalDetectorStub() { +NetworkPortalDetectorTestImpl::~NetworkPortalDetectorTestImpl() { } -void NetworkPortalDetectorStub::SetDefaultNetworkPathForTesting( +void NetworkPortalDetectorTestImpl::SetDefaultNetworkPathForTesting( const std::string& service_path) { if (service_path.empty()) default_network_.reset(); @@ -21,14 +21,14 @@ void NetworkPortalDetectorStub::SetDefaultNetworkPathForTesting( default_network_.reset(new NetworkState(service_path)); } -void NetworkPortalDetectorStub::SetDetectionResultsForTesting( +void NetworkPortalDetectorTestImpl::SetDetectionResultsForTesting( const std::string& service_path, const CaptivePortalState& state) { if (!service_path.empty()) portal_state_map_[service_path] = state; } -void NetworkPortalDetectorStub::NotifyObserversForTesting() { +void NetworkPortalDetectorTestImpl::NotifyObserversForTesting() { CaptivePortalState state; if (default_network_ && portal_state_map_.count(default_network_->path())) { @@ -38,18 +38,12 @@ void NetworkPortalDetectorStub::NotifyObserversForTesting() { OnPortalDetectionCompleted(default_network_.get(), state)); } -void NetworkPortalDetectorStub::Init() { -} - -void NetworkPortalDetectorStub::Shutdown() { -} - -void NetworkPortalDetectorStub::AddObserver(Observer* observer) { +void NetworkPortalDetectorTestImpl::AddObserver(Observer* observer) { if (observer && !observers_.HasObserver(observer)) observers_.AddObserver(observer); } -void NetworkPortalDetectorStub::AddAndFireObserver(Observer* observer) { +void NetworkPortalDetectorTestImpl::AddAndFireObserver(Observer* observer) { AddObserver(observer); if (!observer) return; @@ -64,34 +58,34 @@ void NetworkPortalDetectorStub::AddAndFireObserver(Observer* observer) { } } -void NetworkPortalDetectorStub::RemoveObserver(Observer* observer) { +void NetworkPortalDetectorTestImpl::RemoveObserver(Observer* observer) { if (observer) observers_.RemoveObserver(observer); } NetworkPortalDetector::CaptivePortalState -NetworkPortalDetectorStub::GetCaptivePortalState( +NetworkPortalDetectorTestImpl::GetCaptivePortalState( const chromeos::NetworkState* network) { if (!network || !portal_state_map_.count(network->path())) return CaptivePortalState(); return portal_state_map_[network->path()]; } -bool NetworkPortalDetectorStub::IsEnabled() { +bool NetworkPortalDetectorTestImpl::IsEnabled() { return true; } -void NetworkPortalDetectorStub::Enable(bool start_detection) { +void NetworkPortalDetectorTestImpl::Enable(bool start_detection) { } -bool NetworkPortalDetectorStub::StartDetectionIfIdle() { +bool NetworkPortalDetectorTestImpl::StartDetectionIfIdle() { return false; } -void NetworkPortalDetectorStub::EnableLazyDetection() { +void NetworkPortalDetectorTestImpl::EnableLazyDetection() { } -void NetworkPortalDetectorStub::DisableLazyDetection() { +void NetworkPortalDetectorTestImpl::DisableLazyDetection() { } } // namespace chromeos diff --git a/chrome/browser/chromeos/net/network_portal_detector_stub.h b/chrome/browser/chromeos/net/network_portal_detector_test_impl.h index 9494109a70..9d6f9bab6b 100644 --- a/chrome/browser/chromeos/net/network_portal_detector_stub.h +++ b/chrome/browser/chromeos/net/network_portal_detector_test_impl.h @@ -1,9 +1,9 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Copyright 2013 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_CHROMEOS_NET_NETWORK_PORTAL_DETECTOR_STUB_H_ -#define CHROME_BROWSER_CHROMEOS_NET_NETWORK_PORTAL_DETECTOR_STUB_H_ +#ifndef CHROME_BROWSER_CHROMEOS_NET_NETWORK_PORTAL_DETECTOR_TEST_IMPL_H_ +#define CHROME_BROWSER_CHROMEOS_NET_NETWORK_PORTAL_DETECTOR_TEST_IMPL_H_ #include <string> @@ -16,10 +16,10 @@ namespace chromeos { -class NetworkPortalDetectorStub : public NetworkPortalDetector { +class NetworkPortalDetectorTestImpl : public NetworkPortalDetector { public: - NetworkPortalDetectorStub(); - virtual ~NetworkPortalDetectorStub(); + NetworkPortalDetectorTestImpl(); + virtual ~NetworkPortalDetectorTestImpl(); void SetDefaultNetworkPathForTesting(const std::string& service_path); void SetDetectionResultsForTesting(const std::string& service_path, @@ -27,8 +27,6 @@ class NetworkPortalDetectorStub : public NetworkPortalDetector { void NotifyObserversForTesting(); // NetworkPortalDetector implementation: - virtual void Init() OVERRIDE; - virtual void Shutdown() OVERRIDE; virtual void AddObserver(Observer* observer) OVERRIDE; virtual void AddAndFireObserver(Observer* observer) OVERRIDE; virtual void RemoveObserver(Observer* observer) OVERRIDE; @@ -49,9 +47,9 @@ class NetworkPortalDetectorStub : public NetworkPortalDetector { scoped_ptr<NetworkState> default_network_; CaptivePortalStateMap portal_state_map_; - DISALLOW_COPY_AND_ASSIGN(NetworkPortalDetectorStub); + DISALLOW_COPY_AND_ASSIGN(NetworkPortalDetectorTestImpl); }; } // namespace chromeos -#endif // CHROME_BROWSER_CHROMEOS_NET_NETWORK_PORTAL_DETECTOR_STUB_H_ +#endif // CHROME_BROWSER_CHROMEOS_NET_NETWORK_PORTAL_DETECTOR_TEST_IMPL_H_ diff --git a/chrome/browser/chromeos/net/onc_utils.cc b/chrome/browser/chromeos/net/onc_utils.cc index 4a52464bd7..32d88f464e 100644 --- a/chrome/browser/chromeos/net/onc_utils.cc +++ b/chrome/browser/chromeos/net/onc_utils.cc @@ -248,6 +248,33 @@ const base::DictionaryValue* FindPolicyForActiveUser( FindPolicyByGUID(username_hash, guid, onc_source); } +const base::DictionaryValue* GetGlobalConfigFromPolicy(bool for_active_user) { + std::string username_hash; + if (for_active_user) { + const User* user = UserManager::Get()->GetActiveUser(); + if (!user) { + LOG(ERROR) << "No user logged in yet."; + return NULL; + } + username_hash = user->username_hash(); + } + return NetworkHandler::Get()->managed_network_configuration_handler()-> + GetGlobalConfigFromPolicy(username_hash); +} + +bool PolicyAllowsOnlyPolicyNetworksToAutoconnect(bool for_active_user) { + const base::DictionaryValue* global_config = + GetGlobalConfigFromPolicy(for_active_user); + if (!global_config) + return false; // By default, all networks are allowed to autoconnect. + + bool only_policy_autoconnect = false; + global_config->GetBooleanWithoutPathExpansion( + ::onc::global_network_config::kAllowOnlyPolicyNetworksToAutoconnect, + &only_policy_autoconnect); + return only_policy_autoconnect; +} + namespace { const base::DictionaryValue* GetNetworkConfigByGUID( diff --git a/chrome/browser/chromeos/net/onc_utils.h b/chrome/browser/chromeos/net/onc_utils.h index 35d076267a..4fa8784abb 100644 --- a/chrome/browser/chromeos/net/onc_utils.h +++ b/chrome/browser/chromeos/net/onc_utils.h @@ -47,11 +47,22 @@ void ImportNetworksForUser(const chromeos::User* user, std::string* error); // Looks up the policy for |guid| for the current active user and sets -// |onc_source| accordingly. +// |global_config| (if not NULL) and |onc_source| (if not NULL) accordingly. If +// |guid| is empty, returns NULL and sets the |global_config| and |onc_source| +// if a policy is found. const base::DictionaryValue* FindPolicyForActiveUser( const std::string& guid, ::onc::ONCSource* onc_source); +// Returns the global network configuration section of the active user's network +// policy (if |for_active_user| is true) or of the device policy. +const base::DictionaryValue* GetGlobalConfigFromPolicy(bool for_active_user); + +// Convenvience function to retrieve the "AllowOnlyPolicyNetworksToAutoconnect" +// setting from the global network configuration (see +// GetGlobalConfigFromPolicy). +bool PolicyAllowsOnlyPolicyNetworksToAutoconnect(bool for_active_user); + // Returns the effective (user or device) policy for network |favorite|. Both // |profile_prefs| and |local_state_prefs| might be NULL. Returns NULL if no // applicable policy is found. Sets |onc_source| accordingly. diff --git a/chrome/browser/chromeos/offline/offline_load_page.cc b/chrome/browser/chromeos/offline/offline_load_page.cc index 952072998a..0219847218 100644 --- a/chrome/browser/chromeos/offline/offline_load_page.cc +++ b/chrome/browser/chromeos/offline/offline_load_page.cc @@ -50,9 +50,6 @@ using content::WebContents; namespace { -// Maximum time to show a blank page. -const int kMaxBlankPeriod = 3000; - // A utility function to set the dictionary's value given by |resource_id|. void SetString(DictionaryValue* strings, const char* name, int resource_id) { strings->SetString(name, l10n_util::GetStringUTF16(resource_id)); diff --git a/chrome/browser/chromeos/options/vpn_config_view.cc b/chrome/browser/chromeos/options/vpn_config_view.cc index 3bde57995d..ddb6c2d3cf 100644 --- a/chrome/browser/chromeos/options/vpn_config_view.cc +++ b/chrome/browser/chromeos/options/vpn_config_view.cc @@ -39,9 +39,6 @@ namespace { -// Root CA certificates that are built into Chrome use this token name. -const char* const kRootCertificateTokenName = "Builtin Object Token"; - enum ProviderTypeIndex { PROVIDER_TYPE_INDEX_L2TP_IPSEC_PSK = 0, PROVIDER_TYPE_INDEX_L2TP_IPSEC_USER_CERT = 1, @@ -376,6 +373,14 @@ bool VPNConfigView::Login() { SetConfigProperties(&properties); bool shared = !LoginState::Get()->IsUserAuthenticated(); + + bool only_policy_autoconnect = + onc::PolicyAllowsOnlyPolicyNetworksToAutoconnect(!shared); + if (only_policy_autoconnect) { + properties.SetBooleanWithoutPathExpansion(shill::kAutoConnectProperty, + false); + } + ash::network_connect::CreateConfigurationAndConnect(&properties, shared); } else { const NetworkState* vpn = NetworkHandler::Get()->network_state_handler()-> diff --git a/chrome/browser/chromeos/options/wifi_config_view.cc b/chrome/browser/chromeos/options/wifi_config_view.cc index f5190ae7a4..19cfbbcf86 100644 --- a/chrome/browser/chromeos/options/wifi_config_view.cc +++ b/chrome/browser/chromeos/options/wifi_config_view.cc @@ -648,9 +648,19 @@ void WifiConfigView::OnCertificatesLoaded(bool initial_load) { bool WifiConfigView::Login() { const bool share_default = true; + + // Set configuration properties. + base::DictionaryValue properties; + bool share_network = GetShareNetwork(share_default); + + bool only_policy_autoconnect = + onc::PolicyAllowsOnlyPolicyNetworksToAutoconnect(!share_network); + if (only_policy_autoconnect) { + properties.SetBooleanWithoutPathExpansion(shill::kAutoConnectProperty, + false); + } + if (service_path_.empty()) { - // Set configuration properties. - base::DictionaryValue properties; properties.SetStringWithoutPathExpansion( shill::kTypeProperty, shill::kTypeWifi); shill_property_util::SetSSID(GetSsid(), &properties); @@ -686,8 +696,8 @@ bool WifiConfigView::Login() { shill::kSecurityProperty, security); // Configure and connect to network. - bool shared = GetShareNetwork(share_default); - ash::network_connect::CreateConfigurationAndConnect(&properties, shared); + ash::network_connect::CreateConfigurationAndConnect(&properties, + share_network); } else { const NetworkState* wifi = NetworkHandler::Get()->network_state_handler()-> GetNetworkState(service_path_); @@ -697,7 +707,6 @@ bool WifiConfigView::Login() { NET_LOG_ERROR("Network not found", service_path_); return true; // Close dialog } - base::DictionaryValue properties; if (eap_method_combobox_) { // Visible 802.1X EAP Wi-Fi connection. SetEapProperties(&properties); @@ -711,7 +720,6 @@ bool WifiConfigView::Login() { shill::kPassphraseProperty, passphrase); } } - bool share_network = GetShareNetwork(share_default); ash::network_connect::ConfigureNetworkAndConnect( service_path_, properties, share_network); } diff --git a/chrome/browser/chromeos/options/wimax_config_view.cc b/chrome/browser/chromeos/options/wimax_config_view.cc index b1979351dc..c1b715f738 100644 --- a/chrome/browser/chromeos/options/wimax_config_view.cc +++ b/chrome/browser/chromeos/options/wimax_config_view.cc @@ -10,6 +10,7 @@ #include "base/strings/utf_string_conversions.h" #include "chrome/browser/chromeos/enrollment_dialog_view.h" #include "chrome/browser/chromeos/login/startup_utils.h" +#include "chrome/browser/chromeos/net/onc_utils.h" #include "chrome/browser/profiles/profile_manager.h" #include "chromeos/login/login_state.h" #include "chromeos/network/network_configuration_handler.h" @@ -158,6 +159,14 @@ bool WimaxConfigView::Login() { const bool share_default = true; bool share_network = GetShareNetwork(share_default); + + bool only_policy_autoconnect = + onc::PolicyAllowsOnlyPolicyNetworksToAutoconnect(!share_network); + if (only_policy_autoconnect) { + properties.SetBooleanWithoutPathExpansion(shill::kAutoConnectProperty, + false); + } + ash::network_connect::ConfigureNetworkAndConnect( service_path_, properties, share_network); return true; // dialog will be closed @@ -192,11 +201,11 @@ void WimaxConfigView::Init() { DCHECK(wimax && wimax->type() == shill::kTypeWimax); WifiConfigView::ParseWiFiEAPUIProperty( - &save_credentials_ui_data_, wimax, onc::eap::kSaveCredentials); + &save_credentials_ui_data_, wimax, ::onc::eap::kSaveCredentials); WifiConfigView::ParseWiFiEAPUIProperty( - &identity_ui_data_, wimax, onc::eap::kIdentity); + &identity_ui_data_, wimax, ::onc::eap::kIdentity); WifiConfigView::ParseWiFiUIProperty( - &passphrase_ui_data_, wimax, onc::wifi::kPassphrase); + &passphrase_ui_data_, wimax, ::onc::wifi::kPassphrase); views::GridLayout* layout = views::GridLayout::CreatePanel(this); SetLayoutManager(layout); diff --git a/chrome/browser/chromeos/policy/configuration_policy_handler_chromeos.cc b/chrome/browser/chromeos/policy/configuration_policy_handler_chromeos.cc index 71796220df..58f600c1eb 100644 --- a/chrome/browser/chromeos/policy/configuration_policy_handler_chromeos.cc +++ b/chrome/browser/chromeos/policy/configuration_policy_handler_chromeos.cc @@ -105,9 +105,16 @@ void NetworkConfigurationPolicyHandler::ApplyPolicySettings( scoped_ptr<base::ListValue> network_configs(new base::ListValue); base::ListValue certificates; - chromeos::onc::ParseAndValidateOncForImport( - onc_blob, onc_source_, "", network_configs.get(), &certificates); - + base::DictionaryValue global_network_config; + chromeos::onc::ParseAndValidateOncForImport(onc_blob, + onc_source_, + "", + network_configs.get(), + &global_network_config, + &certificates); + + // Currently, only the per-network configuration is stored in a pref. Ignore + // |global_network_config| and |certificates|. prefs->SetValue(pref_path_, network_configs.release()); } diff --git a/chrome/browser/chromeos/policy/configuration_policy_handler_chromeos.h b/chrome/browser/chromeos/policy/configuration_policy_handler_chromeos.h index f470b514cc..d1b751d222 100644 --- a/chrome/browser/chromeos/policy/configuration_policy_handler_chromeos.h +++ b/chrome/browser/chromeos/policy/configuration_policy_handler_chromeos.h @@ -5,6 +5,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_POLICY_CONFIGURATION_POLICY_HANDLER_CHROMEOS_H_ #define CHROME_BROWSER_CHROMEOS_POLICY_CONFIGURATION_POLICY_HANDLER_CHROMEOS_H_ +#include "chrome/browser/extensions/policy_handlers.h" #include "chrome/browser/policy/configuration_policy_handler.h" #include "chromeos/network/network_ui_data.h" #include "components/onc/onc_constants.h" @@ -56,7 +57,8 @@ class NetworkConfigurationPolicyHandler : public TypeCheckingPolicyHandler { }; // Maps the PinnedLauncherApps policy to the corresponding pref. -class PinnedLauncherAppsPolicyHandler : public ExtensionListPolicyHandler { +class PinnedLauncherAppsPolicyHandler + : public extensions::ExtensionListPolicyHandler { public: PinnedLauncherAppsPolicyHandler(); virtual ~PinnedLauncherAppsPolicyHandler(); diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc index 987d400c18..beea91d6ec 100644 --- a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc +++ b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc @@ -82,6 +82,7 @@ bool GetMachineFlag(const std::string& key, bool default_value) { DeviceCloudPolicyManagerChromeOS::DeviceCloudPolicyManagerChromeOS( scoped_ptr<DeviceCloudPolicyStoreChromeOS> store, const scoped_refptr<base::SequencedTaskRunner>& task_runner, + const scoped_refptr<base::SequencedTaskRunner>& background_task_runner, EnterpriseInstallAttributes* install_attributes) : CloudPolicyManager( PolicyNamespaceKey(dm_protocol::kChromeDevicePolicyType, @@ -89,6 +90,7 @@ DeviceCloudPolicyManagerChromeOS::DeviceCloudPolicyManagerChromeOS( store.get(), task_runner), device_store_(store.Pass()), + background_task_runner_(background_task_runner), install_attributes_(install_attributes), device_management_service_(NULL), local_state_(NULL) {} @@ -120,7 +122,8 @@ void DeviceCloudPolicyManagerChromeOS::StartEnrollment( enrollment_handler_.reset( new EnrollmentHandlerChromeOS( - device_store_.get(), install_attributes_, CreateClient(), auth_token, + device_store_.get(), install_attributes_, CreateClient(), + background_task_runner_, auth_token, install_attributes_->GetDeviceId(), is_auto_enrollment, GetDeviceRequisition(), allowed_device_modes, base::Bind(&DeviceCloudPolicyManagerChromeOS::EnrollmentCompleted, diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h index a79dd031d1..120847878c 100644 --- a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h +++ b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h @@ -46,9 +46,12 @@ class DeviceCloudPolicyManagerChromeOS : public CloudPolicyManager { typedef base::Callback<void(EnrollmentStatus)> EnrollmentCallback; // |task_runner| is the runner for policy refresh tasks. + // |background_task_runner| is used to execute long-running background tasks + // that may involve file I/O. DeviceCloudPolicyManagerChromeOS( scoped_ptr<DeviceCloudPolicyStoreChromeOS> store, const scoped_refptr<base::SequencedTaskRunner>& task_runner, + const scoped_refptr<base::SequencedTaskRunner>& background_task_runner, EnterpriseInstallAttributes* install_attributes); virtual ~DeviceCloudPolicyManagerChromeOS(); @@ -115,6 +118,7 @@ class DeviceCloudPolicyManagerChromeOS : public CloudPolicyManager { // Points to the same object as the base CloudPolicyManager::store(), but with // actual device policy specific type. scoped_ptr<DeviceCloudPolicyStoreChromeOS> device_store_; + scoped_refptr<base::SequencedTaskRunner> background_task_runner_; EnterpriseInstallAttributes* install_attributes_; DeviceManagementService* device_management_service_; diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc index 3b58d05f7d..7ee648e6f0 100644 --- a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc +++ b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc @@ -25,7 +25,7 @@ #include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h" #include "chrome/browser/prefs/browser_prefs.h" #include "chrome/test/base/testing_browser_process.h" -#include "chromeos/cryptohome/cryptohome_library.h" +#include "chromeos/cryptohome/system_salt_getter.h" #include "chromeos/dbus/dbus_client_implementation_type.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/system/mock_statistics_provider.h" @@ -84,10 +84,12 @@ class DeviceCloudPolicyManagerChromeOSTest install_attributes_.reset(new EnterpriseInstallAttributes( chromeos::DBusThreadManager::Get()->GetCryptohomeClient())); store_ = new DeviceCloudPolicyStoreChromeOS(&device_settings_service_, - install_attributes_.get()); + install_attributes_.get(), + loop_.message_loop_proxy()); manager_.reset(new DeviceCloudPolicyManagerChromeOS( make_scoped_ptr(store_), loop_.message_loop_proxy(), + loop_.message_loop_proxy(), install_attributes_.get())); chrome::RegisterLocalState(local_state_.registry()); @@ -102,9 +104,9 @@ class DeviceCloudPolicyManagerChromeOSTest request_context_getter_.get()); TestingBrowserProcess::GetGlobal()->SetLocalState(&local_state_); chromeos::DeviceOAuth2TokenServiceFactory::Initialize(); - // This is needed as CryptohomeLibrary is used for encrypting tokens in + // This is needed as SystemSaltGetter is used for encrypting tokens in // CryptohomeTokenEncryptor. - chromeos::CryptohomeLibrary::Initialize(); + chromeos::SystemSaltGetter::Initialize(); url_fetcher_response_code_ = 200; url_fetcher_response_string_ = "{\"access_token\":\"accessToken4Test\"," "\"expires_in\":1234," @@ -116,7 +118,7 @@ class DeviceCloudPolicyManagerChromeOSTest DeviceSettingsTestBase::TearDown(); chromeos::DeviceOAuth2TokenServiceFactory::Shutdown(); - chromeos::CryptohomeLibrary::Shutdown(); + chromeos::SystemSaltGetter::Shutdown(); TestingBrowserProcess::GetGlobal()->SetLocalState(NULL); } diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.cc b/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.cc index 3b0d634cd8..7f8a547974 100644 --- a/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.cc +++ b/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.cc @@ -5,6 +5,7 @@ #include "chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h" #include "base/bind.h" +#include "base/sequenced_task_runner.h" #include "chrome/browser/chromeos/policy/device_policy_decoder_chromeos.h" #include "chrome/browser/chromeos/policy/enterprise_install_attributes.h" #include "chrome/browser/policy/proto/chromeos/chrome_device_policy.pb.h" @@ -16,9 +17,11 @@ namespace policy { DeviceCloudPolicyStoreChromeOS::DeviceCloudPolicyStoreChromeOS( chromeos::DeviceSettingsService* device_settings_service, - EnterpriseInstallAttributes* install_attributes) + EnterpriseInstallAttributes* install_attributes, + scoped_refptr<base::SequencedTaskRunner> background_task_runner) : device_settings_service_(device_settings_service), install_attributes_(install_attributes), + background_task_runner_(background_task_runner), weak_factory_(this) { device_settings_service_->AddObserver(this); } @@ -92,7 +95,8 @@ scoped_ptr<DeviceCloudPolicyValidator> scoped_ptr<DeviceCloudPolicyValidator> validator( DeviceCloudPolicyValidator::Create( scoped_ptr<em::PolicyFetchResponse>( - new em::PolicyFetchResponse(policy)))); + new em::PolicyFetchResponse(policy)), + background_task_runner_)); validator->ValidateDomain(install_attributes_->GetDomain()); validator->ValidatePolicyType(dm_protocol::kChromeDevicePolicyType); validator->ValidatePayload(); diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h b/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h index f468a7598b..6afcd442eb 100644 --- a/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h +++ b/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h @@ -7,12 +7,17 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "chrome/browser/chromeos/policy/device_cloud_policy_validator.h" #include "chrome/browser/chromeos/settings/device_settings_service.h" #include "chrome/browser/policy/cloud/cloud_policy_store.h" +namespace base { +class SequencedTaskRunner; +} + namespace enterprise_management { class PolicyFetchResponse; } @@ -29,7 +34,8 @@ class DeviceCloudPolicyStoreChromeOS public: DeviceCloudPolicyStoreChromeOS( chromeos::DeviceSettingsService* device_settings_service, - EnterpriseInstallAttributes* install_attributes); + EnterpriseInstallAttributes* install_attributes, + scoped_refptr<base::SequencedTaskRunner> background_task_runner); virtual ~DeviceCloudPolicyStoreChromeOS(); // CloudPolicyStore: @@ -68,6 +74,8 @@ class DeviceCloudPolicyStoreChromeOS chromeos::DeviceSettingsService* device_settings_service_; EnterpriseInstallAttributes* install_attributes_; + scoped_refptr<base::SequencedTaskRunner> background_task_runner_; + base::WeakPtrFactory<DeviceCloudPolicyStoreChromeOS> weak_factory_; DISALLOW_COPY_AND_ASSIGN(DeviceCloudPolicyStoreChromeOS); diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos_unittest.cc b/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos_unittest.cc index b0347dacb4..5ccd535f95 100644 --- a/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos_unittest.cc +++ b/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos_unittest.cc @@ -39,7 +39,8 @@ class DeviceCloudPolicyStoreChromeOSTest install_attributes_(new EnterpriseInstallAttributes( fake_cryptohome_client_.get())), store_(new DeviceCloudPolicyStoreChromeOS(&device_settings_service_, - install_attributes_.get())) { + install_attributes_.get(), + loop_.message_loop_proxy())) { fake_cryptohome_client_->Init(NULL /* no dbus::Bus */); } @@ -99,8 +100,10 @@ class DeviceCloudPolicyStoreChromeOSTest std::string()); install_attributes_.reset(new EnterpriseInstallAttributes( fake_cryptohome_client_.get())); - store_.reset(new DeviceCloudPolicyStoreChromeOS(&device_settings_service_, - install_attributes_.get())); + store_.reset( + new DeviceCloudPolicyStoreChromeOS(&device_settings_service_, + install_attributes_.get(), + loop_.message_loop_proxy())); } scoped_ptr<chromeos::FakeCryptohomeClient> fake_cryptohome_client_; diff --git a/chrome/browser/chromeos/policy/device_local_account.h b/chrome/browser/chromeos/policy/device_local_account.h index ff83461863..d10b2286a3 100644 --- a/chrome/browser/chromeos/policy/device_local_account.h +++ b/chrome/browser/chromeos/policy/device_local_account.h @@ -32,6 +32,22 @@ struct DeviceLocalAccount { ~DeviceLocalAccount(); Type type; + // A device-local account has two identifiers: + // * The |account_id| is chosen by the entity that defines the device-local + // account. The only constraints are that the |account_id| be unique and, + // for legacy reasons, it contain an @ symbol. + // * The |user_id| is a synthesized identifier that is guaranteed to be + // unique, contain an @ symbol, not collide with the |user_id| of any other + // user on the device (such as regular users or supervised users) and be + // identifiable as belonging to a device-local account by. + // The |account_id| is primarily used by policy code: If device policy defines + // a device-local account with a certain |account_id|, the user policy for + // that account has to be fetched by referencing the same |account_id|. + // The |user_id| is passed to the chromeos::UserManager where it becomes part + // of the global user list on the device. The |account_id| would not be safe + // to use here as it is a free-form identifier that could conflict with + // another |user_id| on the device and cannot be easily identified as + // belonging to a device-local account. std::string account_id; std::string user_id; std::string kiosk_app_id; diff --git a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc index aae8978aa6..99346085fd 100644 --- a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc +++ b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc @@ -12,14 +12,17 @@ #include "base/command_line.h" #include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/scoped_temp_dir.h" #include "base/json/json_reader.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/path_service.h" #include "base/run_loop.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_path_override.h" #include "base/values.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_notification_types.h" @@ -57,6 +60,7 @@ #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension.h" +#include "chromeos/chromeos_paths.h" #include "chromeos/chromeos_switches.h" #include "chromeos/dbus/cryptohome_client.h" #include "chromeos/dbus/dbus_method_call_status.h" @@ -113,9 +117,9 @@ const char kUpdateManifestFooter[] = "</gupdate>\n"; const char kHostedAppID[] = "kbmnembihfiondgfjekmnmcbddelicoi"; const char kHostedAppCRXPath[] = "extensions/hosted_app.crx"; -const char kHostedAppVersion[] = "0.1"; +const char kHostedAppVersion[] = "1.0.0.0"; const char kGoodExtensionID[] = "ldnnhddmnhbkjipkidpdiheffobcpfmf"; -const char kGoodExtensionPath[] = "extensions/good.crx"; +const char kGoodExtensionCRXPath[] = "extensions/good.crx"; const char kGoodExtensionVersion[] = "1.0"; // Helper that serves extension update manifests to Chrome. @@ -247,6 +251,11 @@ class DeviceLocalAccountTest : public DevicePolicyCrosBrowserTest { PolicyBuilder::kFakeDeviceId); ASSERT_TRUE(test_server_.Start()); + ASSERT_TRUE(extension_cache_root_dir_.CreateUniqueTempDir()); + extension_cache_root_dir_override_.reset(new base::ScopedPathOverride( + chromeos::DIR_DEVICE_LOCAL_ACCOUNT_CACHE, + extension_cache_root_dir_.path())); + DevicePolicyCrosBrowserTest::SetUp(); } @@ -336,9 +345,24 @@ class DeviceLocalAccountTest : public DevicePolicyCrosBrowserTest { EXPECT_EQ(chromeos::User::USER_TYPE_PUBLIC_ACCOUNT, user->GetType()); } + base::FilePath GetCacheDirectoryForAccountID(const std::string& account_id) { + return extension_cache_root_dir_.path() + .Append(base::HexEncode(account_id.c_str(), account_id.size())); + } + + base::FilePath GetCacheCRXFile(const std::string& account_id, + const std::string& id, + const std::string& version) { + return GetCacheDirectoryForAccountID(account_id) + .Append(base::StringPrintf("%s-%s.crx", id.c_str(), version.c_str())); + } + const std::string user_id_1_; const std::string user_id_2_; + base::ScopedTempDir extension_cache_root_dir_; + scoped_ptr<base::ScopedPathOverride> extension_cache_root_dir_override_; + UserPolicyBuilder device_local_account_policy_; LocalPolicyTestServer test_server_; }; @@ -554,7 +578,7 @@ IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, FullscreenDisallowed) { EXPECT_FALSE(browser_window->IsFullscreen()); } -IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, ExtensionWhitelist) { +IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, ExtensionsUncached) { // Make it possible to force-install a hosted app and an extension. ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); TestingUpdateManifestProvider testing_update_manifest_provider( @@ -566,7 +590,7 @@ IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, ExtensionWhitelist) { testing_update_manifest_provider.AddUpdate( kGoodExtensionID, kGoodExtensionVersion, - embedded_test_server()->GetURL(std::string("/") + kGoodExtensionPath)); + embedded_test_server()->GetURL(std::string("/") + kGoodExtensionCRXPath)); embedded_test_server()->RegisterRequestHandler( base::Bind(&TestingUpdateManifestProvider::HandleRequest, base::Unretained(&testing_update_manifest_provider))); @@ -625,6 +649,105 @@ IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, ExtensionWhitelist) { controller->LoginAsPublicAccount(user_id_1_); // Wait for the hosted app installation to succeed and the extension + // installation to fail (because hosted apps are whitelisted for use in + // device-local accounts and extensions are not). + hosted_app_observer.Wait(); + extension_observer.Wait(); + + // Verify that the hosted app was installed. + Profile* profile = ProfileManager::GetDefaultProfile(); + ASSERT_TRUE(profile); + ExtensionService* extension_service = + extensions::ExtensionSystem::Get(profile)->extension_service(); + EXPECT_TRUE(extension_service->GetExtensionById(kHostedAppID, true)); + + // Verify that the extension was not installed. + EXPECT_FALSE(extension_service->GetExtensionById(kGoodExtensionID, true)); + + // Verify that the app was copied to the account's extension cache. + base::FilePath test_dir; + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir)); + EXPECT_TRUE(ContentsEqual( + GetCacheCRXFile(kAccountId1, kHostedAppID, kHostedAppVersion), + test_dir.Append(kHostedAppCRXPath))); + + // Verify that the extension was not copied to the account's extension cache. + EXPECT_FALSE(PathExists(GetCacheCRXFile( + kAccountId1, kGoodExtensionID, kGoodExtensionVersion))); +} + +IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, ExtensionsCached) { + ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); + + // Pre-populate the device local account's extension cache with a hosted app + // and an extension. + EXPECT_TRUE(file_util::CreateDirectory( + GetCacheDirectoryForAccountID(kAccountId1))); + base::FilePath test_dir; + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir)); + const base::FilePath cached_hosted_app = + GetCacheCRXFile(kAccountId1, kHostedAppID, kHostedAppVersion); + EXPECT_TRUE(CopyFile(test_dir.Append(kHostedAppCRXPath), + cached_hosted_app)); + const base::FilePath cached_extension = + GetCacheCRXFile(kAccountId1, kGoodExtensionID, kGoodExtensionVersion); + EXPECT_TRUE(CopyFile(test_dir.Append(kGoodExtensionCRXPath), + cached_extension)); + + // Specify policy to force-install the hosted app. + em::StringList* forcelist = device_local_account_policy_.payload() + .mutable_extensioninstallforcelist()->mutable_value(); + forcelist->add_entries(base::StringPrintf( + "%s;%s", + kHostedAppID, + embedded_test_server()->GetURL(kRelativeUpdateURL).spec().c_str())); + forcelist->add_entries(base::StringPrintf( + "%s;%s", + kGoodExtensionID, + embedded_test_server()->GetURL(kRelativeUpdateURL).spec().c_str())); + + UploadAndInstallDeviceLocalAccountPolicy(); + AddPublicSessionToDevicePolicy(kAccountId1); + + // This observes the display name becoming available as this indicates + // device-local account policy is fully loaded, which is a prerequisite for + // successful login. + content::WindowedNotificationObserver( + chrome::NOTIFICATION_USER_LIST_CHANGED, + base::Bind(&DisplayNameMatches, user_id_1_, kDisplayName)).Wait(); + + // Wait for the login UI to be ready. + chromeos::LoginDisplayHostImpl* host = + reinterpret_cast<chromeos::LoginDisplayHostImpl*>( + chromeos::LoginDisplayHostImpl::default_host()); + ASSERT_TRUE(host); + chromeos::OobeUI* oobe_ui = host->GetOobeUI(); + ASSERT_TRUE(oobe_ui); + base::RunLoop run_loop; + const bool oobe_ui_ready = oobe_ui->IsJSReady(run_loop.QuitClosure()); + if (!oobe_ui_ready) + run_loop.Run(); + + // Ensure that the browser stays alive, even though no windows are opened + // during session start. + chrome::StartKeepAlive(); + + // Start listening for app/extension installation results. + content::WindowedNotificationObserver hosted_app_observer( + chrome::NOTIFICATION_EXTENSION_INSTALLED, + base::Bind(DoesInstallSuccessReferToId, kHostedAppID)); + content::WindowedNotificationObserver extension_observer( + chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR, + base::Bind(DoesInstallFailureReferToId, kGoodExtensionID)); + + // Start login into the device-local account. + host->StartSignInScreen(); + chromeos::ExistingUserController* controller = + chromeos::ExistingUserController::current_controller(); + ASSERT_TRUE(controller); + controller->LoginAsPublicAccount(user_id_1_); + + // Wait for the hosted app installation to succeed and the extension // installation to fail. hosted_app_observer.Wait(); extension_observer.Wait(); @@ -638,6 +761,12 @@ IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, ExtensionWhitelist) { // Verify that the extension was not installed. EXPECT_FALSE(extension_service->GetExtensionById(kGoodExtensionID, true)); + + // Verify that the app is still in the account's extension cache. + EXPECT_TRUE(PathExists(cached_hosted_app)); + + // Verify that the extension was removed from the account's extension cache. + EXPECT_FALSE(PathExists(cached_extension)); } class TermsOfServiceTest : public DeviceLocalAccountTest, @@ -684,7 +813,7 @@ IN_PROC_BROWSER_TEST_P(TermsOfServiceTest, TermsOfServiceScreen) { // and the first wizard screen, if any, is being shown. base::RunLoop login_wait_run_loop; chromeos::MockConsumer login_status_consumer; - EXPECT_CALL(login_status_consumer, OnLoginSuccess(_, false, false)) + EXPECT_CALL(login_status_consumer, OnLoginSuccess(_)) .Times(1) .WillOnce(InvokeWithoutArgs(&login_wait_run_loop, &base::RunLoop::Quit)); diff --git a/chrome/browser/chromeos/policy/device_local_account_policy_provider.cc b/chrome/browser/chromeos/policy/device_local_account_policy_provider.cc index 6b0535e00d..4381ce43ae 100644 --- a/chrome/browser/chromeos/policy/device_local_account_policy_provider.cc +++ b/chrome/browser/chromeos/policy/device_local_account_policy_provider.cc @@ -5,6 +5,7 @@ #include "chrome/browser/chromeos/policy/device_local_account_policy_provider.h" #include "base/bind.h" +#include "chrome/browser/policy/cloud/cloud_policy_core.h" #include "chrome/browser/policy/cloud/cloud_policy_service.h" #include "chrome/browser/policy/policy_bundle.h" #include "chrome/browser/policy/policy_service.h" diff --git a/chrome/browser/chromeos/policy/device_local_account_policy_service.cc b/chrome/browser/chromeos/policy/device_local_account_policy_service.cc index 5e59294b98..3579bec7a2 100644 --- a/chrome/browser/chromeos/policy/device_local_account_policy_service.cc +++ b/chrome/browser/chromeos/policy/device_local_account_policy_service.cc @@ -7,18 +7,24 @@ #include <vector> #include "base/bind.h" +#include "base/file_util.h" +#include "base/files/file_enumerator.h" +#include "base/files/file_path.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop_proxy.h" +#include "base/path_service.h" +#include "base/sequenced_task_runner.h" +#include "base/strings/string_number_conversions.h" #include "chrome/browser/chromeos/policy/device_local_account.h" #include "chrome/browser/chromeos/policy/device_local_account_policy_store.h" -#include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/chromeos/settings/device_settings_service.h" #include "chrome/browser/policy/cloud/cloud_policy_client.h" #include "chrome/browser/policy/cloud/cloud_policy_constants.h" #include "chrome/browser/policy/cloud/cloud_policy_refresh_scheduler.h" #include "chrome/browser/policy/cloud/device_management_service.h" #include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h" +#include "chromeos/chromeos_paths.h" #include "chromeos/dbus/session_manager_client.h" #include "chromeos/settings/cros_settings_names.h" #include "chromeos/settings/cros_settings_provider.h" @@ -53,23 +59,86 @@ scoped_ptr<CloudPolicyClient> CreateClient( return client.Pass(); } +// Get the subdirectory of the cache directory in which force-installed +// extensions are cached for |account_id|. +std::string GetCacheSubdirectoryForAccountID(const std::string& account_id) { + return base::HexEncode(account_id.c_str(), account_id.size()); +} + +// Cleans up the cache directory by removing subdirectories that are not found +// in |subdirectories_to_keep|. Only caches whose cache directory is found in +// |subdirectories_to_keep| may be running while the clean-up is in progress. +void DeleteOrphanedExtensionCaches( + const std::set<std::string>& subdirectories_to_keep) { + base::FilePath cache_root_dir; + CHECK(PathService::Get(chromeos::DIR_DEVICE_LOCAL_ACCOUNT_CACHE, + &cache_root_dir)); + base::FileEnumerator enumerator(cache_root_dir, + false, + base::FileEnumerator::DIRECTORIES); + for (base::FilePath path = enumerator.Next(); !path.empty(); + path = enumerator.Next()) { + const std::string subdirectory(path.BaseName().MaybeAsASCII()); + if (subdirectories_to_keep.find(subdirectory) == + subdirectories_to_keep.end()) { + base::DeleteFile(path, true); + } + } +} + +// Removes the subdirectory belonging to |account_id_to_delete| from the cache +// directory. No cache belonging to |account_id_to_delete| may be running while +// the removal is in progress. +void DeleteObsoleteExtensionCache(const std::string& account_id_to_delete) { + base::FilePath cache_root_dir; + CHECK(PathService::Get(chromeos::DIR_DEVICE_LOCAL_ACCOUNT_CACHE, + &cache_root_dir)); + const base::FilePath path = cache_root_dir + .Append(GetCacheSubdirectoryForAccountID(account_id_to_delete)); + if (base::DirectoryExists(path)) + base::DeleteFile(path, true); +} + } // namespace DeviceLocalAccountPolicyBroker::DeviceLocalAccountPolicyBroker( - const std::string& user_id, + const DeviceLocalAccount& account, scoped_ptr<DeviceLocalAccountPolicyStore> store, const scoped_refptr<base::SequencedTaskRunner>& task_runner) - : user_id_(user_id), + : account_id_(account.account_id), + user_id_(account.user_id), store_(store.Pass()), core_(PolicyNamespaceKey(dm_protocol::kChromePublicAccountPolicyType, store_->account_id()), store_.get(), - task_runner) {} + task_runner) { + base::FilePath cache_root_dir; + CHECK(PathService::Get(chromeos::DIR_DEVICE_LOCAL_ACCOUNT_CACHE, + &cache_root_dir)); + extension_loader_ = new chromeos::DeviceLocalAccountExternalPolicyLoader( + store_.get(), + cache_root_dir.Append( + GetCacheSubdirectoryForAccountID(account.account_id))); +} + +DeviceLocalAccountPolicyBroker::~DeviceLocalAccountPolicyBroker() { +} + +void DeviceLocalAccountPolicyBroker::Initialize() { + store_->Load(); +} + +void DeviceLocalAccountPolicyBroker::ConnectIfPossible( + chromeos::DeviceSettingsService* device_settings_service, + DeviceManagementService* device_management_service) { + if (core_.client()) + return; -DeviceLocalAccountPolicyBroker::~DeviceLocalAccountPolicyBroker() {} + scoped_ptr<CloudPolicyClient> client(CreateClient(device_settings_service, + device_management_service)); + if (!client) + return; -void DeviceLocalAccountPolicyBroker::Connect( - scoped_ptr<CloudPolicyClient> client) { core_.Connect(client.Pass()); core_.StartRefreshScheduler(); UpdateRefreshDelay(); @@ -98,61 +167,26 @@ std::string DeviceLocalAccountPolicyBroker::GetDisplayName() const { return display_name; } -DeviceLocalAccountPolicyService::PolicyBrokerWrapper::PolicyBrokerWrapper() - : parent(NULL), broker(NULL) {} - -DeviceLocalAccountPolicyBroker* - DeviceLocalAccountPolicyService::PolicyBrokerWrapper::GetBroker() { - if (!broker) { - scoped_ptr<DeviceLocalAccountPolicyStore> store( - new DeviceLocalAccountPolicyStore(account_id, - parent->session_manager_client_, - parent->device_settings_service_)); - broker = new DeviceLocalAccountPolicyBroker( - user_id, store.Pass(), base::MessageLoopProxy::current()); - broker->core()->store()->AddObserver(parent); - broker->core()->store()->Load(); - } - return broker; -} - -void DeviceLocalAccountPolicyService::PolicyBrokerWrapper::ConnectIfPossible() { - if (broker && broker->core()->client()) - return; - scoped_ptr<CloudPolicyClient> client(CreateClient( - parent->device_settings_service_, - parent->device_management_service_)); - if (client) - GetBroker()->Connect(client.Pass()); -} - -void DeviceLocalAccountPolicyService::PolicyBrokerWrapper::Disconnect() { - if (broker) - broker->Disconnect(); -} - -void DeviceLocalAccountPolicyService::PolicyBrokerWrapper::DeleteBroker() { - if (!broker) - return; - broker->core()->store()->RemoveObserver(parent); - delete broker; - broker = NULL; -} - DeviceLocalAccountPolicyService::DeviceLocalAccountPolicyService( chromeos::SessionManagerClient* session_manager_client, chromeos::DeviceSettingsService* device_settings_service, - chromeos::CrosSettings* cros_settings) + chromeos::CrosSettings* cros_settings, + scoped_refptr<base::SequencedTaskRunner> store_background_task_runner, + scoped_refptr<base::SequencedTaskRunner> extension_cache_task_runner) : session_manager_client_(session_manager_client), device_settings_service_(device_settings_service), cros_settings_(cros_settings), device_management_service_(NULL), - cros_settings_callback_factory_(this) { - local_accounts_subscription_ = cros_settings_->AddSettingsObserver( - chromeos::kAccountsPrefDeviceLocalAccounts, - base::Bind(&DeviceLocalAccountPolicyService:: - UpdateAccountListIfNonePending, - base::Unretained(this))); + waiting_for_cros_settings_(false), + orphan_cache_deletion_state_(NOT_STARTED), + store_background_task_runner_(store_background_task_runner), + extension_cache_task_runner_(extension_cache_task_runner), + local_accounts_subscription_(cros_settings_->AddSettingsObserver( + chromeos::kAccountsPrefDeviceLocalAccounts, + base::Bind(&DeviceLocalAccountPolicyService:: + UpdateAccountListIfNonePending, + base::Unretained(this)))), + weak_factory_(this) { UpdateAccountList(); } @@ -168,7 +202,8 @@ void DeviceLocalAccountPolicyService::Connect( // Connect the brokers. for (PolicyBrokerMap::iterator it(policy_brokers_.begin()); it != policy_brokers_.end(); ++it) { - it->second.ConnectIfPossible(); + it->second->ConnectIfPossible(device_settings_service_, + device_management_service_); } } @@ -179,7 +214,7 @@ void DeviceLocalAccountPolicyService::Disconnect() { // Disconnect the brokers. for (PolicyBrokerMap::iterator it(policy_brokers_.begin()); it != policy_brokers_.end(); ++it) { - it->second.Disconnect(); + it->second->Disconnect(); } } @@ -190,7 +225,7 @@ DeviceLocalAccountPolicyBroker* if (entry == policy_brokers_.end()) return NULL; - return entry->second.GetBroker(); + return entry->second; } bool DeviceLocalAccountPolicyService::IsPolicyAvailableForUser( @@ -224,52 +259,214 @@ void DeviceLocalAccountPolicyService::OnStoreError(CloudPolicyStore* store) { FOR_EACH_OBSERVER(Observer, observers_, OnPolicyUpdated(broker->user_id())); } +bool DeviceLocalAccountPolicyService::IsExtensionCacheDirectoryBusy( + const std::string& account_id) { + return busy_extension_cache_directories_.find(account_id) != + busy_extension_cache_directories_.end(); +} + +void DeviceLocalAccountPolicyService::StartExtensionCachesIfPossible() { + for (PolicyBrokerMap::iterator it = policy_brokers_.begin(); + it != policy_brokers_.end(); ++it) { + if (!it->second->extension_loader()->IsCacheRunning() && + !IsExtensionCacheDirectoryBusy(it->second->account_id())) { + it->second->extension_loader()->StartCache(extension_cache_task_runner_); + } + } +} + +bool DeviceLocalAccountPolicyService::StartExtensionCacheForAccountIfPresent( + const std::string& account_id) { + for (PolicyBrokerMap::iterator it = policy_brokers_.begin(); + it != policy_brokers_.end(); ++it) { + if (it->second->account_id() == account_id) { + DCHECK(!it->second->extension_loader()->IsCacheRunning()); + it->second->extension_loader()->StartCache(extension_cache_task_runner_); + return true; + } + } + return false; +} + +void DeviceLocalAccountPolicyService::OnOrphanedExtensionCachesDeleted() { + DCHECK_EQ(IN_PROGRESS, orphan_cache_deletion_state_); + + orphan_cache_deletion_state_ = DONE; + StartExtensionCachesIfPossible(); +} + +void DeviceLocalAccountPolicyService::OnObsoleteExtensionCacheShutdown( + const std::string& account_id) { + DCHECK_NE(NOT_STARTED, orphan_cache_deletion_state_); + DCHECK(IsExtensionCacheDirectoryBusy(account_id)); + + // The account with |account_id| was deleted and the broker for it has shut + // down completely. + + if (StartExtensionCacheForAccountIfPresent(account_id)) { + // If another account with the same ID was created in the meantime, its + // extension cache is started, reusing the cache directory. The directory no + // longer needs to be marked as busy in this case. + busy_extension_cache_directories_.erase(account_id); + return; + } + + // If no account with |account_id| exists anymore, the cache directory should + // be removed. The directory must stay marked as busy while the removal is in + // progress. + extension_cache_task_runner_->PostTaskAndReply( + FROM_HERE, + base::Bind(&DeleteObsoleteExtensionCache, account_id), + base::Bind(&DeviceLocalAccountPolicyService:: + OnObsoleteExtensionCacheDeleted, + weak_factory_.GetWeakPtr(), + account_id)); +} + +void DeviceLocalAccountPolicyService::OnObsoleteExtensionCacheDeleted( + const std::string& account_id) { + DCHECK_EQ(DONE, orphan_cache_deletion_state_); + DCHECK(IsExtensionCacheDirectoryBusy(account_id)); + + // The cache directory for |account_id| has been deleted. The directory no + // longer needs to be marked as busy. + busy_extension_cache_directories_.erase(account_id); + + // If another account with the same ID was created in the meantime, start its + // extension cache, creating a new cache directory. + StartExtensionCacheForAccountIfPresent(account_id); +} + void DeviceLocalAccountPolicyService::UpdateAccountListIfNonePending() { // Avoid unnecessary calls to UpdateAccountList(): If an earlier call is still // pending (because the |cros_settings_| are not trusted yet), the updated // account list will be processed by that call when it eventually runs. - if (!cros_settings_callback_factory_.HasWeakPtrs()) + if (!waiting_for_cros_settings_) UpdateAccountList(); } void DeviceLocalAccountPolicyService::UpdateAccountList() { - if (chromeos::CrosSettingsProvider::TRUSTED != - cros_settings_->PrepareTrustedValues( - base::Bind(&DeviceLocalAccountPolicyService::UpdateAccountList, - cros_settings_callback_factory_.GetWeakPtr()))) { - return; + chromeos::CrosSettingsProvider::TrustedStatus status = + cros_settings_->PrepareTrustedValues( + base::Bind(&DeviceLocalAccountPolicyService::UpdateAccountList, + weak_factory_.GetWeakPtr())); + switch (status) { + case chromeos::CrosSettingsProvider::TRUSTED: + waiting_for_cros_settings_ = false; + break; + case chromeos::CrosSettingsProvider::TEMPORARILY_UNTRUSTED: + waiting_for_cros_settings_ = true; + return; + case chromeos::CrosSettingsProvider::PERMANENTLY_UNTRUSTED: + waiting_for_cros_settings_ = false; + return; } // Update |policy_brokers_|, keeping existing entries. PolicyBrokerMap old_policy_brokers; policy_brokers_.swap(old_policy_brokers); + std::set<std::string> subdirectories_to_keep; const std::vector<DeviceLocalAccount> device_local_accounts = GetDeviceLocalAccounts(cros_settings_); for (std::vector<DeviceLocalAccount>::const_iterator it = device_local_accounts.begin(); it != device_local_accounts.end(); ++it) { - PolicyBrokerWrapper& wrapper = policy_brokers_[it->user_id]; - wrapper.user_id = it->user_id; - wrapper.account_id = it->account_id; - wrapper.parent = this; - - // Reuse the existing broker if present. - PolicyBrokerWrapper& existing_wrapper = old_policy_brokers[it->user_id]; - wrapper.broker = existing_wrapper.broker; - existing_wrapper.broker = NULL; + PolicyBrokerMap::iterator broker_it = old_policy_brokers.find(it->user_id); + + scoped_ptr<DeviceLocalAccountPolicyBroker> broker; + bool broker_initialized = false; + if (broker_it != old_policy_brokers.end()) { + // Reuse the existing broker if present. + broker.reset(broker_it->second); + old_policy_brokers.erase(broker_it); + broker_initialized = true; + } else { + scoped_ptr<DeviceLocalAccountPolicyStore> store( + new DeviceLocalAccountPolicyStore(it->account_id, + session_manager_client_, + device_settings_service_, + store_background_task_runner_)); + store->AddObserver(this); + broker.reset(new DeviceLocalAccountPolicyBroker( + *it, + store.Pass(), + base::MessageLoopProxy::current())); + } // Fire up the cloud connection for fetching policy for the account from // the cloud if this is an enterprise-managed device. - wrapper.ConnectIfPossible(); + broker->ConnectIfPossible(device_settings_service_, + device_management_service_); + + policy_brokers_[it->user_id] = broker.release(); + if (!broker_initialized) { + // The broker must be initialized after it has been added to + // |policy_brokers_|. + policy_brokers_[it->user_id]->Initialize(); + } + + if (orphan_cache_deletion_state_ == NOT_STARTED) { + subdirectories_to_keep.insert( + GetCacheSubdirectoryForAccountID(it->account_id)); + } + } + + std::set<std::string> obsolete_account_ids; + for (PolicyBrokerMap::const_iterator it = old_policy_brokers.begin(); + it != old_policy_brokers.end(); ++it) { + obsolete_account_ids.insert(it->second->account_id()); + } + + if (orphan_cache_deletion_state_ == NOT_STARTED) { + DCHECK(old_policy_brokers.empty()); + DCHECK(busy_extension_cache_directories_.empty()); + + // If this method is running for the first time, no extension caches have + // been started yet. Take this opportunity to do a clean-up by removing + // orphaned cache directories not found in |subdirectories_to_keep| from the + // cache directory. + orphan_cache_deletion_state_ = IN_PROGRESS; + extension_cache_task_runner_->PostTaskAndReply( + FROM_HERE, + base::Bind(&DeleteOrphanedExtensionCaches, subdirectories_to_keep), + base::Bind(&DeviceLocalAccountPolicyService:: + OnOrphanedExtensionCachesDeleted, + weak_factory_.GetWeakPtr())); + + // Start the extension caches for all brokers. These belong to accounts in + // |account_ids| and are not affected by the clean-up. + StartExtensionCachesIfPossible(); + } else { + // If this method has run before, obsolete brokers may exist. Shut down + // their extension caches and delete the brokers. + DeleteBrokers(&old_policy_brokers); + + if (orphan_cache_deletion_state_ == DONE) { + // If the initial clean-up of orphaned cache directories has been + // complete, start any extension caches that are not running yet but can + // be started now because their cache directories are not busy. + StartExtensionCachesIfPossible(); + } } - DeleteBrokers(&old_policy_brokers); FOR_EACH_OBSERVER(Observer, observers_, OnDeviceLocalAccountsChanged()); } void DeviceLocalAccountPolicyService::DeleteBrokers(PolicyBrokerMap* map) { - for (PolicyBrokerMap::iterator it = map->begin(); it != map->end(); ++it) - it->second.DeleteBroker(); + for (PolicyBrokerMap::iterator it = map->begin(); it != map->end(); ++it) { + it->second->core()->store()->RemoveObserver(this); + scoped_refptr<chromeos::DeviceLocalAccountExternalPolicyLoader> + extension_loader = it->second->extension_loader(); + if (extension_loader->IsCacheRunning()) { + DCHECK(!IsExtensionCacheDirectoryBusy(it->second->account_id())); + busy_extension_cache_directories_.insert(it->second->account_id()); + extension_loader->StopCache(base::Bind( + &DeviceLocalAccountPolicyService::OnObsoleteExtensionCacheShutdown, + weak_factory_.GetWeakPtr(), + it->second->account_id())); + } + delete it->second; + } map->clear(); } @@ -278,8 +475,8 @@ DeviceLocalAccountPolicyBroker* CloudPolicyStore* store) { for (PolicyBrokerMap::iterator it(policy_brokers_.begin()); it != policy_brokers_.end(); ++it) { - if (it->second.broker && it->second.broker->core()->store() == store) - return it->second.broker; + if (it->second->core()->store() == store) + return it->second; } return NULL; } diff --git a/chrome/browser/chromeos/policy/device_local_account_policy_service.h b/chrome/browser/chromeos/policy/device_local_account_policy_service.h index 02b82f4de2..268f0a70a8 100644 --- a/chrome/browser/chromeos/policy/device_local_account_policy_service.h +++ b/chrome/browser/chromeos/policy/device_local_account_policy_service.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_CHROMEOS_POLICY_DEVICE_LOCAL_ACCOUNT_POLICY_SERVICE_H_ #include <map> +#include <set> #include <string> #include "base/basictypes.h" @@ -14,6 +15,7 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" +#include "chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.h" #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/policy/cloud/cloud_policy_core.h" #include "chrome/browser/policy/cloud/cloud_policy_store.h" @@ -23,14 +25,13 @@ class SequencedTaskRunner; } namespace chromeos { -class CrosSettings; class DeviceSettingsService; class SessionManagerClient; } namespace policy { -class CloudPolicyClient; +struct DeviceLocalAccount; class DeviceLocalAccountPolicyStore; class DeviceManagementService; @@ -39,19 +40,31 @@ class DeviceManagementService; class DeviceLocalAccountPolicyBroker { public: // |task_runner| is the runner for policy refresh tasks. - explicit DeviceLocalAccountPolicyBroker( - const std::string& user_id, + DeviceLocalAccountPolicyBroker( + const DeviceLocalAccount& account, scoped_ptr<DeviceLocalAccountPolicyStore> store, const scoped_refptr<base::SequencedTaskRunner>& task_runner); ~DeviceLocalAccountPolicyBroker(); + // Initialize the broker, loading its |store_|. + void Initialize(); + + // For the difference between |account_id| and |user_id|, see the + // documentation of DeviceLocalAccount. + const std::string& account_id() const { return account_id_; } const std::string& user_id() const { return user_id_; } + scoped_refptr<chromeos::DeviceLocalAccountExternalPolicyLoader> + extension_loader() const { return extension_loader_; } + CloudPolicyCore* core() { return &core_; } const CloudPolicyCore* core() const { return &core_; } - // Establish a cloud connection for the service. - void Connect(scoped_ptr<CloudPolicyClient> client); + // Fire up the cloud connection for fetching policy for the account from the + // cloud if this is an enterprise-managed device. + void ConnectIfPossible( + chromeos::DeviceSettingsService* device_settings_service, + DeviceManagementService* device_management_service); // Destroy the cloud connection, stopping policy refreshes. void Disconnect(); @@ -64,8 +77,11 @@ class DeviceLocalAccountPolicyBroker { std::string GetDisplayName() const; private: + const std::string account_id_; const std::string user_id_; - scoped_ptr<DeviceLocalAccountPolicyStore> store_; + const scoped_ptr<DeviceLocalAccountPolicyStore> store_; + scoped_refptr<chromeos::DeviceLocalAccountExternalPolicyLoader> + extension_loader_; CloudPolicyCore core_; DISALLOW_COPY_AND_ASSIGN(DeviceLocalAccountPolicyBroker); @@ -92,7 +108,9 @@ class DeviceLocalAccountPolicyService : public CloudPolicyStore::Observer { DeviceLocalAccountPolicyService( chromeos::SessionManagerClient* session_manager_client, chromeos::DeviceSettingsService* device_settings_service, - chromeos::CrosSettings* cros_settings); + chromeos::CrosSettings* cros_settings, + scoped_refptr<base::SequencedTaskRunner> store_background_task_runner, + scoped_refptr<base::SequencedTaskRunner> extension_cache_task_runner); virtual ~DeviceLocalAccountPolicyService(); // Initializes the cloud policy service connection. @@ -117,29 +135,32 @@ class DeviceLocalAccountPolicyService : public CloudPolicyStore::Observer { virtual void OnStoreError(CloudPolicyStore* store) OVERRIDE; private: - struct PolicyBrokerWrapper { - PolicyBrokerWrapper(); + typedef std::map<std::string, DeviceLocalAccountPolicyBroker*> + PolicyBrokerMap; - // Return the |broker|, creating it first if necessary. - DeviceLocalAccountPolicyBroker* GetBroker(); + // Returns |true| if the directory in which force-installed extensions are + // cached for |account_id| is busy, either because a broker that was using + // this directory has not shut down completely yet or because the directory is + // being deleted. + bool IsExtensionCacheDirectoryBusy(const std::string& account_id); - // Fire up the cloud connection for fetching policy for the account from the - // cloud if this is an enterprise-managed device. - void ConnectIfPossible(); + // Starts any extension caches that are not running yet but can be started now + // because their cache directories are no longer busy. + void StartExtensionCachesIfPossible(); - // Destroy the cloud connection. - void Disconnect(); + // Checks whether a broker exists for |account_id|. If so, starts the broker's + // extension cache and returns |true|. Otherwise, returns |false|. + bool StartExtensionCacheForAccountIfPresent(const std::string& account_id); - // Delete the broker. - void DeleteBroker(); + // Called back when any extension caches belonging to device-local accounts + // that no longer exist have been removed at start-up. + void OnOrphanedExtensionCachesDeleted(); - std::string user_id; - std::string account_id; - DeviceLocalAccountPolicyService* parent; - DeviceLocalAccountPolicyBroker* broker; - }; + // Called back when the extension cache for |account_id| has been shut down. + void OnObsoleteExtensionCacheShutdown(const std::string& account_id); - typedef std::map<std::string, PolicyBrokerWrapper> PolicyBrokerMap; + // Called back when the extension cache for |account_id| has been removed. + void OnObsoleteExtensionCacheDeleted(const std::string& account_id); // Re-queries the list of defined device-local accounts from device settings // and updates |policy_brokers_| to match that list. @@ -163,14 +184,33 @@ class DeviceLocalAccountPolicyService : public CloudPolicyStore::Observer { // The device-local account policy brokers, keyed by user ID. PolicyBrokerMap policy_brokers_; + // Whether a call to UpdateAccountList() is pending because |cros_settings_| + // are not trusted yet. + bool waiting_for_cros_settings_; + + // Orphaned extension caches are removed at startup. This tracks the status of + // that process. + enum OrphanCacheDeletionState { + NOT_STARTED, + IN_PROGRESS, + DONE, + }; + OrphanCacheDeletionState orphan_cache_deletion_state_; + + // Account IDs whose extension cache directories are busy, either because a + // broker for the account has not shut down completely yet or because the + // directory is being deleted. + std::set<std::string> busy_extension_cache_directories_; + + const scoped_refptr<base::SequencedTaskRunner> store_background_task_runner_; + const scoped_refptr<base::SequencedTaskRunner> extension_cache_task_runner_; + ObserverList<Observer, true> observers_; - scoped_ptr<chromeos::CrosSettings::ObserverSubscription> + const scoped_ptr<chromeos::CrosSettings::ObserverSubscription> local_accounts_subscription_; - // Weak pointer factory for cros_settings_->PrepareTrustedValues() callbacks. - base::WeakPtrFactory<DeviceLocalAccountPolicyService> - cros_settings_callback_factory_; + base::WeakPtrFactory<DeviceLocalAccountPolicyService> weak_factory_; DISALLOW_COPY_AND_ASSIGN(DeviceLocalAccountPolicyService); }; diff --git a/chrome/browser/chromeos/policy/device_local_account_policy_service_unittest.cc b/chrome/browser/chromeos/policy/device_local_account_policy_service_unittest.cc index 8312db92f4..d6c81226d6 100644 --- a/chrome/browser/chromeos/policy/device_local_account_policy_service_unittest.cc +++ b/chrome/browser/chromeos/policy/device_local_account_policy_service_unittest.cc @@ -7,6 +7,17 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/callback.h" +#include "base/file_util.h" +#include "base/files/file_path.h" +#include "base/files/scoped_temp_dir.h" +#include "base/message_loop/message_loop.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/path_service.h" +#include "base/run_loop.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/test/scoped_path_override.h" +#include "base/test/test_simple_task_runner.h" #include "chrome/browser/chromeos/policy/device_local_account.h" #include "chrome/browser/chromeos/policy/device_local_account_policy_provider.h" #include "chrome/browser/chromeos/settings/cros_settings.h" @@ -20,8 +31,11 @@ #include "chrome/browser/policy/external_data_fetcher.h" #include "chrome/browser/policy/mock_configuration_policy_provider.h" #include "chrome/browser/policy/proto/chromeos/chrome_device_policy.pb.h" +#include "chrome/common/chrome_paths.h" +#include "chromeos/chromeos_paths.h" #include "chromeos/dbus/power_policy_controller.h" #include "policy/policy_constants.h" +#include "policy/proto/cloud_policy.pb.h" #include "testing/gtest/include/gtest/gtest.h" using testing::AnyNumber; @@ -34,6 +48,19 @@ namespace em = enterprise_management; namespace policy { +namespace { + +const char kAccount1[] = "account1@localhost"; +const char kAccount2[] = "account2@localhost"; +const char kAccount3[] = "account3@localhost"; + +const char kExtensionID[] = "kbmnembihfiondgfjekmnmcbddelicoi"; +const char kExtensionVersion[] = "1.0.0.0"; +const char kExtensionCRXPath[] = "extensions/hosted_app.crx"; +const char kUpdateURL[] = "https://clients2.google.com/service/update2/crx"; + +} // namespace + class MockDeviceLocalAccountPolicyServiceObserver : public DeviceLocalAccountPolicyService::Observer { public: @@ -41,273 +68,342 @@ class MockDeviceLocalAccountPolicyServiceObserver MOCK_METHOD0(OnDeviceLocalAccountsChanged, void(void)); }; -class DeviceLocalAccountPolicyServiceTest +class DeviceLocalAccountPolicyServiceTestBase : public chromeos::DeviceSettingsTestBase { public: - DeviceLocalAccountPolicyServiceTest() - : public_session_user_id_(GenerateDeviceLocalAccountUserId( - PolicyBuilder::kFakeUsername, - DeviceLocalAccount::TYPE_PUBLIC_SESSION)), - cros_settings_(&device_settings_service_), - service_(&device_settings_test_helper_, - &device_settings_service_, - &cros_settings_) {} - - virtual void SetUp() OVERRIDE { - DeviceSettingsTestBase::SetUp(); - - // Values implicitly enforced for public accounts. - expected_policy_map_.Set(key::kLidCloseAction, - POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, - base::Value::CreateIntegerValue( - chromeos::PowerPolicyController:: - ACTION_STOP_SESSION), - NULL); - expected_policy_map_.Set(key::kShelfAutoHideBehavior, - POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, - Value::CreateStringValue("Never"), - NULL); - expected_policy_map_.Set(key::kShowLogoutButtonInTray, - POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, - Value::CreateBooleanValue(true), - NULL); - expected_policy_map_.Set(key::kFullscreenAllowed, - POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, - Value::CreateBooleanValue(false), - NULL); - - // Explicitly set value. - expected_policy_map_.Set(key::kDisableSpdy, - POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, - Value::CreateBooleanValue(true), - NULL); - - device_local_account_policy_.payload().mutable_disablespdy()->set_value( - true); - device_local_account_policy_.policy_data().set_policy_type( - dm_protocol::kChromePublicAccountPolicyType); - device_local_account_policy_.policy_data().set_settings_entity_id( - PolicyBuilder::kFakeUsername); - device_local_account_policy_.Build(); - - em::DeviceLocalAccountInfoProto* account = - device_policy_.payload().mutable_device_local_accounts()->add_account(); - account->set_account_id(PolicyBuilder::kFakeUsername); - account->set_type( - em::DeviceLocalAccountInfoProto::ACCOUNT_TYPE_PUBLIC_SESSION); - device_policy_.Build(); - - service_.AddObserver(&service_observer_); - } - - virtual void TearDown() OVERRIDE { - service_.RemoveObserver(&service_observer_); - - DeviceSettingsTestBase::TearDown(); - } - - void InstallDevicePolicy() { - EXPECT_CALL(service_observer_, OnDeviceLocalAccountsChanged()); - device_settings_test_helper_.set_policy_blob(device_policy_.GetBlob()); - ReloadDeviceSettings(); - Mock::VerifyAndClearExpectations(&service_observer_); - } + DeviceLocalAccountPolicyServiceTestBase(); - MOCK_METHOD1(OnRefreshDone, void(bool)); + virtual void SetUp() OVERRIDE; + virtual void TearDown() OVERRIDE; + + void CreatePolicyService(); + + void InstallDeviceLocalAccountPolicy(const std::string& account_id); + void AddDeviceLocalAccountToPolicy(const std::string& account_id); + virtual void InstallDevicePolicy(); - const std::string public_session_user_id_; + const std::string account_1_user_id_; + const std::string account_2_user_id_; PolicyMap expected_policy_map_; UserPolicyBuilder device_local_account_policy_; chromeos::CrosSettings cros_settings_; - MockDeviceLocalAccountPolicyServiceObserver service_observer_; + scoped_refptr<base::TestSimpleTaskRunner> extension_cache_task_runner_; MockDeviceManagementService mock_device_management_service_; - DeviceLocalAccountPolicyService service_; + scoped_ptr<DeviceLocalAccountPolicyService> service_; + + private: + DISALLOW_COPY_AND_ASSIGN(DeviceLocalAccountPolicyServiceTestBase); +}; + +class DeviceLocalAccountPolicyServiceTest + : public DeviceLocalAccountPolicyServiceTestBase { + public: + MOCK_METHOD1(OnRefreshDone, void(bool)); + + protected: + DeviceLocalAccountPolicyServiceTest(); + + virtual void SetUp() OVERRIDE; + virtual void TearDown() OVERRIDE; + + void InstallDevicePolicy() OVERRIDE; + + MockDeviceLocalAccountPolicyServiceObserver service_observer_; private: DISALLOW_COPY_AND_ASSIGN(DeviceLocalAccountPolicyServiceTest); }; +DeviceLocalAccountPolicyServiceTestBase:: + DeviceLocalAccountPolicyServiceTestBase() + : account_1_user_id_(GenerateDeviceLocalAccountUserId( + kAccount1, + DeviceLocalAccount::TYPE_PUBLIC_SESSION)), + account_2_user_id_(GenerateDeviceLocalAccountUserId( + kAccount2, + DeviceLocalAccount::TYPE_PUBLIC_SESSION)), + cros_settings_(&device_settings_service_), + extension_cache_task_runner_(new base::TestSimpleTaskRunner) { +} + +void DeviceLocalAccountPolicyServiceTestBase::SetUp() { + chromeos::DeviceSettingsTestBase::SetUp(); + + // Values implicitly enforced for public accounts. + expected_policy_map_.Set(key::kLidCloseAction, + POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_USER, + base::Value::CreateIntegerValue( + chromeos::PowerPolicyController:: + ACTION_STOP_SESSION), + NULL); + expected_policy_map_.Set(key::kShelfAutoHideBehavior, + POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_USER, + Value::CreateStringValue("Never"), + NULL); + expected_policy_map_.Set(key::kShowLogoutButtonInTray, + POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_USER, + Value::CreateBooleanValue(true), + NULL); + expected_policy_map_.Set(key::kFullscreenAllowed, + POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_USER, + Value::CreateBooleanValue(false), + NULL); + + // Explicitly set value. + expected_policy_map_.Set(key::kDisableSpdy, + POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_USER, + Value::CreateBooleanValue(true), + NULL); + + device_local_account_policy_.payload().mutable_disablespdy()->set_value( + true); + device_local_account_policy_.policy_data().set_policy_type( + dm_protocol::kChromePublicAccountPolicyType); +} + +void DeviceLocalAccountPolicyServiceTestBase::TearDown() { + service_.reset(); + extension_cache_task_runner_->RunUntilIdle(); + chromeos::DeviceSettingsTestBase::TearDown(); +} + +void DeviceLocalAccountPolicyServiceTestBase::CreatePolicyService() { + service_.reset(new DeviceLocalAccountPolicyService( + &device_settings_test_helper_, + &device_settings_service_, + &cros_settings_, + loop_.message_loop_proxy(), + extension_cache_task_runner_)); +} + +void DeviceLocalAccountPolicyServiceTestBase:: + InstallDeviceLocalAccountPolicy(const std::string& account_id) { + device_local_account_policy_.policy_data().set_settings_entity_id(account_id); + device_local_account_policy_.policy_data().set_username(account_id); + device_local_account_policy_.Build(); + device_settings_test_helper_.set_device_local_account_policy_blob( + account_id, device_local_account_policy_.GetBlob()); +} + +void DeviceLocalAccountPolicyServiceTestBase::AddDeviceLocalAccountToPolicy( + const std::string& account_id) { + em::DeviceLocalAccountInfoProto* account = + device_policy_.payload().mutable_device_local_accounts()->add_account(); + account->set_account_id(account_id); + account->set_type( + em::DeviceLocalAccountInfoProto::ACCOUNT_TYPE_PUBLIC_SESSION); +} + +void DeviceLocalAccountPolicyServiceTestBase::InstallDevicePolicy() { + device_policy_.Build(); + device_settings_test_helper_.set_policy_blob(device_policy_.GetBlob()); + ReloadDeviceSettings(); +} + +DeviceLocalAccountPolicyServiceTest::DeviceLocalAccountPolicyServiceTest() { + CreatePolicyService(); +} + +void DeviceLocalAccountPolicyServiceTest::SetUp() { + DeviceLocalAccountPolicyServiceTestBase::SetUp(); + service_->AddObserver(&service_observer_); +} + +void DeviceLocalAccountPolicyServiceTest::TearDown() { + service_->RemoveObserver(&service_observer_); + DeviceLocalAccountPolicyServiceTestBase::TearDown(); +} + +void DeviceLocalAccountPolicyServiceTest::InstallDevicePolicy() { + EXPECT_CALL(service_observer_, OnDeviceLocalAccountsChanged()); + DeviceLocalAccountPolicyServiceTestBase::InstallDevicePolicy(); + Mock::VerifyAndClearExpectations(&service_observer_); +} + TEST_F(DeviceLocalAccountPolicyServiceTest, NoAccounts) { - EXPECT_FALSE(service_.GetBrokerForUser(public_session_user_id_)); + EXPECT_FALSE(service_->GetBrokerForUser(account_1_user_id_)); } TEST_F(DeviceLocalAccountPolicyServiceTest, GetBroker) { + InstallDeviceLocalAccountPolicy(kAccount1); + AddDeviceLocalAccountToPolicy(kAccount1); + EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_)); InstallDevicePolicy(); DeviceLocalAccountPolicyBroker* broker = - service_.GetBrokerForUser(public_session_user_id_); + service_->GetBrokerForUser(account_1_user_id_); ASSERT_TRUE(broker); - EXPECT_EQ(public_session_user_id_, broker->user_id()); + EXPECT_EQ(account_1_user_id_, broker->user_id()); ASSERT_TRUE(broker->core()->store()); EXPECT_EQ(CloudPolicyStore::STATUS_OK, broker->core()->store()->status()); EXPECT_FALSE(broker->core()->client()); - EXPECT_TRUE(broker->core()->store()->policy_map().empty()); + EXPECT_FALSE(broker->core()->store()->policy_map().empty()); } TEST_F(DeviceLocalAccountPolicyServiceTest, LoadNoPolicy) { + AddDeviceLocalAccountToPolicy(kAccount1); + EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_)); InstallDevicePolicy(); - EXPECT_CALL(service_observer_, OnPolicyUpdated(public_session_user_id_)); DeviceLocalAccountPolicyBroker* broker = - service_.GetBrokerForUser(public_session_user_id_); + service_->GetBrokerForUser(account_1_user_id_); ASSERT_TRUE(broker); - FlushDeviceSettings(); - Mock::VerifyAndClearExpectations(&service_observer_); - + EXPECT_EQ(account_1_user_id_, broker->user_id()); ASSERT_TRUE(broker->core()->store()); EXPECT_EQ(CloudPolicyStore::STATUS_LOAD_ERROR, broker->core()->store()->status()); EXPECT_TRUE(broker->core()->store()->policy_map().empty()); - EXPECT_FALSE(service_.IsPolicyAvailableForUser(public_session_user_id_)); + EXPECT_FALSE(service_->IsPolicyAvailableForUser(account_1_user_id_)); } TEST_F(DeviceLocalAccountPolicyServiceTest, LoadValidationFailure) { device_local_account_policy_.policy_data().set_policy_type( dm_protocol::kChromeUserPolicyType); - device_local_account_policy_.Build(); - device_settings_test_helper_.set_device_local_account_policy_blob( - PolicyBuilder::kFakeUsername, device_local_account_policy_.GetBlob()); + InstallDeviceLocalAccountPolicy(kAccount1); + AddDeviceLocalAccountToPolicy(kAccount1); + EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_)); InstallDevicePolicy(); - EXPECT_CALL(service_observer_, OnPolicyUpdated(public_session_user_id_)); DeviceLocalAccountPolicyBroker* broker = - service_.GetBrokerForUser(public_session_user_id_); + service_->GetBrokerForUser(account_1_user_id_); ASSERT_TRUE(broker); - FlushDeviceSettings(); - Mock::VerifyAndClearExpectations(&service_observer_); - + EXPECT_EQ(account_1_user_id_, broker->user_id()); ASSERT_TRUE(broker->core()->store()); EXPECT_EQ(CloudPolicyStore::STATUS_VALIDATION_ERROR, broker->core()->store()->status()); EXPECT_TRUE(broker->core()->store()->policy_map().empty()); - EXPECT_FALSE(service_.IsPolicyAvailableForUser(public_session_user_id_)); + EXPECT_FALSE(service_->IsPolicyAvailableForUser(account_1_user_id_)); } TEST_F(DeviceLocalAccountPolicyServiceTest, LoadPolicy) { - device_settings_test_helper_.set_device_local_account_policy_blob( - PolicyBuilder::kFakeUsername, device_local_account_policy_.GetBlob()); + InstallDeviceLocalAccountPolicy(kAccount1); + AddDeviceLocalAccountToPolicy(kAccount1); + EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_)); InstallDevicePolicy(); - EXPECT_CALL(service_observer_, OnPolicyUpdated(public_session_user_id_)); DeviceLocalAccountPolicyBroker* broker = - service_.GetBrokerForUser(public_session_user_id_); + service_->GetBrokerForUser(account_1_user_id_); ASSERT_TRUE(broker); - FlushDeviceSettings(); - Mock::VerifyAndClearExpectations(&service_observer_); - + EXPECT_EQ(account_1_user_id_, broker->user_id()); ASSERT_TRUE(broker->core()->store()); - EXPECT_EQ(CloudPolicyStore::STATUS_OK, - broker->core()->store()->status()); + EXPECT_EQ(CloudPolicyStore::STATUS_OK, broker->core()->store()->status()); ASSERT_TRUE(broker->core()->store()->policy()); EXPECT_EQ(device_local_account_policy_.policy_data().SerializeAsString(), broker->core()->store()->policy()->SerializeAsString()); EXPECT_TRUE(expected_policy_map_.Equals( broker->core()->store()->policy_map())); - EXPECT_TRUE(service_.IsPolicyAvailableForUser(public_session_user_id_)); + EXPECT_TRUE(service_->IsPolicyAvailableForUser(account_1_user_id_)); } TEST_F(DeviceLocalAccountPolicyServiceTest, StoreValidationFailure) { - device_local_account_policy_.policy_data().set_policy_type( - dm_protocol::kChromeUserPolicyType); - device_local_account_policy_.Build(); + AddDeviceLocalAccountToPolicy(kAccount1); + EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_)); InstallDevicePolicy(); + Mock::VerifyAndClearExpectations(&service_observer_); - EXPECT_CALL(service_observer_, OnPolicyUpdated(public_session_user_id_)); DeviceLocalAccountPolicyBroker* broker = - service_.GetBrokerForUser(public_session_user_id_); + service_->GetBrokerForUser(account_1_user_id_); ASSERT_TRUE(broker); + EXPECT_EQ(account_1_user_id_, broker->user_id()); ASSERT_TRUE(broker->core()->store()); + + device_local_account_policy_.policy_data().set_policy_type( + dm_protocol::kChromeUserPolicyType); + device_local_account_policy_.Build(); broker->core()->store()->Store(device_local_account_policy_.policy()); + EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_)); FlushDeviceSettings(); - Mock::VerifyAndClearExpectations(&service_observer_); - ASSERT_TRUE(broker->core()->store()); EXPECT_EQ(CloudPolicyStore::STATUS_VALIDATION_ERROR, broker->core()->store()->status()); EXPECT_EQ(CloudPolicyValidatorBase::VALIDATION_WRONG_POLICY_TYPE, broker->core()->store()->validation_status()); - EXPECT_FALSE(service_.IsPolicyAvailableForUser(public_session_user_id_)); + EXPECT_FALSE(service_->IsPolicyAvailableForUser(account_1_user_id_)); } TEST_F(DeviceLocalAccountPolicyServiceTest, StorePolicy) { + AddDeviceLocalAccountToPolicy(kAccount1); + EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_)); InstallDevicePolicy(); + Mock::VerifyAndClearExpectations(&service_observer_); - EXPECT_CALL(service_observer_, OnPolicyUpdated(public_session_user_id_)); DeviceLocalAccountPolicyBroker* broker = - service_.GetBrokerForUser(public_session_user_id_); + service_->GetBrokerForUser(account_1_user_id_); ASSERT_TRUE(broker); + EXPECT_EQ(account_1_user_id_, broker->user_id()); ASSERT_TRUE(broker->core()->store()); + + device_local_account_policy_.policy_data().set_settings_entity_id(kAccount1); + device_local_account_policy_.policy_data().set_username(kAccount1); + device_local_account_policy_.Build(); broker->core()->store()->Store(device_local_account_policy_.policy()); + EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_)); FlushDeviceSettings(); - Mock::VerifyAndClearExpectations(&service_observer_); - EXPECT_EQ(device_local_account_policy_.GetBlob(), - device_settings_test_helper_.device_local_account_policy_blob( - PolicyBuilder::kFakeUsername)); - EXPECT_TRUE(service_.IsPolicyAvailableForUser(public_session_user_id_)); + EXPECT_EQ(CloudPolicyStore::STATUS_OK, broker->core()->store()->status()); + ASSERT_TRUE(broker->core()->store()->policy()); + EXPECT_EQ(device_local_account_policy_.policy_data().SerializeAsString(), + broker->core()->store()->policy()->SerializeAsString()); + EXPECT_TRUE(expected_policy_map_.Equals( + broker->core()->store()->policy_map())); + EXPECT_TRUE(service_->IsPolicyAvailableForUser(account_1_user_id_)); } TEST_F(DeviceLocalAccountPolicyServiceTest, DevicePolicyChange) { - device_settings_test_helper_.set_device_local_account_policy_blob( - PolicyBuilder::kFakeUsername, device_local_account_policy_.GetBlob()); + InstallDeviceLocalAccountPolicy(kAccount1); + AddDeviceLocalAccountToPolicy(kAccount1); + EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_)); InstallDevicePolicy(); - EXPECT_CALL(service_observer_, OnDeviceLocalAccountsChanged()); device_policy_.payload().mutable_device_local_accounts()->clear_account(); - device_policy_.Build(); - device_settings_test_helper_.set_policy_blob(device_policy_.GetBlob()); - device_settings_service_.PropertyChangeComplete(true); - FlushDeviceSettings(); - EXPECT_FALSE(service_.GetBrokerForUser(public_session_user_id_)); - Mock::VerifyAndClearExpectations(&service_observer_); + InstallDevicePolicy(); + + EXPECT_FALSE(service_->GetBrokerForUser(account_1_user_id_)); } TEST_F(DeviceLocalAccountPolicyServiceTest, DuplicateAccounts) { + InstallDeviceLocalAccountPolicy(kAccount1); + AddDeviceLocalAccountToPolicy(kAccount1); + EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_)); InstallDevicePolicy(); - DeviceLocalAccountPolicyBroker* broker = - service_.GetBrokerForUser(public_session_user_id_); - ASSERT_TRUE(broker); + Mock::VerifyAndClearExpectations(&service_observer_); // Add a second entry with a duplicate account name to device policy. - em::DeviceLocalAccountInfoProto* account = - device_policy_.payload().mutable_device_local_accounts()->add_account(); - account->set_account_id(PolicyBuilder::kFakeUsername); - account->set_type( - em::DeviceLocalAccountInfoProto::ACCOUNT_TYPE_PUBLIC_SESSION); - device_policy_.Build(); - device_settings_test_helper_.set_device_local_account_policy_blob( - PolicyBuilder::kFakeUsername, device_local_account_policy_.GetBlob()); - device_settings_test_helper_.set_policy_blob(device_policy_.GetBlob()); - - EXPECT_CALL(service_observer_, OnDeviceLocalAccountsChanged()); - EXPECT_CALL(service_observer_, OnPolicyUpdated(public_session_user_id_)); - device_settings_service_.PropertyChangeComplete(true); - FlushDeviceSettings(); - Mock::VerifyAndClearExpectations(&service_observer_); + AddDeviceLocalAccountToPolicy(kAccount1); + InstallDevicePolicy(); // Make sure the broker is accessible and policy got loaded. - broker = service_.GetBrokerForUser(public_session_user_id_); + DeviceLocalAccountPolicyBroker* broker = + service_->GetBrokerForUser(account_1_user_id_); ASSERT_TRUE(broker); - EXPECT_EQ(public_session_user_id_, broker->user_id()); - EXPECT_TRUE(broker->core()->store()->policy()); + EXPECT_EQ(account_1_user_id_, broker->user_id()); + ASSERT_TRUE(broker->core()->store()); + EXPECT_EQ(CloudPolicyStore::STATUS_OK, broker->core()->store()->status()); + ASSERT_TRUE(broker->core()->store()->policy()); + EXPECT_EQ(device_local_account_policy_.policy_data().SerializeAsString(), + broker->core()->store()->policy()->SerializeAsString()); + EXPECT_TRUE(expected_policy_map_.Equals( + broker->core()->store()->policy_map())); + EXPECT_TRUE(service_->IsPolicyAvailableForUser(account_1_user_id_)); } TEST_F(DeviceLocalAccountPolicyServiceTest, FetchPolicy) { - device_settings_test_helper_.set_device_local_account_policy_blob( - PolicyBuilder::kFakeUsername, device_local_account_policy_.GetBlob()); + InstallDeviceLocalAccountPolicy(kAccount1); + AddDeviceLocalAccountToPolicy(kAccount1); + EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_)); InstallDevicePolicy(); DeviceLocalAccountPolicyBroker* broker = - service_.GetBrokerForUser(public_session_user_id_); + service_->GetBrokerForUser(account_1_user_id_); ASSERT_TRUE(broker); - service_.Connect(&mock_device_management_service_); + service_->Connect(&mock_device_management_service_); EXPECT_TRUE(broker->core()->client()); em::DeviceManagementRequest request; @@ -325,7 +421,7 @@ TEST_F(DeviceLocalAccountPolicyServiceTest, FetchPolicy) { device_policy_.policy_data().device_id(), _)) .WillOnce(SaveArg<6>(&request)); - EXPECT_CALL(service_observer_, OnPolicyUpdated(public_session_user_id_)); + EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_)); broker->core()->client()->FetchPolicy(); FlushDeviceSettings(); Mock::VerifyAndClearExpectations(&service_observer_); @@ -335,7 +431,7 @@ TEST_F(DeviceLocalAccountPolicyServiceTest, FetchPolicy) { EXPECT_EQ(dm_protocol::kChromePublicAccountPolicyType, request.policy_request().request(0).policy_type()); EXPECT_FALSE(request.policy_request().request(0).has_machine_id()); - EXPECT_EQ(PolicyBuilder::kFakeUsername, + EXPECT_EQ(kAccount1, request.policy_request().request(0).settings_entity_id()); ASSERT_TRUE(broker->core()->store()); @@ -346,26 +442,27 @@ TEST_F(DeviceLocalAccountPolicyServiceTest, FetchPolicy) { broker->core()->store()->policy()->SerializeAsString()); EXPECT_TRUE(expected_policy_map_.Equals( broker->core()->store()->policy_map())); - EXPECT_TRUE(service_.IsPolicyAvailableForUser(public_session_user_id_)); + EXPECT_TRUE(service_->IsPolicyAvailableForUser(account_1_user_id_)); - EXPECT_CALL(service_observer_, OnPolicyUpdated(public_session_user_id_)) + EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_)) .Times(0); - service_.Disconnect(); + service_->Disconnect(); EXPECT_FALSE(broker->core()->client()); Mock::VerifyAndClearExpectations(&service_observer_); - EXPECT_TRUE(service_.IsPolicyAvailableForUser(public_session_user_id_)); + EXPECT_TRUE(service_->IsPolicyAvailableForUser(account_1_user_id_)); } TEST_F(DeviceLocalAccountPolicyServiceTest, RefreshPolicy) { - device_settings_test_helper_.set_device_local_account_policy_blob( - PolicyBuilder::kFakeUsername, device_local_account_policy_.GetBlob()); + InstallDeviceLocalAccountPolicy(kAccount1); + AddDeviceLocalAccountToPolicy(kAccount1); + EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_)); InstallDevicePolicy(); DeviceLocalAccountPolicyBroker* broker = - service_.GetBrokerForUser(public_session_user_id_); + service_->GetBrokerForUser(account_1_user_id_); ASSERT_TRUE(broker); - service_.Connect(&mock_device_management_service_); + service_->Connect(&mock_device_management_service_); ASSERT_TRUE(broker->core()->service()); em::DeviceManagementResponse response; @@ -375,7 +472,7 @@ TEST_F(DeviceLocalAccountPolicyServiceTest, RefreshPolicy) { .WillOnce(mock_device_management_service_.SucceedJob(response)); EXPECT_CALL(mock_device_management_service_, StartJob(_, _, _, _, _, _, _)); EXPECT_CALL(*this, OnRefreshDone(true)).Times(1); - EXPECT_CALL(service_observer_, OnPolicyUpdated(public_session_user_id_)); + EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_)); broker->core()->service()->RefreshPolicy( base::Bind(&DeviceLocalAccountPolicyServiceTest::OnRefreshDone, base::Unretained(this))); @@ -389,90 +486,369 @@ TEST_F(DeviceLocalAccountPolicyServiceTest, RefreshPolicy) { broker->core()->store()->status()); EXPECT_TRUE(expected_policy_map_.Equals( broker->core()->store()->policy_map())); - EXPECT_TRUE(service_.IsPolicyAvailableForUser(public_session_user_id_)); + EXPECT_TRUE(service_->IsPolicyAvailableForUser(account_1_user_id_)); +} + +class DeviceLocalAccountPolicyExtensionCacheTest + : public DeviceLocalAccountPolicyServiceTestBase { + protected: + DeviceLocalAccountPolicyExtensionCacheTest(); + + virtual void SetUp() OVERRIDE; + + base::FilePath GetCacheDirectoryForAccountID(const std::string& account_id); + + base::ScopedTempDir cache_root_dir_; + scoped_ptr<base::ScopedPathOverride> cache_root_dir_override_; + + base::FilePath cache_dir_1_; + base::FilePath cache_dir_2_; + base::FilePath cache_dir_3_; + + private: + DISALLOW_COPY_AND_ASSIGN(DeviceLocalAccountPolicyExtensionCacheTest); +}; + +DeviceLocalAccountPolicyExtensionCacheTest:: + DeviceLocalAccountPolicyExtensionCacheTest() { +} + +void DeviceLocalAccountPolicyExtensionCacheTest::SetUp() { + DeviceLocalAccountPolicyServiceTestBase::SetUp(); + ASSERT_TRUE(cache_root_dir_.CreateUniqueTempDir()); + cache_root_dir_override_.reset(new base::ScopedPathOverride( + chromeos::DIR_DEVICE_LOCAL_ACCOUNT_CACHE, + cache_root_dir_.path())); + + cache_dir_1_ = GetCacheDirectoryForAccountID(kAccount1); + cache_dir_2_ = GetCacheDirectoryForAccountID(kAccount2); + cache_dir_3_ = GetCacheDirectoryForAccountID(kAccount3); + + em::StringList* forcelist = device_local_account_policy_.payload() + .mutable_extensioninstallforcelist()->mutable_value(); + forcelist->add_entries(base::StringPrintf("%s;%s", kExtensionID, kUpdateURL)); +} + +base::FilePath DeviceLocalAccountPolicyExtensionCacheTest:: + GetCacheDirectoryForAccountID(const std::string& account_id) { + return cache_root_dir_.path().Append(base::HexEncode(account_id.c_str(), + account_id.size())); +} + +// Verifies that during startup, orphaned cache directories are deleted, +// cache directories belonging to an existing account are preserved and missing +// cache directories are created. Also verifies that when startup is complete, +// the caches for all existing accounts are running. +TEST_F(DeviceLocalAccountPolicyExtensionCacheTest, Startup) { + base::FilePath test_data_dir; + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir)); + const base::FilePath source_crx_file = + test_data_dir.Append(kExtensionCRXPath); + const std::string target_crx_file_name = + base::StringPrintf("%s-%s.crx", kExtensionID, kExtensionVersion); + + // Create and pre-populate a cache directory for account 1. + EXPECT_TRUE(file_util::CreateDirectory(cache_dir_1_)); + EXPECT_TRUE(CopyFile(source_crx_file, + cache_dir_1_.Append(target_crx_file_name))); + + // Create and pre-populate a cache directory for account 3. + EXPECT_TRUE(file_util::CreateDirectory(cache_dir_3_)); + EXPECT_TRUE(CopyFile(source_crx_file, + cache_dir_3_.Append(target_crx_file_name))); + + // Add accounts 1 and 2 to device policy. + InstallDeviceLocalAccountPolicy(kAccount1); + InstallDeviceLocalAccountPolicy(kAccount2); + AddDeviceLocalAccountToPolicy(kAccount1); + AddDeviceLocalAccountToPolicy(kAccount2); + InstallDevicePolicy(); + + // Create the DeviceLocalAccountPolicyService, allowing it to finish the + // deletion of orphaned cache directories. + CreatePolicyService(); + FlushDeviceSettings(); + extension_cache_task_runner_->RunUntilIdle(); + + // Verify that the cache directory for account 1 and its contents still exist. + EXPECT_TRUE(base::DirectoryExists(cache_dir_1_)); + EXPECT_TRUE(ContentsEqual(source_crx_file, + cache_dir_1_.Append(target_crx_file_name))); + + // Verify that a cache directory for account 2 was created. + EXPECT_TRUE(base::DirectoryExists(cache_dir_2_)); + + // Verify that the cache directory for account 3 was deleted. + EXPECT_FALSE(base::DirectoryExists(cache_dir_3_)); + + // Verify that the cache for account 1 has been started. + DeviceLocalAccountPolicyBroker* broker = + service_->GetBrokerForUser(account_1_user_id_); + ASSERT_TRUE(broker); + EXPECT_TRUE(broker->extension_loader()->IsCacheRunning()); + + // Verify that the cache for account 2 has been started. + broker = service_->GetBrokerForUser(account_2_user_id_); + ASSERT_TRUE(broker); + EXPECT_TRUE(broker->extension_loader()->IsCacheRunning()); +} + +// Verifies that while the deletion of orphaned cache directories is in +// progress, the caches for accounts which existed before the deletion started +// are running but caches for newly added accounts are not started. +TEST_F(DeviceLocalAccountPolicyExtensionCacheTest, RaceAgainstOrphanDeletion) { + // Add account 1 to device policy. + InstallDeviceLocalAccountPolicy(kAccount1); + AddDeviceLocalAccountToPolicy(kAccount1); + InstallDevicePolicy(); + + // Create the DeviceLocalAccountPolicyService, triggering the deletion of + // orphaned cache directories. + CreatePolicyService(); + FlushDeviceSettings(); + + // Verify that the cache for account 1 has been started as it is unaffected by + // the orphan deletion. + DeviceLocalAccountPolicyBroker* broker = + service_->GetBrokerForUser(account_1_user_id_); + ASSERT_TRUE(broker); + EXPECT_TRUE(broker->extension_loader()->IsCacheRunning()); + + // Add account 2 to device policy. + InstallDeviceLocalAccountPolicy(kAccount2); + AddDeviceLocalAccountToPolicy(kAccount2); + InstallDevicePolicy(); + + // Verify that the cache for account 2 has not been started yet as the orphan + // deletion is still in progress. + broker = service_->GetBrokerForUser(account_2_user_id_); + ASSERT_TRUE(broker); + EXPECT_FALSE(broker->extension_loader()->IsCacheRunning()); + + // Allow the orphan deletion to finish. + extension_cache_task_runner_->RunUntilIdle(); + base::RunLoop().RunUntilIdle(); + + // Verify that the cache for account 2 has been started. + EXPECT_TRUE(broker->extension_loader()->IsCacheRunning()); +} + +// Verifies that while the shutdown of a cache is in progress, no new cache is +// started if an account with the same ID is re-added. +TEST_F(DeviceLocalAccountPolicyExtensionCacheTest, RaceAgainstCacheShutdown) { + // Add account 1 to device policy. + InstallDeviceLocalAccountPolicy(kAccount1); + AddDeviceLocalAccountToPolicy(kAccount1); + InstallDevicePolicy(); + + // Create the DeviceLocalAccountPolicyService, allowing it to finish the + // deletion of orphaned cache directories. + CreatePolicyService(); + FlushDeviceSettings(); + extension_cache_task_runner_->RunUntilIdle(); + + // Remove account 1 from device policy, triggering a shutdown of its cache. + device_policy_.payload().mutable_device_local_accounts()->clear_account(); + InstallDevicePolicy(); + + // Re-add account 1 to device policy. + AddDeviceLocalAccountToPolicy(kAccount1); + InstallDevicePolicy(); + + // Verify that the cache for account 1 has not been started yet as the + // shutdown of a previous cache for this account ID is still in progress. + DeviceLocalAccountPolicyBroker* broker = + service_->GetBrokerForUser(account_1_user_id_); + ASSERT_TRUE(broker); + EXPECT_FALSE(broker->extension_loader()->IsCacheRunning()); + + // Allow the cache shutdown to finish. + extension_cache_task_runner_->RunUntilIdle(); + base::RunLoop().RunUntilIdle(); + + // Verify that the cache directory for account 1 still exists. + EXPECT_TRUE(base::DirectoryExists(cache_dir_1_)); + + // Verify that the cache for account 1 has been started, reusing the existing + // cache directory. + EXPECT_TRUE(broker->extension_loader()->IsCacheRunning()); +} + +// Verifies that while the deletion of an obsolete cache directory is in +// progress, no new cache is started if an account with the same ID is re-added. +TEST_F(DeviceLocalAccountPolicyExtensionCacheTest, + RaceAgainstObsoleteDeletion) { + // Add account 1 to device policy. + InstallDeviceLocalAccountPolicy(kAccount1); + AddDeviceLocalAccountToPolicy(kAccount1); + InstallDevicePolicy(); + + // Create the DeviceLocalAccountPolicyService, allowing it to finish the + // deletion of orphaned cache directories. + CreatePolicyService(); + FlushDeviceSettings(); + extension_cache_task_runner_->RunUntilIdle(); + + // Remove account 1 from device policy, allowing the shutdown of its cache to + // finish and the deletion of its now obsolete cache directory to begin. + device_policy_.payload().mutable_device_local_accounts()->clear_account(); + InstallDevicePolicy(); + extension_cache_task_runner_->RunUntilIdle(); + base::RunLoop().RunUntilIdle(); + + // Re-add account 1 to device policy. + AddDeviceLocalAccountToPolicy(kAccount1); + InstallDevicePolicy(); + + // Verify that the cache for account 1 has not been started yet as the + // deletion of the cache directory for this account ID is still in progress. + DeviceLocalAccountPolicyBroker* broker = + service_->GetBrokerForUser(account_1_user_id_); + ASSERT_TRUE(broker); + EXPECT_FALSE(broker->extension_loader()->IsCacheRunning()); + + // Allow the deletion to finish. + extension_cache_task_runner_->RunUntilIdle(); + base::RunLoop().RunUntilIdle(); + + // Verify that the cache directory for account 1 was deleted. + EXPECT_FALSE(base::DirectoryExists(cache_dir_1_)); + + // Verify that the cache for account 1 has been started. + EXPECT_TRUE(broker->extension_loader()->IsCacheRunning()); +} + +// Verifies that when an account is added and no deletion of cache directories +// affecting this account is in progress, its cache is started immediately. +TEST_F(DeviceLocalAccountPolicyExtensionCacheTest, AddAccount) { + // Create the DeviceLocalAccountPolicyService, allowing it to finish the + // deletion of orphaned cache directories. + InstallDevicePolicy(); + CreatePolicyService(); + FlushDeviceSettings(); + extension_cache_task_runner_->RunUntilIdle(); + + // Add account 1 to device policy. + InstallDeviceLocalAccountPolicy(kAccount1); + AddDeviceLocalAccountToPolicy(kAccount1); + InstallDevicePolicy(); + + // Verify that the cache for account 1 has been started. + DeviceLocalAccountPolicyBroker* broker = + service_->GetBrokerForUser(account_1_user_id_); + ASSERT_TRUE(broker); + EXPECT_TRUE(broker->extension_loader()->IsCacheRunning()); +} + +// Verifies that when an account is removed, its cache directory is deleted. +TEST_F(DeviceLocalAccountPolicyExtensionCacheTest, RemoveAccount) { + // Add account 1 to device policy. + InstallDeviceLocalAccountPolicy(kAccount1); + AddDeviceLocalAccountToPolicy(kAccount1); + InstallDevicePolicy(); + + // Create the DeviceLocalAccountPolicyService, allowing it to finish the + // deletion of orphaned cache directories. + CreatePolicyService(); + FlushDeviceSettings(); + extension_cache_task_runner_->RunUntilIdle(); + + // Verify that a cache directory has been created for account 1. + EXPECT_TRUE(base::DirectoryExists(cache_dir_1_)); + + // Remove account 1 from device policy, allowing the deletion of its now + // obsolete cache directory to finish. + device_policy_.payload().mutable_device_local_accounts()->clear_account(); + InstallDevicePolicy(); + extension_cache_task_runner_->RunUntilIdle(); + base::RunLoop().RunUntilIdle(); + extension_cache_task_runner_->RunUntilIdle(); + + // Verify that the cache directory for account 1 was deleted. + EXPECT_FALSE(base::DirectoryExists(cache_dir_1_)); } class DeviceLocalAccountPolicyProviderTest - : public DeviceLocalAccountPolicyServiceTest { + : public DeviceLocalAccountPolicyServiceTestBase { protected: - DeviceLocalAccountPolicyProviderTest() - : provider_( - GenerateDeviceLocalAccountUserId( - PolicyBuilder::kFakeUsername, - DeviceLocalAccount::TYPE_PUBLIC_SESSION), - &service_) {} - - virtual void SetUp() OVERRIDE { - DeviceLocalAccountPolicyServiceTest::SetUp(); - provider_.Init(); - provider_.AddObserver(&provider_observer_); - - EXPECT_CALL(service_observer_, OnPolicyUpdated(_)).Times(AnyNumber()); - EXPECT_CALL(service_observer_, OnDeviceLocalAccountsChanged()) - .Times(AnyNumber()); - } - - virtual void TearDown() OVERRIDE { - provider_.RemoveObserver(&provider_observer_); - provider_.Shutdown(); - DeviceLocalAccountPolicyServiceTest::TearDown(); - } - - DeviceLocalAccountPolicyProvider provider_; + DeviceLocalAccountPolicyProviderTest(); + + virtual void SetUp() OVERRIDE; + virtual void TearDown() OVERRIDE; + + scoped_ptr<DeviceLocalAccountPolicyProvider> provider_; MockConfigurationPolicyObserver provider_observer_; private: DISALLOW_COPY_AND_ASSIGN(DeviceLocalAccountPolicyProviderTest); }; +DeviceLocalAccountPolicyProviderTest::DeviceLocalAccountPolicyProviderTest() { + CreatePolicyService(); + provider_.reset(new DeviceLocalAccountPolicyProvider( + GenerateDeviceLocalAccountUserId(kAccount1, + DeviceLocalAccount::TYPE_PUBLIC_SESSION), + service_.get())); +} + +void DeviceLocalAccountPolicyProviderTest::SetUp() { + DeviceLocalAccountPolicyServiceTestBase::SetUp(); + provider_->Init(); + provider_->AddObserver(&provider_observer_); +} + +void DeviceLocalAccountPolicyProviderTest::TearDown() { + provider_->RemoveObserver(&provider_observer_); + provider_->Shutdown(); + provider_.reset(); + DeviceLocalAccountPolicyServiceTestBase::TearDown(); +} + TEST_F(DeviceLocalAccountPolicyProviderTest, Initialization) { - EXPECT_FALSE(provider_.IsInitializationComplete(POLICY_DOMAIN_CHROME)); + EXPECT_FALSE(provider_->IsInitializationComplete(POLICY_DOMAIN_CHROME)); // Policy change should complete initialization. - EXPECT_CALL(provider_observer_, OnUpdatePolicy(&provider_)).Times(AtLeast(1)); - device_settings_test_helper_.set_device_local_account_policy_blob( - PolicyBuilder::kFakeUsername, device_local_account_policy_.GetBlob()); - device_settings_test_helper_.set_policy_blob(device_policy_.GetBlob()); - ReloadDeviceSettings(); + InstallDeviceLocalAccountPolicy(kAccount1); + AddDeviceLocalAccountToPolicy(kAccount1); + EXPECT_CALL(provider_observer_, OnUpdatePolicy(provider_.get())) + .Times(AtLeast(1)); + InstallDevicePolicy(); Mock::VerifyAndClearExpectations(&provider_observer_); - EXPECT_TRUE(provider_.IsInitializationComplete(POLICY_DOMAIN_CHROME)); + EXPECT_TRUE(provider_->IsInitializationComplete(POLICY_DOMAIN_CHROME)); // The account disappearing should *not* flip the initialization flag back. - EXPECT_CALL(provider_observer_, OnUpdatePolicy(&provider_)) + EXPECT_CALL(provider_observer_, OnUpdatePolicy(provider_.get())) .Times(AnyNumber()); device_policy_.payload().mutable_device_local_accounts()->clear_account(); - device_policy_.Build(); - device_settings_test_helper_.set_policy_blob(device_policy_.GetBlob()); - ReloadDeviceSettings(); + InstallDevicePolicy(); Mock::VerifyAndClearExpectations(&provider_observer_); - EXPECT_TRUE(provider_.IsInitializationComplete(POLICY_DOMAIN_CHROME)); + EXPECT_TRUE(provider_->IsInitializationComplete(POLICY_DOMAIN_CHROME)); } TEST_F(DeviceLocalAccountPolicyProviderTest, Policy) { // Policy should load successfully. - EXPECT_CALL(provider_observer_, OnUpdatePolicy(&provider_)).Times(AtLeast(1)); - device_settings_test_helper_.set_policy_blob(device_policy_.GetBlob()); - device_settings_test_helper_.set_device_local_account_policy_blob( - PolicyBuilder::kFakeUsername, device_local_account_policy_.GetBlob()); - ReloadDeviceSettings(); + EXPECT_CALL(provider_observer_, OnUpdatePolicy(provider_.get())) + .Times(AtLeast(1)); + InstallDeviceLocalAccountPolicy(kAccount1); + AddDeviceLocalAccountToPolicy(kAccount1); + InstallDevicePolicy(); Mock::VerifyAndClearExpectations(&provider_observer_); PolicyBundle expected_policy_bundle; expected_policy_bundle.Get(PolicyNamespace( POLICY_DOMAIN_CHROME, std::string())).CopyFrom(expected_policy_map_); - EXPECT_TRUE(expected_policy_bundle.Equals(provider_.policies())); + EXPECT_TRUE(expected_policy_bundle.Equals(provider_->policies())); // Policy change should be reported. - EXPECT_CALL(provider_observer_, OnUpdatePolicy(&provider_)).Times(AtLeast(1)); + EXPECT_CALL(provider_observer_, OnUpdatePolicy(provider_.get())) + .Times(AtLeast(1)); device_local_account_policy_.payload().mutable_disablespdy()->set_value( false); - device_local_account_policy_.Build(); - device_settings_test_helper_.set_device_local_account_policy_blob( - PolicyBuilder::kFakeUsername, device_local_account_policy_.GetBlob()); + InstallDeviceLocalAccountPolicy(kAccount1); DeviceLocalAccountPolicyBroker* broker = - service_.GetBrokerForUser(public_session_user_id_); + service_->GetBrokerForUser(account_1_user_id_); ASSERT_TRUE(broker); broker->core()->store()->Load(); FlushDeviceSettings(); @@ -485,57 +861,55 @@ TEST_F(DeviceLocalAccountPolicyProviderTest, Policy) { POLICY_SCOPE_USER, Value::CreateBooleanValue(false), NULL); - EXPECT_TRUE(expected_policy_bundle.Equals(provider_.policies())); + EXPECT_TRUE(expected_policy_bundle.Equals(provider_->policies())); // Any values set for the |ShelfAutoHideBehavior|, |ShowLogoutButtonInTray| // and |ExtensionAllowedTypes| policies should be overridden. - EXPECT_CALL(provider_observer_, OnUpdatePolicy(&provider_)).Times(AtLeast(1)); + EXPECT_CALL(provider_observer_, OnUpdatePolicy(provider_.get())) + .Times(AtLeast(1)); device_local_account_policy_.payload().mutable_shelfautohidebehavior()-> set_value("Always"); device_local_account_policy_.payload().mutable_showlogoutbuttonintray()-> set_value(false); - device_local_account_policy_.Build(); - device_settings_test_helper_.set_device_local_account_policy_blob( - PolicyBuilder::kFakeUsername, device_local_account_policy_.GetBlob()); + InstallDeviceLocalAccountPolicy(kAccount1); broker->core()->store()->Load(); FlushDeviceSettings(); Mock::VerifyAndClearExpectations(&provider_observer_); - EXPECT_TRUE(expected_policy_bundle.Equals(provider_.policies())); + EXPECT_TRUE(expected_policy_bundle.Equals(provider_->policies())); // Account disappears, policy should stay in effect. - EXPECT_CALL(provider_observer_, OnUpdatePolicy(&provider_)) + EXPECT_CALL(provider_observer_, OnUpdatePolicy(provider_.get())) .Times(AnyNumber()); device_policy_.payload().mutable_device_local_accounts()->clear_account(); - device_policy_.Build(); - device_settings_test_helper_.set_policy_blob(device_policy_.GetBlob()); - ReloadDeviceSettings(); + InstallDevicePolicy(); Mock::VerifyAndClearExpectations(&provider_observer_); - EXPECT_TRUE(expected_policy_bundle.Equals(provider_.policies())); + EXPECT_TRUE(expected_policy_bundle.Equals(provider_->policies())); } TEST_F(DeviceLocalAccountPolicyProviderTest, RefreshPolicies) { // If there's no device policy, the refresh completes immediately. - EXPECT_FALSE(service_.GetBrokerForUser(public_session_user_id_)); - EXPECT_CALL(provider_observer_, OnUpdatePolicy(&provider_)).Times(AtLeast(1)); - provider_.RefreshPolicies(); + EXPECT_FALSE(service_->GetBrokerForUser(account_1_user_id_)); + EXPECT_CALL(provider_observer_, OnUpdatePolicy(provider_.get())) + .Times(AtLeast(1)); + provider_->RefreshPolicies(); Mock::VerifyAndClearExpectations(&provider_observer_); // Make device settings appear. - EXPECT_CALL(provider_observer_, OnUpdatePolicy(&provider_)) + EXPECT_CALL(provider_observer_, OnUpdatePolicy(provider_.get())) .Times(AnyNumber()); - device_settings_test_helper_.set_policy_blob(device_policy_.GetBlob()); - ReloadDeviceSettings(); - Mock::VerifyAndClearExpectations(&provider_observer_); - EXPECT_TRUE(service_.GetBrokerForUser(public_session_user_id_)); + AddDeviceLocalAccountToPolicy(kAccount1); + InstallDevicePolicy(); + EXPECT_TRUE(service_->GetBrokerForUser(account_1_user_id_)); // If there's no cloud connection, refreshes are still immediate. DeviceLocalAccountPolicyBroker* broker = - service_.GetBrokerForUser(public_session_user_id_); + service_->GetBrokerForUser(account_1_user_id_); ASSERT_TRUE(broker); EXPECT_FALSE(broker->core()->client()); - EXPECT_CALL(provider_observer_, OnUpdatePolicy(&provider_)).Times(AtLeast(1)); - provider_.RefreshPolicies(); + EXPECT_CALL(provider_observer_, OnUpdatePolicy(provider_.get())) + .Times(AtLeast(1)); + provider_->RefreshPolicies(); Mock::VerifyAndClearExpectations(&provider_observer_); // Bring up the cloud connection. The refresh scheduler may fire refreshes at @@ -545,7 +919,7 @@ TEST_F(DeviceLocalAccountPolicyProviderTest, RefreshPolicies) { mock_device_management_service_.FailJob(DM_STATUS_REQUEST_FAILED)); EXPECT_CALL(mock_device_management_service_, StartJob(_, _, _, _, _, _, _)) .Times(AnyNumber()); - service_.Connect(&mock_device_management_service_); + service_->Connect(&mock_device_management_service_); FlushDeviceSettings(); Mock::VerifyAndClearExpectations(&mock_device_management_service_); @@ -555,16 +929,18 @@ TEST_F(DeviceLocalAccountPolicyProviderTest, RefreshPolicies) { EXPECT_CALL(mock_device_management_service_, CreateJob(_)) .WillOnce(mock_device_management_service_.CreateAsyncJob(&request_job)); EXPECT_CALL(mock_device_management_service_, StartJob(_, _, _, _, _, _, _)); - provider_.RefreshPolicies(); + provider_->RefreshPolicies(); ReloadDeviceSettings(); Mock::VerifyAndClearExpectations(&provider_observer_); Mock::VerifyAndClearExpectations(&mock_device_management_service_); - EXPECT_TRUE(provider_.IsInitializationComplete(POLICY_DOMAIN_CHROME)); + EXPECT_TRUE(provider_->IsInitializationComplete(POLICY_DOMAIN_CHROME)); // When the response comes in, it should propagate and fire the notification. - EXPECT_CALL(provider_observer_, OnUpdatePolicy(&provider_)).Times(AtLeast(1)); + EXPECT_CALL(provider_observer_, OnUpdatePolicy(provider_.get())) + .Times(AtLeast(1)); ASSERT_TRUE(request_job); em::DeviceManagementResponse response; + device_local_account_policy_.Build(); response.mutable_policy_response()->add_response()->CopyFrom( device_local_account_policy_.policy()); request_job->SendResponse(DM_STATUS_SUCCESS, response); diff --git a/chrome/browser/chromeos/policy/device_local_account_policy_store.cc b/chrome/browser/chromeos/policy/device_local_account_policy_store.cc index 898733a134..553d5cf707 100644 --- a/chrome/browser/chromeos/policy/device_local_account_policy_store.cc +++ b/chrome/browser/chromeos/policy/device_local_account_policy_store.cc @@ -23,8 +23,10 @@ namespace policy { DeviceLocalAccountPolicyStore::DeviceLocalAccountPolicyStore( const std::string& account_id, chromeos::SessionManagerClient* session_manager_client, - chromeos::DeviceSettingsService* device_settings_service) - : account_id_(account_id), + chromeos::DeviceSettingsService* device_settings_service, + scoped_refptr<base::SequencedTaskRunner> background_task_runner) + : UserCloudPolicyStoreBase(background_task_runner), + account_id_(account_id), session_manager_client_(session_manager_client), device_settings_service_(device_settings_service), weak_factory_(this) {} @@ -169,7 +171,8 @@ void DeviceLocalAccountPolicyStore::Validate( } scoped_ptr<UserCloudPolicyValidator> validator( - UserCloudPolicyValidator::Create(policy_response.Pass())); + UserCloudPolicyValidator::Create(policy_response.Pass(), + background_task_runner())); validator->ValidateUsername(account_id_); validator->ValidatePolicyType(dm_protocol::kChromePublicAccountPolicyType); validator->ValidateAgainstCurrentPolicy( diff --git a/chrome/browser/chromeos/policy/device_local_account_policy_store.h b/chrome/browser/chromeos/policy/device_local_account_policy_store.h index 9036bb6de7..3af5134e47 100644 --- a/chrome/browser/chromeos/policy/device_local_account_policy_store.h +++ b/chrome/browser/chromeos/policy/device_local_account_policy_store.h @@ -9,12 +9,17 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "chrome/browser/chromeos/settings/device_settings_service.h" #include "chrome/browser/policy/cloud/cloud_policy_validator.h" #include "chrome/browser/policy/cloud/user_cloud_policy_store_base.h" +namespace base { +class SequencedTaskRunner; +} + namespace chromeos { class DeviceSettingsService; class SessionManagerClient; @@ -36,7 +41,8 @@ class DeviceLocalAccountPolicyStore DeviceLocalAccountPolicyStore( const std::string& account_id, chromeos::SessionManagerClient* client, - chromeos::DeviceSettingsService* device_settings_service); + chromeos::DeviceSettingsService* device_settings_service, + scoped_refptr<base::SequencedTaskRunner> background_task_runner); virtual ~DeviceLocalAccountPolicyStore(); const std::string& account_id() const { return account_id_; } @@ -76,6 +82,8 @@ class DeviceLocalAccountPolicyStore chromeos::SessionManagerClient* session_manager_client_; chromeos::DeviceSettingsService* device_settings_service_; + scoped_refptr<base::SequencedTaskRunner> background_task_runner_; + base::WeakPtrFactory<DeviceLocalAccountPolicyStore> weak_factory_; DISALLOW_COPY_AND_ASSIGN(DeviceLocalAccountPolicyStore); diff --git a/chrome/browser/chromeos/policy/device_policy_cros_browser_test.cc b/chrome/browser/chromeos/policy/device_policy_cros_browser_test.cc index 93058379ed..26065eb99f 100644 --- a/chrome/browser/chromeos/policy/device_policy_cros_browser_test.cc +++ b/chrome/browser/chromeos/policy/device_policy_cros_browser_test.cc @@ -15,8 +15,8 @@ #include "chrome/browser/chromeos/policy/enterprise_install_attributes.h" #include "chrome/browser/policy/proto/chromeos/install_attributes.pb.h" #include "chromeos/chromeos_paths.h" +#include "chromeos/dbus/fake_dbus_thread_manager.h" #include "chromeos/dbus/fake_session_manager_client.h" -#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" #include "crypto/rsa_private_key.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -72,15 +72,15 @@ void DevicePolicyCrosTestHelper::InstallOwnerKey() { } DevicePolicyCrosBrowserTest::DevicePolicyCrosBrowserTest() - : mock_dbus_thread_manager_( - new chromeos::MockDBusThreadManagerWithoutGMock) { + : fake_dbus_thread_manager_( + new chromeos::FakeDBusThreadManager) { } DevicePolicyCrosBrowserTest::~DevicePolicyCrosBrowserTest() { } void DevicePolicyCrosBrowserTest::SetUpInProcessBrowserTestFixture() { - chromeos::DBusThreadManager::InitializeForTesting(mock_dbus_thread_manager_); + chromeos::DBusThreadManager::InitializeForTesting(fake_dbus_thread_manager_); InProcessBrowserTest::SetUpInProcessBrowserTestFixture(); } diff --git a/chrome/browser/chromeos/policy/device_policy_cros_browser_test.h b/chrome/browser/chromeos/policy/device_policy_cros_browser_test.h index 1a0dafc974..afe39ba19f 100644 --- a/chrome/browser/chromeos/policy/device_policy_cros_browser_test.h +++ b/chrome/browser/chromeos/policy/device_policy_cros_browser_test.h @@ -10,7 +10,7 @@ #include "base/files/scoped_temp_dir.h" #include "chrome/browser/chromeos/policy/device_policy_builder.h" #include "chrome/test/base/in_process_browser_test.h" -#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" +#include "chromeos/dbus/fake_dbus_thread_manager.h" namespace chromeos { class FakeSessionManagerClient; @@ -64,12 +64,12 @@ class DevicePolicyCrosBrowserTest : public InProcessBrowserTest { // recently changed). void RefreshDevicePolicy(); - chromeos::MockDBusThreadManagerWithoutGMock* mock_dbus_thread_manager() { - return mock_dbus_thread_manager_; + chromeos::FakeDBusThreadManager* fake_dbus_thread_manager() { + return fake_dbus_thread_manager_; } chromeos::FakeSessionManagerClient* session_manager_client() { - return mock_dbus_thread_manager_->fake_session_manager_client(); + return fake_dbus_thread_manager_->fake_session_manager_client(); } DevicePolicyBuilder* device_policy() { return test_helper_.device_policy(); } @@ -77,8 +77,8 @@ class DevicePolicyCrosBrowserTest : public InProcessBrowserTest { private: DevicePolicyCrosTestHelper test_helper_; - // MockDBusThreadManagerWithoutGMock uses FakeSessionManagerClient. - chromeos::MockDBusThreadManagerWithoutGMock* mock_dbus_thread_manager_; + // FakeDBusThreadManager uses FakeSessionManagerClient. + chromeos::FakeDBusThreadManager* fake_dbus_thread_manager_; DISALLOW_COPY_AND_ASSIGN(DevicePolicyCrosBrowserTest); }; diff --git a/chrome/browser/chromeos/policy/enrollment_handler_chromeos.cc b/chrome/browser/chromeos/policy/enrollment_handler_chromeos.cc index c4990df245..182b46a76d 100644 --- a/chrome/browser/chromeos/policy/enrollment_handler_chromeos.cc +++ b/chrome/browser/chromeos/policy/enrollment_handler_chromeos.cc @@ -33,6 +33,7 @@ EnrollmentHandlerChromeOS::EnrollmentHandlerChromeOS( DeviceCloudPolicyStoreChromeOS* store, EnterpriseInstallAttributes* install_attributes, scoped_ptr<CloudPolicyClient> client, + scoped_refptr<base::SequencedTaskRunner> background_task_runner, const std::string& auth_token, const std::string& client_id, bool is_auto_enrollment, @@ -42,6 +43,7 @@ EnrollmentHandlerChromeOS::EnrollmentHandlerChromeOS( : store_(store), install_attributes_(install_attributes), client_(client.Pass()), + background_task_runner_(background_task_runner), auth_token_(auth_token), client_id_(client_id), is_auto_enrollment_(is_auto_enrollment), @@ -94,7 +96,8 @@ void EnrollmentHandlerChromeOS::OnPolicyFetched(CloudPolicyClient* client) { scoped_ptr<DeviceCloudPolicyValidator> validator( DeviceCloudPolicyValidator::Create( scoped_ptr<em::PolicyFetchResponse>( - new em::PolicyFetchResponse(*policy)))); + new em::PolicyFetchResponse(*policy)), + background_task_runner_)); validator->ValidateTimestamp(base::Time(), base::Time::NowFromSystemTime(), CloudPolicyValidatorBase::TIMESTAMP_REQUIRED); diff --git a/chrome/browser/chromeos/policy/enrollment_handler_chromeos.h b/chrome/browser/chromeos/policy/enrollment_handler_chromeos.h index c470e0881f..74b0fd17bc 100644 --- a/chrome/browser/chromeos/policy/enrollment_handler_chromeos.h +++ b/chrome/browser/chromeos/policy/enrollment_handler_chromeos.h @@ -9,6 +9,7 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h" @@ -18,6 +19,10 @@ #include "chrome/browser/policy/cloud/cloud_policy_store.h" #include "google_apis/gaia/gaia_oauth_client.h" +namespace base { +class SequencedTaskRunner; +} + namespace enterprise_management { class PolicyFetchResponse; } @@ -49,15 +54,17 @@ class EnrollmentHandlerChromeOS : public CloudPolicyClient::Observer, // are acceptable. If the mode specified by the server is not acceptable, // enrollment will fail with an EnrollmentStatus indicating // STATUS_REGISTRATION_BAD_MODE. - EnrollmentHandlerChromeOS(DeviceCloudPolicyStoreChromeOS* store, - EnterpriseInstallAttributes* install_attributes, - scoped_ptr<CloudPolicyClient> client, - const std::string& auth_token, - const std::string& client_id, - bool is_auto_enrollment, - const std::string& requisition, - const AllowedDeviceModes& allowed_device_modes, - const EnrollmentCallback& completion_callback); + EnrollmentHandlerChromeOS( + DeviceCloudPolicyStoreChromeOS* store, + EnterpriseInstallAttributes* install_attributes, + scoped_ptr<CloudPolicyClient> client, + scoped_refptr<base::SequencedTaskRunner> background_task_runner, + const std::string& auth_token, + const std::string& client_id, + bool is_auto_enrollment, + const std::string& requisition, + const AllowedDeviceModes& allowed_device_modes, + const EnrollmentCallback& completion_callback); virtual ~EnrollmentHandlerChromeOS(); // Starts the enrollment process and reports the result to @@ -138,6 +145,7 @@ class EnrollmentHandlerChromeOS : public CloudPolicyClient::Observer, DeviceCloudPolicyStoreChromeOS* store_; EnterpriseInstallAttributes* install_attributes_; scoped_ptr<CloudPolicyClient> client_; + scoped_refptr<base::SequencedTaskRunner> background_task_runner_; scoped_ptr<gaia::GaiaOAuthClient> gaia_oauth_client_; std::string auth_token_; diff --git a/chrome/browser/chromeos/policy/network_configuration_updater.cc b/chrome/browser/chromeos/policy/network_configuration_updater.cc index d4f29c22ed..ae998cd958 100644 --- a/chrome/browser/chromeos/policy/network_configuration_updater.cc +++ b/chrome/browser/chromeos/policy/network_configuration_updater.cc @@ -91,9 +91,12 @@ void NetworkConfigurationUpdater::ImportCertificates( } void NetworkConfigurationUpdater::ApplyNetworkPolicy( - base::ListValue* network_configs_onc) { - network_config_handler_->SetPolicy( - onc_source_, std::string() /* no username hash */, *network_configs_onc); + base::ListValue* network_configs_onc, + base::DictionaryValue* global_network_config) { + network_config_handler_->SetPolicy(onc_source_, + std::string() /* no username hash */, + *network_configs_onc, + *global_network_config); } void NetworkConfigurationUpdater::OnPolicyChanged( @@ -115,15 +118,17 @@ void NetworkConfigurationUpdater::ApplyPolicy() { LOG(ERROR) << LogHeader() << " is not a string value."; base::ListValue network_configs; + base::DictionaryValue global_network_config; base::ListValue certificates; chromeos::onc::ParseAndValidateOncForImport(onc_blob, onc_source_, "" /* no passphrase */, &network_configs, + &global_network_config, &certificates); ImportCertificates(certificates); - ApplyNetworkPolicy(&network_configs); + ApplyNetworkPolicy(&network_configs, &global_network_config); } std::string NetworkConfigurationUpdater::LogHeader() const { diff --git a/chrome/browser/chromeos/policy/network_configuration_updater.h b/chrome/browser/chromeos/policy/network_configuration_updater.h index 3e7fe0d494..4b6556ba95 100644 --- a/chrome/browser/chromeos/policy/network_configuration_updater.h +++ b/chrome/browser/chromeos/policy/network_configuration_updater.h @@ -14,6 +14,8 @@ #include "components/onc/onc_constants.h" namespace base { +class DictionaryValue; +class ListValue; class Value; } @@ -69,7 +71,8 @@ class NetworkConfigurationUpdater : public PolicyService::Observer { // Pushes the network part of the policy to the // ManagedNetworkConfigurationHandler. This can be overridden by subclasses to // modify |network_configs_onc| before the actual application. - virtual void ApplyNetworkPolicy(base::ListValue* network_configs_onc); + virtual void ApplyNetworkPolicy(base::ListValue* network_configs_onc, + base::DictionaryValue* global_network_config); onc::ONCSource onc_source_; diff --git a/chrome/browser/chromeos/policy/network_configuration_updater_unittest.cc b/chrome/browser/chromeos/policy/network_configuration_updater_unittest.cc index 6b48c5cad2..8f72c50a0a 100644 --- a/chrome/browser/chromeos/policy/network_configuration_updater_unittest.cc +++ b/chrome/browser/chromeos/policy/network_configuration_updater_unittest.cc @@ -71,6 +71,9 @@ const char kFakeONC[] = " \"Security\": \"None\" }" " }" " ]," + " \"GlobalNetworkConfiguration\": {" + " \"AllowOnlyPolicyNetworksToAutoconnect\": true," + " }," " \"Certificates\": [" " { \"GUID\": \"{f998f760-272b-6939-4c2beffe428697ac}\"," " \"PKCS12\": \"abc\"," @@ -79,20 +82,31 @@ const char kFakeONC[] = " \"Type\": \"UnencryptedConfiguration\"" "}"; -std::string ValueToString(const base::Value* value) { +std::string ValueToString(const base::Value& value) { std::stringstream str; - str << *value; + str << value; return str.str(); } +void AppendAll(const base::ListValue& from, base::ListValue* to) { + for (base::ListValue::const_iterator it = from.begin(); it != from.end(); + ++it) { + to->Append((*it)->DeepCopy()); + } +} + // Matcher to match base::Value. MATCHER_P(IsEqualTo, value, std::string(negation ? "isn't" : "is") + " equal to " + - ValueToString(value)) { + ValueToString(*value)) { return value->Equals(&arg); } +MATCHER(IsEmpty, std::string(negation ? "isn't" : "is") + " empty.") { + return arg.empty(); +} + ACTION_P(SetCertificateList, list) { if (arg2) *arg2 = list; @@ -114,25 +128,23 @@ class NetworkConfigurationUpdaterTest : public testing::Test { providers.push_back(&provider_); policy_service_.reset(new PolicyServiceImpl(providers)); - empty_network_configs_.reset(new base::ListValue); - empty_certificates_.reset(new base::ListValue); - scoped_ptr<base::DictionaryValue> fake_toplevel_onc = chromeos::onc::ReadDictionaryFromJson(kFakeONC); - scoped_ptr<base::Value> network_configs_value; base::ListValue* network_configs = NULL; - fake_toplevel_onc->RemoveWithoutPathExpansion( - onc::toplevel_config::kNetworkConfigurations, &network_configs_value); - network_configs_value.release()->GetAsList(&network_configs); - fake_network_configs_.reset(network_configs); + fake_toplevel_onc->GetListWithoutPathExpansion( + onc::toplevel_config::kNetworkConfigurations, &network_configs); + AppendAll(*network_configs, &fake_network_configs_); + + base::DictionaryValue* global_config = NULL; + fake_toplevel_onc->GetDictionaryWithoutPathExpansion( + onc::toplevel_config::kGlobalNetworkConfiguration, &global_config); + fake_global_network_config_.MergeDictionary(global_config); - scoped_ptr<base::Value> certs_value; base::ListValue* certs = NULL; - fake_toplevel_onc->RemoveWithoutPathExpansion( - onc::toplevel_config::kCertificates, &certs_value); - certs_value.release()->GetAsList(&certs); - fake_certificates_.reset(certs); + fake_toplevel_onc->GetListWithoutPathExpansion( + onc::toplevel_config::kCertificates, &certs); + AppendAll(*certs, &fake_certificates_); certificate_importer_ = new StrictMock<chromeos::onc::MockCertificateImporter>(); @@ -172,10 +184,9 @@ class NetworkConfigurationUpdaterTest : public testing::Test { &network_config_handler_); } - scoped_ptr<base::ListValue> empty_network_configs_; - scoped_ptr<base::ListValue> empty_certificates_; - scoped_ptr<base::ListValue> fake_network_configs_; - scoped_ptr<base::ListValue> fake_certificates_; + base::ListValue fake_network_configs_; + base::DictionaryValue fake_global_network_config_; + base::ListValue fake_certificates_; StrictMock<chromeos::MockManagedNetworkConfigurationHandler> network_config_handler_; @@ -207,6 +218,12 @@ TEST_F(NetworkConfigurationUpdaterTest, PolicyIsValidatedAndRepaired) { onc::toplevel_config::kNetworkConfigurations, &network_configs_repaired); ASSERT_TRUE(network_configs_repaired); + base::DictionaryValue* global_config_repaired = NULL; + onc_repaired->GetDictionaryWithoutPathExpansion( + onc::toplevel_config::kGlobalNetworkConfiguration, + &global_config_repaired); + ASSERT_TRUE(global_config_repaired); + PolicyMap policy; policy.Set(key::kOpenNetworkConfiguration, POLICY_LEVEL_MANDATORY, @@ -215,10 +232,11 @@ TEST_F(NetworkConfigurationUpdaterTest, PolicyIsValidatedAndRepaired) { NULL); UpdateProviderPolicy(policy); - EXPECT_CALL( - network_config_handler_, - SetPolicy( - onc::ONC_SOURCE_USER_POLICY, _, IsEqualTo(network_configs_repaired))); + EXPECT_CALL(network_config_handler_, + SetPolicy(onc::ONC_SOURCE_USER_POLICY, + _, + IsEqualTo(network_configs_repaired), + IsEqualTo(global_config_repaired))); EXPECT_CALL(*certificate_importer_, ImportCertificates(_, onc::ONC_SOURCE_USER_POLICY, _)); @@ -236,7 +254,7 @@ TEST_F(NetworkConfigurationUpdaterTest, ASSERT_EQ(1u, cert_list.size()); EXPECT_CALL(network_config_handler_, - SetPolicy(onc::ONC_SOURCE_USER_POLICY, _, _)); + SetPolicy(onc::ONC_SOURCE_USER_POLICY, _, _, _)); EXPECT_CALL(*certificate_importer_, ImportCertificates(_, _, _)) .WillRepeatedly(SetCertificateList(cert_list)); @@ -265,7 +283,7 @@ TEST_F(NetworkConfigurationUpdaterTest, AllowTrustedCertificatesFromPolicy) { ASSERT_EQ(1u, cert_list.size()); EXPECT_CALL(network_config_handler_, - SetPolicy(onc::ONC_SOURCE_USER_POLICY, _, _)); + SetPolicy(onc::ONC_SOURCE_USER_POLICY, _, _, _)); EXPECT_CALL(*certificate_importer_, ImportCertificates(_, onc::ONC_SOURCE_USER_POLICY, _)) .WillRepeatedly(SetCertificateList(cert_list)); @@ -325,10 +343,11 @@ TEST_P(NetworkConfigurationUpdaterTestWithParam, InitialUpdates) { EXPECT_CALL(network_config_handler_, SetPolicy(CurrentONCSource(), ExpectedUsernameHash(), - IsEqualTo(fake_network_configs_.get()))); + IsEqualTo(&fake_network_configs_), + IsEqualTo(&fake_global_network_config_))); EXPECT_CALL(*certificate_importer_, ImportCertificates( - IsEqualTo(fake_certificates_.get()), CurrentONCSource(), _)); + IsEqualTo(&fake_certificates_), CurrentONCSource(), _)); CreateNetworkConfigurationUpdater(); } @@ -336,7 +355,7 @@ TEST_P(NetworkConfigurationUpdaterTestWithParam, InitialUpdates) { TEST_P(NetworkConfigurationUpdaterTestWithParam, PolicyChange) { // Ignore the initial updates. - EXPECT_CALL(network_config_handler_, SetPolicy(_, _, _)).Times(AtLeast(1)); + EXPECT_CALL(network_config_handler_, SetPolicy(_, _, _, _)).Times(AtLeast(1)); EXPECT_CALL(*certificate_importer_, ImportCertificates(_, _, _)) .Times(AtLeast(1)); CreateNetworkConfigurationUpdater(); @@ -344,12 +363,14 @@ TEST_P(NetworkConfigurationUpdaterTestWithParam, PolicyChange) { Mock::VerifyAndClearExpectations(certificate_importer_); // The Updater should update if policy changes. - EXPECT_CALL( - network_config_handler_, - SetPolicy(CurrentONCSource(), _, IsEqualTo(fake_network_configs_.get()))); + EXPECT_CALL(network_config_handler_, + SetPolicy(CurrentONCSource(), + _, + IsEqualTo(&fake_network_configs_), + IsEqualTo(&fake_global_network_config_))); EXPECT_CALL(*certificate_importer_, ImportCertificates( - IsEqualTo(fake_certificates_.get()), CurrentONCSource(), _)); + IsEqualTo(&fake_certificates_), CurrentONCSource(), _)); PolicyMap policy; policy.Set(GetParam(), POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, @@ -359,13 +380,10 @@ TEST_P(NetworkConfigurationUpdaterTestWithParam, PolicyChange) { Mock::VerifyAndClearExpectations(certificate_importer_); // Another update is expected if the policy goes away. - EXPECT_CALL( - network_config_handler_, - SetPolicy( - CurrentONCSource(), _, IsEqualTo(empty_network_configs_.get()))); + EXPECT_CALL(network_config_handler_, + SetPolicy(CurrentONCSource(), _, IsEmpty(), IsEmpty())); EXPECT_CALL(*certificate_importer_, - ImportCertificates( - IsEqualTo(empty_certificates_.get()), CurrentONCSource(), _)); + ImportCertificates(IsEmpty(), CurrentONCSource(), _)); policy.Erase(GetParam()); UpdateProviderPolicy(policy); diff --git a/chrome/browser/chromeos/policy/power_policy_browsertest.cc b/chrome/browser/chromeos/policy/power_policy_browsertest.cc index b00064f82b..28365d35a7 100644 --- a/chrome/browser/chromeos/policy/power_policy_browsertest.cc +++ b/chrome/browser/chromeos/policy/power_policy_browsertest.cc @@ -44,9 +44,9 @@ #include "chromeos/chromeos_paths.h" #include "chromeos/chromeos_switches.h" #include "chromeos/dbus/cryptohome_client.h" +#include "chromeos/dbus/fake_dbus_thread_manager.h" #include "chromeos/dbus/fake_power_manager_client.h" #include "chromeos/dbus/fake_session_manager_client.h" -#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" #include "chromeos/dbus/power_manager/policy.pb.h" #include "chromeos/dbus/power_policy_controller.h" #include "content/public/browser/notification_details.h" @@ -158,7 +158,7 @@ void PowerPolicyBrowserTestBase::SetUpInProcessBrowserTestFixture() { MarkAsEnterpriseOwned(); power_manager_client_ = - mock_dbus_thread_manager()->fake_power_manager_client(); + fake_dbus_thread_manager()->fake_power_manager_client(); } void PowerPolicyBrowserTestBase::SetUpOnMainThread() { diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc index c11889b906..9c2f8090ad 100644 --- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc +++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc @@ -20,7 +20,7 @@ #include "chrome/browser/policy/cloud/resource_cache.h" #include "chrome/browser/policy/policy_bundle.h" #include "chrome/browser/policy/policy_domain_descriptor.h" -#include "chrome/common/pref_names.h" +#include "components/policy/core/common/policy_pref_names.h" #include "content/public/browser/browser_thread.h" #include "net/url_request/url_request_context_getter.h" @@ -351,7 +351,8 @@ void UserCloudPolicyManagerChromeOS::StartRefreshSchedulerIfReady() { } core()->StartRefreshScheduler(); - core()->TrackRefreshDelayPref(local_state_, prefs::kUserPolicyRefreshRate); + core()->TrackRefreshDelayPref(local_state_, + policy_prefs::kUserPolicyRefreshRate); } } // namespace policy diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.cc b/chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.cc index 4cb9987be3..a8fdf59e03 100644 --- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.cc +++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.cc @@ -78,8 +78,10 @@ UserCloudPolicyManagerChromeOS* scoped_ptr<UserCloudPolicyManagerChromeOS> UserCloudPolicyManagerFactoryChromeOS::CreateForProfile( Profile* profile, - bool force_immediate_load) { - return GetInstance()->CreateManagerForProfile(profile, force_immediate_load); + bool force_immediate_load, + scoped_refptr<base::SequencedTaskRunner> background_task_runner) { + return GetInstance()->CreateManagerForProfile( + profile, force_immediate_load, background_task_runner); } UserCloudPolicyManagerFactoryChromeOS::UserCloudPolicyManagerFactoryChromeOS() @@ -102,7 +104,8 @@ UserCloudPolicyManagerChromeOS* scoped_ptr<UserCloudPolicyManagerChromeOS> UserCloudPolicyManagerFactoryChromeOS::CreateManagerForProfile( Profile* profile, - bool force_immediate_load) { + bool force_immediate_load, + scoped_refptr<base::SequencedTaskRunner> background_task_runner) { const CommandLine* command_line = CommandLine::ForCurrentProcess(); // Don't initialize cloud policy for the signin profile. if (chromeos::ProfileHelper::IsSigninProfile(profile)) @@ -156,6 +159,7 @@ scoped_ptr<UserCloudPolicyManagerChromeOS> new UserCloudPolicyStoreChromeOS( chromeos::DBusThreadManager::Get()->GetCryptohomeClient(), chromeos::DBusThreadManager::Get()->GetSessionManagerClient(), + background_task_runner, username, policy_key_dir, token_cache_file, policy_cache_file)); scoped_refptr<base::SequencedTaskRunner> backend_task_runner = diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.h b/chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.h index 471bd61bc8..d746067891 100644 --- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.h +++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.h @@ -8,11 +8,16 @@ #include <map> #include "base/basictypes.h" +#include "base/memory/ref_counted.h" #include "base/memory/singleton.h" #include "components/browser_context_keyed_service/browser_context_keyed_base_factory.h" class Profile; +namespace base { +class SequencedTaskRunner; +} + namespace content { class BrowserContext; } @@ -53,7 +58,8 @@ class UserCloudPolicyManagerFactoryChromeOS // UserCloudPolicyStore at startup. static scoped_ptr<UserCloudPolicyManagerChromeOS> CreateForProfile( Profile* profile, - bool force_immediate_load); + bool force_immediate_load, + scoped_refptr<base::SequencedTaskRunner> background_task_runner); private: friend struct DefaultSingletonTraits<UserCloudPolicyManagerFactoryChromeOS>; @@ -65,7 +71,8 @@ class UserCloudPolicyManagerFactoryChromeOS UserCloudPolicyManagerChromeOS* GetManagerForProfile(Profile* profile); scoped_ptr<UserCloudPolicyManagerChromeOS> CreateManagerForProfile( Profile* profile, - bool force_immediate_load); + bool force_immediate_load, + scoped_refptr<base::SequencedTaskRunner> background_task_runner); // BrowserContextKeyedBaseFactory: virtual void BrowserContextShutdown( diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.cc b/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.cc index cce3d66103..323baecca4 100644 --- a/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.cc +++ b/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.cc @@ -8,9 +8,10 @@ #include "base/bind_helpers.h" #include "base/callback.h" #include "base/file_util.h" +#include "base/location.h" #include "base/logging.h" -#include "base/memory/ref_counted.h" #include "base/metrics/histogram.h" +#include "base/sequenced_task_runner.h" #include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "chrome/browser/chromeos/policy/user_policy_disk_cache.h" @@ -18,7 +19,6 @@ #include "chrome/browser/policy/proto/cloud/device_management_local.pb.h" #include "chromeos/dbus/cryptohome_client.h" #include "chromeos/dbus/session_manager_client.h" -#include "content/public/browser/browser_thread.h" #include "google_apis/gaia/gaia_auth_util.h" #include "policy/proto/cloud_policy.pb.h" @@ -59,8 +59,10 @@ class LegacyPolicyCacheLoader : public UserPolicyTokenLoader::Delegate, CloudPolicyStore::Status, scoped_ptr<em::PolicyFetchResponse>)> Callback; - LegacyPolicyCacheLoader(const base::FilePath& token_cache_file, - const base::FilePath& policy_cache_file); + LegacyPolicyCacheLoader( + const base::FilePath& token_cache_file, + const base::FilePath& policy_cache_file, + scoped_refptr<base::SequencedTaskRunner> background_task_runner); virtual ~LegacyPolicyCacheLoader(); // Starts loading, and reports the result to |callback| when done. @@ -102,14 +104,17 @@ class LegacyPolicyCacheLoader : public UserPolicyTokenLoader::Delegate, LegacyPolicyCacheLoader::LegacyPolicyCacheLoader( const base::FilePath& token_cache_file, - const base::FilePath& policy_cache_file) + const base::FilePath& policy_cache_file, + scoped_refptr<base::SequencedTaskRunner> background_task_runner) : weak_factory_(this), has_policy_(false), status_(CloudPolicyStore::STATUS_OK) { token_loader_ = new UserPolicyTokenLoader(weak_factory_.GetWeakPtr(), - token_cache_file); + token_cache_file, + background_task_runner); policy_cache_ = new UserPolicyDiskCache(weak_factory_.GetWeakPtr(), - policy_cache_file); + policy_cache_file, + background_task_runner); } LegacyPolicyCacheLoader::~LegacyPolicyCacheLoader() {} @@ -165,18 +170,21 @@ CloudPolicyStore::Status LegacyPolicyCacheLoader::TranslateLoadResult( UserCloudPolicyStoreChromeOS::UserCloudPolicyStoreChromeOS( chromeos::CryptohomeClient* cryptohome_client, chromeos::SessionManagerClient* session_manager_client, + scoped_refptr<base::SequencedTaskRunner> background_task_runner, const std::string& username, const base::FilePath& user_policy_key_dir, const base::FilePath& legacy_token_cache_file, const base::FilePath& legacy_policy_cache_file) - : cryptohome_client_(cryptohome_client), + : UserCloudPolicyStoreBase(background_task_runner), + cryptohome_client_(cryptohome_client), session_manager_client_(session_manager_client), username_(username), user_policy_key_dir_(user_policy_key_dir), weak_factory_(this), legacy_cache_dir_(legacy_token_cache_file.DirName()), legacy_loader_(new LegacyPolicyCacheLoader(legacy_token_cache_file, - legacy_policy_cache_file)), + legacy_policy_cache_file, + background_task_runner)), legacy_caches_loaded_(false), policy_key_loaded_(false) {} @@ -388,7 +396,7 @@ void UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated( // Policy has been loaded successfully. This indicates that new-style policy // is working, so the legacy cache directory can be removed. if (!legacy_cache_dir_.empty()) { - content::BrowserThread::PostBlockingPoolTask( + background_task_runner()->PostTask( FROM_HERE, base::Bind(&UserCloudPolicyStoreChromeOS::RemoveLegacyCacheDir, legacy_cache_dir_)); @@ -468,7 +476,7 @@ void UserCloudPolicyStoreChromeOS::RemoveLegacyCacheDir( void UserCloudPolicyStoreChromeOS::ReloadPolicyKey( const base::Closure& callback) { std::vector<uint8>* key = new std::vector<uint8>(); - content::BrowserThread::PostBlockingPoolTaskAndReply( + background_task_runner()->PostTaskAndReply( FROM_HERE, base::Bind(&UserCloudPolicyStoreChromeOS::LoadPolicyKey, policy_key_path_, diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.h b/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.h index 913235d456..e1939f8799 100644 --- a/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.h +++ b/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.h @@ -11,12 +11,17 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/files/file_path.h" +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "chrome/browser/policy/cloud/cloud_policy_validator.h" #include "chrome/browser/policy/cloud/user_cloud_policy_store_base.h" #include "chromeos/dbus/dbus_method_call_status.h" +namespace base { +class SequencedTaskRunner; +} + namespace chromeos { class CryptohomeClient; class SessionManagerClient; @@ -38,6 +43,7 @@ class UserCloudPolicyStoreChromeOS : public UserCloudPolicyStoreBase { UserCloudPolicyStoreChromeOS( chromeos::CryptohomeClient* cryptohome_client, chromeos::SessionManagerClient* session_manager_client, + scoped_refptr<base::SequencedTaskRunner> background_task_runner, const std::string& username, const base::FilePath& user_policy_key_dir, const base::FilePath& legacy_token_cache_file, diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos_unittest.cc b/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos_unittest.cc index 994d52ff16..0f5ab74564 100644 --- a/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos_unittest.cc +++ b/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos_unittest.cc @@ -19,7 +19,6 @@ #include "chrome/browser/policy/proto/cloud/device_management_local.pb.h" #include "chromeos/dbus/mock_cryptohome_client.h" #include "chromeos/dbus/mock_session_manager_client.h" -#include "content/public/test/test_browser_thread.h" #include "policy/policy_constants.h" #include "policy/proto/cloud_policy.pb.h" #include "testing/gmock/include/gmock/gmock.h" @@ -53,9 +52,7 @@ ACTION_P2(SendSanitizedUsername, call_status, sanitized_username) { class UserCloudPolicyStoreChromeOSTest : public testing::Test { protected: UserCloudPolicyStoreChromeOSTest() - : loop_(base::MessageLoop::TYPE_UI), - ui_thread_(content::BrowserThread::UI, &loop_), - file_thread_(content::BrowserThread::FILE, &loop_) {} + : loop_(base::MessageLoop::TYPE_UI) {} virtual void SetUp() OVERRIDE { EXPECT_CALL(cryptohome_client_, @@ -68,6 +65,7 @@ class UserCloudPolicyStoreChromeOSTest : public testing::Test { ASSERT_TRUE(tmp_dir_.CreateUniqueTempDir()); store_.reset(new UserCloudPolicyStoreChromeOS(&cryptohome_client_, &session_manager_client_, + loop_.message_loop_proxy(), PolicyBuilder::kFakeUsername, user_policy_dir(), token_file(), @@ -202,7 +200,6 @@ class UserCloudPolicyStoreChromeOSTest : public testing::Test { void RunUntilIdle() { loop_.RunUntilIdle(); - content::BrowserThread::GetBlockingPool()->FlushForTesting(); loop_.RunUntilIdle(); } @@ -231,8 +228,6 @@ class UserCloudPolicyStoreChromeOSTest : public testing::Test { scoped_ptr<UserCloudPolicyStoreChromeOS> store_; private: - content::TestBrowserThread ui_thread_; - content::TestBrowserThread file_thread_; base::ScopedTempDir tmp_dir_; DISALLOW_COPY_AND_ASSIGN(UserCloudPolicyStoreChromeOSTest); diff --git a/chrome/browser/chromeos/policy/user_network_configuration_updater.cc b/chrome/browser/chromeos/policy/user_network_configuration_updater.cc index 990f5bd743..49c2552ded 100644 --- a/chrome/browser/chromeos/policy/user_network_configuration_updater.cc +++ b/chrome/browser/chromeos/policy/user_network_configuration_updater.cc @@ -76,12 +76,15 @@ void UserNetworkConfigurationUpdater::ImportCertificates( } void UserNetworkConfigurationUpdater::ApplyNetworkPolicy( - base::ListValue* network_configs_onc) { + base::ListValue* network_configs_onc, + base::DictionaryValue* global_network_config) { DCHECK(user_); chromeos::onc::ExpandStringPlaceholdersInNetworksForUser(user_, network_configs_onc); - network_config_handler_->SetPolicy( - onc_source_, user_->username_hash(), *network_configs_onc); + network_config_handler_->SetPolicy(onc_source_, + user_->username_hash(), + *network_configs_onc, + *global_network_config); } void UserNetworkConfigurationUpdater::SetTrustAnchors() { diff --git a/chrome/browser/chromeos/policy/user_network_configuration_updater.h b/chrome/browser/chromeos/policy/user_network_configuration_updater.h index 78d9ef11e6..aca6b89f29 100644 --- a/chrome/browser/chromeos/policy/user_network_configuration_updater.h +++ b/chrome/browser/chromeos/policy/user_network_configuration_updater.h @@ -73,7 +73,8 @@ class UserNetworkConfigurationUpdater : public NetworkConfigurationUpdater { const base::ListValue& certificates_onc) OVERRIDE; virtual void ApplyNetworkPolicy( - base::ListValue* network_configs_onc) OVERRIDE; + base::ListValue* network_configs_onc, + base::DictionaryValue* global_network_config) OVERRIDE; // Push |web_trust_certs_| to |cert_verifier_| if necessary. void SetTrustAnchors(); diff --git a/chrome/browser/chromeos/policy/user_policy_disk_cache.cc b/chrome/browser/chromeos/policy/user_policy_disk_cache.cc index e9249cd3f3..d22cddf3e2 100644 --- a/chrome/browser/chromeos/policy/user_policy_disk_cache.cc +++ b/chrome/browser/chromeos/policy/user_policy_disk_cache.cc @@ -7,63 +7,47 @@ #include "base/bind.h" #include "base/file_util.h" #include "base/logging.h" +#include "base/message_loop/message_loop_proxy.h" #include "base/metrics/histogram.h" +#include "base/sequenced_task_runner.h" #include "chrome/browser/policy/cloud/enterprise_metrics.h" #include "chrome/browser/policy/proto/cloud/device_management_local.pb.h" #include "content/public/browser/browser_thread.h" -using content::BrowserThread; - namespace em = enterprise_management; -namespace { - -// Other places can sample on the same UMA counter, so make sure they all do -// it on the same thread (UI). -void SampleUMAOnUIThread(policy::MetricPolicy sample) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - UMA_HISTOGRAM_ENUMERATION(policy::kMetricPolicy, sample, - policy::kMetricPolicySize); -} - -void SampleUMA(policy::MetricPolicy sample) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&SampleUMAOnUIThread, sample)); -} - -} // namespace - namespace policy { UserPolicyDiskCache::Delegate::~Delegate() {} UserPolicyDiskCache::UserPolicyDiskCache( const base::WeakPtr<Delegate>& delegate, - const base::FilePath& backing_file_path) + const base::FilePath& backing_file_path, + scoped_refptr<base::SequencedTaskRunner> background_task_runner) : delegate_(delegate), - backing_file_path_(backing_file_path) {} + backing_file_path_(backing_file_path), + origin_task_runner_(base::MessageLoopProxy::current()), + background_task_runner_(background_task_runner) {} void UserPolicyDiskCache::Load() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - bool ret = BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, - base::Bind(&UserPolicyDiskCache::LoadOnFileThread, this)); + DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); + bool ret = background_task_runner_->PostTask( + FROM_HERE, base::Bind(&UserPolicyDiskCache::LoadOnFileThread, this)); DCHECK(ret); } void UserPolicyDiskCache::Store( const em::CachedCloudPolicyResponse& policy) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, + DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); + background_task_runner_->PostTask( + FROM_HERE, base::Bind(&UserPolicyDiskCache::StoreOnFileThread, this, policy)); } UserPolicyDiskCache::~UserPolicyDiskCache() {} void UserPolicyDiskCache::LoadOnFileThread() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + DCHECK(background_task_runner_->RunsTasksOnCurrentThread()); em::CachedCloudPolicyResponse cached_response; if (!base::PathExists(backing_file_path_)) { @@ -94,26 +78,31 @@ void UserPolicyDiskCache::LoadOnFileThread() { void UserPolicyDiskCache::LoadDone( LoadResult result, const em::CachedCloudPolicyResponse& policy) { - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&UserPolicyDiskCache::ReportResultOnUIThread, this, - result, policy)); + origin_task_runner_->PostTask( + FROM_HERE, + base::Bind( + &UserPolicyDiskCache::ReportResultOnUIThread, this, result, policy)); } void UserPolicyDiskCache::ReportResultOnUIThread( LoadResult result, const em::CachedCloudPolicyResponse& policy) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); switch (result) { case LOAD_RESULT_NOT_FOUND: break; case LOAD_RESULT_READ_ERROR: case LOAD_RESULT_PARSE_ERROR: - SampleUMAOnUIThread(kMetricPolicyLoadFailed); + UMA_HISTOGRAM_ENUMERATION(policy::kMetricPolicy, + kMetricPolicyLoadFailed, + policy::kMetricPolicySize); break; case LOAD_RESULT_SUCCESS: - SampleUMAOnUIThread(kMetricPolicyLoadSucceeded); + UMA_HISTOGRAM_ENUMERATION(policy::kMetricPolicy, + kMetricPolicyLoadSucceeded, + policy::kMetricPolicySize); + break; } if (delegate_.get()) @@ -122,28 +111,36 @@ void UserPolicyDiskCache::ReportResultOnUIThread( void UserPolicyDiskCache::StoreOnFileThread( const em::CachedCloudPolicyResponse& policy) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + DCHECK(background_task_runner_->RunsTasksOnCurrentThread()); std::string data; if (!policy.SerializeToString(&data)) { LOG(WARNING) << "Failed to serialize policy data"; - SampleUMA(kMetricPolicyStoreFailed); + UMA_HISTOGRAM_ENUMERATION(policy::kMetricPolicy, + kMetricPolicyStoreFailed, + policy::kMetricPolicySize); return; } if (!file_util::CreateDirectory(backing_file_path_.DirName())) { LOG(WARNING) << "Failed to create directory " << backing_file_path_.DirName().value(); - SampleUMA(kMetricPolicyStoreFailed); + UMA_HISTOGRAM_ENUMERATION(policy::kMetricPolicy, + kMetricPolicyStoreFailed, + policy::kMetricPolicySize); return; } int size = data.size(); if (file_util::WriteFile(backing_file_path_, data.c_str(), size) != size) { LOG(WARNING) << "Failed to write " << backing_file_path_.value(); - SampleUMA(kMetricPolicyStoreFailed); + UMA_HISTOGRAM_ENUMERATION(policy::kMetricPolicy, + kMetricPolicyStoreFailed, + policy::kMetricPolicySize); return; } - SampleUMA(kMetricPolicyStoreSucceeded); + UMA_HISTOGRAM_ENUMERATION(policy::kMetricPolicy, + kMetricPolicyStoreSucceeded, + policy::kMetricPolicySize); } } // namespace policy diff --git a/chrome/browser/chromeos/policy/user_policy_disk_cache.h b/chrome/browser/chromeos/policy/user_policy_disk_cache.h index 2c41688664..b22ad90a46 100644 --- a/chrome/browser/chromeos/policy/user_policy_disk_cache.h +++ b/chrome/browser/chromeos/policy/user_policy_disk_cache.h @@ -10,6 +10,10 @@ #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" +namespace base { +class SequencedTaskRunner; +} + namespace enterprise_management { class CachedCloudPolicyResponse; } @@ -43,8 +47,10 @@ class UserPolicyDiskCache const enterprise_management::CachedCloudPolicyResponse& policy) = 0; }; - UserPolicyDiskCache(const base::WeakPtr<Delegate>& delegate, - const base::FilePath& backing_file_path); + UserPolicyDiskCache( + const base::WeakPtr<Delegate>& delegate, + const base::FilePath& backing_file_path, + scoped_refptr<base::SequencedTaskRunner> background_task_runner); // Starts reading the policy cache from disk. Passes the read policy // information back to the hosting UserPolicyCache after a successful cache @@ -76,6 +82,8 @@ class UserPolicyDiskCache base::WeakPtr<Delegate> delegate_; const base::FilePath backing_file_path_; + scoped_refptr<base::SequencedTaskRunner> origin_task_runner_; + scoped_refptr<base::SequencedTaskRunner> background_task_runner_; DISALLOW_COPY_AND_ASSIGN(UserPolicyDiskCache); }; diff --git a/chrome/browser/chromeos/policy/user_policy_token_loader.cc b/chrome/browser/chromeos/policy/user_policy_token_loader.cc index 1501e6cb77..0df0966803 100644 --- a/chrome/browser/chromeos/policy/user_policy_token_loader.cc +++ b/chrome/browser/chromeos/policy/user_policy_token_loader.cc @@ -6,30 +6,12 @@ #include "base/bind.h" #include "base/file_util.h" +#include "base/location.h" +#include "base/message_loop/message_loop_proxy.h" #include "base/metrics/histogram.h" +#include "base/sequenced_task_runner.h" #include "chrome/browser/policy/cloud/enterprise_metrics.h" #include "chrome/browser/policy/proto/cloud/device_management_local.pb.h" -#include "content/public/browser/browser_thread.h" - -using content::BrowserThread; - -namespace { - -// Other places can sample on the same UMA counter, so make sure they all do -// it on the same thread (UI). -void SampleUMAOnUIThread(policy::MetricToken sample) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - UMA_HISTOGRAM_ENUMERATION(policy::kMetricToken, sample, - policy::kMetricTokenSize); -} - -void SampleUMA(policy::MetricToken sample) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&SampleUMAOnUIThread, sample)); -} - -} // namespace namespace policy { @@ -39,22 +21,25 @@ UserPolicyTokenLoader::Delegate::~Delegate() {} UserPolicyTokenLoader::UserPolicyTokenLoader( const base::WeakPtr<Delegate>& delegate, - const base::FilePath& cache_file) + const base::FilePath& cache_file, + scoped_refptr<base::SequencedTaskRunner> background_task_runner) : delegate_(delegate), - cache_file_(cache_file) {} + cache_file_(cache_file), + origin_task_runner_(base::MessageLoopProxy::current()), + background_task_runner_(background_task_runner) {} void UserPolicyTokenLoader::Load() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, - base::Bind(&UserPolicyTokenLoader::LoadOnFileThread, this)); + DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); + background_task_runner_->PostTask( + FROM_HERE, + base::Bind(&UserPolicyTokenLoader::LoadOnBackgroundThread, this)); } UserPolicyTokenLoader::~UserPolicyTokenLoader() { } -void UserPolicyTokenLoader::LoadOnFileThread() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); +void UserPolicyTokenLoader::LoadOnBackgroundThread() { + DCHECK(background_task_runner_->RunsTasksOnCurrentThread()); std::string device_token; std::string device_id; @@ -65,23 +50,27 @@ void UserPolicyTokenLoader::LoadOnFileThread() { device_credentials.ParseFromArray(data.c_str(), data.size())) { device_token = device_credentials.device_token(); device_id = device_credentials.device_id(); - SampleUMA(kMetricTokenLoadSucceeded); + UMA_HISTOGRAM_ENUMERATION(policy::kMetricToken, + kMetricTokenLoadSucceeded, + policy::kMetricTokenSize); } else { - SampleUMA(kMetricTokenLoadFailed); + UMA_HISTOGRAM_ENUMERATION(policy::kMetricToken, + kMetricTokenLoadFailed, + policy::kMetricTokenSize); } } - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&UserPolicyTokenLoader::NotifyOnUIThread, + origin_task_runner_->PostTask( + FROM_HERE, + base::Bind(&UserPolicyTokenLoader::NotifyOnOriginThread, this, device_token, device_id)); } -void UserPolicyTokenLoader::NotifyOnUIThread(const std::string& token, - const std::string& device_id) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); +void UserPolicyTokenLoader::NotifyOnOriginThread(const std::string& token, + const std::string& device_id) { + DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); if (delegate_.get()) delegate_->OnTokenLoaded(token, device_id); } diff --git a/chrome/browser/chromeos/policy/user_policy_token_loader.h b/chrome/browser/chromeos/policy/user_policy_token_loader.h index f213b00660..919e4aef43 100644 --- a/chrome/browser/chromeos/policy/user_policy_token_loader.h +++ b/chrome/browser/chromeos/policy/user_policy_token_loader.h @@ -12,6 +12,10 @@ #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" +namespace base { +class SequencedTaskRunner; +} + namespace policy { // Handles disk access and threading details for loading and storing tokens. @@ -29,8 +33,10 @@ class UserPolicyTokenLoader const std::string& device_id) = 0; }; - UserPolicyTokenLoader(const base::WeakPtr<Delegate>& delegate, - const base::FilePath& cache_file); + UserPolicyTokenLoader( + const base::WeakPtr<Delegate>& delegate, + const base::FilePath& cache_file, + scoped_refptr<base::SequencedTaskRunner> background_task_runner); // Starts loading the disk cache. After the load is finished, the result is // reported through the delegate. @@ -40,12 +46,14 @@ class UserPolicyTokenLoader friend class base::RefCountedThreadSafe<UserPolicyTokenLoader>; ~UserPolicyTokenLoader(); - void LoadOnFileThread(); - void NotifyOnUIThread(const std::string& token, + void LoadOnBackgroundThread(); + void NotifyOnOriginThread(const std::string& token, const std::string& device_id); const base::WeakPtr<Delegate> delegate_; const base::FilePath cache_file_; + scoped_refptr<base::SequencedTaskRunner> origin_task_runner_; + scoped_refptr<base::SequencedTaskRunner> background_task_runner_; DISALLOW_COPY_AND_ASSIGN(UserPolicyTokenLoader); }; diff --git a/chrome/browser/chromeos/power/peripheral_battery_observer_browsertest.cc b/chrome/browser/chromeos/power/peripheral_battery_observer_browsertest.cc index 4e8a0a2e44..d29045a313 100644 --- a/chrome/browser/chromeos/power/peripheral_battery_observer_browsertest.cc +++ b/chrome/browser/chromeos/power/peripheral_battery_observer_browsertest.cc @@ -9,7 +9,7 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/notifications/notification_ui_manager.h" #include "chrome/test/base/in_process_browser_test.h" -#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" +#include "chromeos/dbus/fake_dbus_thread_manager.h" #include "content/public/browser/browser_thread.h" #include "content/public/test/test_browser_thread.h" #include "content/public/test/test_utils.h" @@ -37,9 +37,9 @@ class PeripheralBatteryObserverTest : public InProcessBrowserTest { virtual ~PeripheralBatteryObserverTest () {} virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { - MockDBusThreadManagerWithoutGMock* mock_dbus_thread_manager = - new MockDBusThreadManagerWithoutGMock; - DBusThreadManager::InitializeForTesting(mock_dbus_thread_manager); + FakeDBusThreadManager* fake_dbus_thread_manager = + new FakeDBusThreadManager; + DBusThreadManager::InitializeForTesting(fake_dbus_thread_manager); InProcessBrowserTest::SetUpInProcessBrowserTestFixture(); } diff --git a/chrome/browser/chromeos/power/power_prefs_unittest.cc b/chrome/browser/chromeos/power/power_prefs_unittest.cc index 7a28e040f3..3c040e5183 100644 --- a/chrome/browser/chromeos/power/power_prefs_unittest.cc +++ b/chrome/browser/chromeos/power/power_prefs_unittest.cc @@ -23,8 +23,8 @@ #include "chrome/test/base/testing_profile.h" #include "chrome/test/base/testing_profile_manager.h" #include "chromeos/chromeos_switches.h" +#include "chromeos/dbus/fake_dbus_thread_manager.h" #include "chromeos/dbus/fake_power_manager_client.h" -#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" #include "chromeos/dbus/power_manager/policy.pb.h" #include "chromeos/dbus/power_policy_controller.h" #include "components/user_prefs/pref_registry_syncable.h" @@ -51,7 +51,7 @@ class PowerPrefsTest : public testing::Test { bool GetCurrentAllowScreenWakeLocks() const; TestingProfileManager profile_manager_; - MockDBusThreadManagerWithoutGMock mock_dbus_thread_manager_; + FakeDBusThreadManager fake_dbus_thread_manager_; PowerPolicyController* power_policy_controller_; // Not owned. FakePowerManagerClient* fake_power_manager_client_; // Not owned. @@ -63,9 +63,9 @@ class PowerPrefsTest : public testing::Test { PowerPrefsTest::PowerPrefsTest() : profile_manager_(TestingBrowserProcess::GetGlobal()), power_policy_controller_( - mock_dbus_thread_manager_.GetPowerPolicyController()), + fake_dbus_thread_manager_.GetPowerPolicyController()), fake_power_manager_client_( - mock_dbus_thread_manager_.fake_power_manager_client()) { + fake_dbus_thread_manager_.fake_power_manager_client()) { } void PowerPrefsTest::SetUp() { diff --git a/chrome/browser/chromeos/preferences.cc b/chrome/browser/chromeos/preferences.cc index 5271c72bd3..b3e6262a66 100644 --- a/chrome/browser/chromeos/preferences.cc +++ b/chrome/browser/chromeos/preferences.cc @@ -4,6 +4,7 @@ #include "chrome/browser/chromeos/preferences.h" +#include "ash/autoclick/autoclick_controller.h" #include "ash/magnifier/magnifier_constants.h" #include "ash/shell.h" #include "base/command_line.h" @@ -162,6 +163,10 @@ void Preferences::RegisterProfilePrefs( prefs::kAutoclickEnabled, false, user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); + registry->RegisterIntegerPref( + prefs::kAutoclickDelayMs, + ash::AutoclickController::kDefaultAutoclickDelayMs, + user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); registry->RegisterBooleanPref( prefs::kShouldAlwaysShowAccessibilityMenu, false, diff --git a/chrome/browser/chromeos/settings/device_oauth2_token_service.cc b/chrome/browser/chromeos/settings/device_oauth2_token_service.cc index 6d5d917576..64f2c84000 100644 --- a/chrome/browser/chromeos/settings/device_oauth2_token_service.cc +++ b/chrome/browser/chromeos/settings/device_oauth2_token_service.cc @@ -16,7 +16,7 @@ #include "chrome/browser/policy/browser_policy_connector.h" #include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h" #include "chrome/common/pref_names.h" -#include "chromeos/cryptohome/cryptohome_library.h" +#include "chromeos/cryptohome/system_salt_getter.h" #include "content/public/browser/browser_thread.h" #include "google_apis/gaia/gaia_urls.h" #include "google_apis/gaia/google_service_auth_error.h" @@ -238,7 +238,7 @@ void DeviceOAuth2TokenService::SetAndSaveRefreshToken( DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); // TODO(xiyuan): Use async GetSystemSalt after merging to M31. - const std::string system_salt = CryptohomeLibrary::Get()->GetSystemSaltSync(); + const std::string system_salt = SystemSaltGetter::Get()->GetSystemSaltSync(); if (system_salt.empty()) { const int64 kRequestSystemSaltDelayMs = 500; content::BrowserThread::PostDelayedTask( @@ -266,7 +266,7 @@ std::string DeviceOAuth2TokenService::GetRefreshToken( local_state_->GetString(prefs::kDeviceRobotAnyApiRefreshToken); // TODO(xiyuan): This needs a proper fix after M31. - LOG_IF(ERROR, CryptohomeLibrary::Get()->GetSystemSaltSync().empty()) + LOG_IF(ERROR, SystemSaltGetter::Get()->GetSystemSaltSync().empty()) << "System salt is not available for decryption"; refresh_token_ = token_encryptor_->DecryptWithSystemSalt( diff --git a/chrome/browser/chromeos/settings/device_oauth2_token_service_unittest.cc b/chrome/browser/chromeos/settings/device_oauth2_token_service_unittest.cc index 988f6061c2..dd4b246f7a 100644 --- a/chrome/browser/chromeos/settings/device_oauth2_token_service_unittest.cc +++ b/chrome/browser/chromeos/settings/device_oauth2_token_service_unittest.cc @@ -11,8 +11,8 @@ #include "chrome/common/pref_names.h" #include "chrome/test/base/scoped_testing_local_state.h" #include "chrome/test/base/testing_browser_process.h" -#include "chromeos/cryptohome/cryptohome_library.h" -#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" +#include "chromeos/cryptohome/system_salt_getter.h" +#include "chromeos/dbus/fake_dbus_thread_manager.h" #include "content/public/browser/browser_thread.h" #include "content/public/test/test_browser_thread.h" #include "google_apis/gaia/gaia_oauth_client.h" @@ -101,14 +101,14 @@ class DeviceOAuth2TokenServiceTest : public testing::Test { virtual void SetUp() OVERRIDE { // TODO(xiyuan): Remove this when cleaning up the system salt load temp fix. - scoped_ptr<MockDBusThreadManagerWithoutGMock> mock_dbus_thread_manager( - new MockDBusThreadManagerWithoutGMock); - DBusThreadManager::InitializeForTesting(mock_dbus_thread_manager.release()); - CryptohomeLibrary::Initialize(); + scoped_ptr<FakeDBusThreadManager> fake_dbus_thread_manager( + new FakeDBusThreadManager); + DBusThreadManager::InitializeForTesting(fake_dbus_thread_manager.release()); + SystemSaltGetter::Initialize(); } virtual void TearDown() OVERRIDE { - CryptohomeLibrary::Shutdown(); + SystemSaltGetter::Shutdown(); DBusThreadManager::Shutdown(); base::RunLoop().RunUntilIdle(); } diff --git a/chrome/browser/chromeos/settings/device_settings_provider.cc b/chrome/browser/chromeos/settings/device_settings_provider.cc index a3366ae52a..7c1d3b894e 100644 --- a/chrome/browser/chromeos/settings/device_settings_provider.cc +++ b/chrome/browser/chromeos/settings/device_settings_provider.cc @@ -82,9 +82,6 @@ const char* kKnownSettings[] = { kVariationsRestrictParameter, }; -// Legacy policy file location. Used to detect migration from pre v12 ChromeOS. -const char kLegacyPolicyFile[] = "/var/lib/whitelist/preferences"; - bool HasOldMetricsFile() { // TODO(pastarmovj): Remove this once migration is not needed anymore. // If the value is not set we should try to migrate legacy consent file. diff --git a/chrome/browser/chromeos/settings/device_settings_test_helper.cc b/chrome/browser/chromeos/settings/device_settings_test_helper.cc index 04a77ea81c..2050364b72 100644 --- a/chrome/browser/chromeos/settings/device_settings_test_helper.cc +++ b/chrome/browser/chromeos/settings/device_settings_test_helper.cc @@ -63,6 +63,7 @@ void DeviceSettingsTestHelper::FlushRetrieve() { for (device_local_account_state = device_local_account_policy_.begin(); device_local_account_state != device_local_account_policy_.end(); ++device_local_account_state) { + std::vector<RetrievePolicyCallback> callbacks; callbacks.swap(device_local_account_state->second.retrieve_callbacks_); for (std::vector<RetrievePolicyCallback>::iterator cb(callbacks.begin()); cb != callbacks.end(); ++cb) { diff --git a/chrome/browser/chromeos/settings/session_manager_operation.cc b/chrome/browser/chromeos/settings/session_manager_operation.cc index b31608d501..934c7a2837 100644 --- a/chrome/browser/chromeos/settings/session_manager_operation.cc +++ b/chrome/browser/chromeos/settings/session_manager_operation.cc @@ -143,8 +143,16 @@ void SessionManagerOperation::ValidateDeviceSettings( return; } + base::SequencedWorkerPool* pool = + content::BrowserThread::GetBlockingPool(); + scoped_refptr<base::SequencedTaskRunner> background_task_runner = + pool->GetSequencedTaskRunnerWithShutdownBehavior( + pool->GetSequenceToken(), + base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); + policy::DeviceCloudPolicyValidator* validator = - policy::DeviceCloudPolicyValidator::Create(policy.Pass()); + policy::DeviceCloudPolicyValidator::Create(policy.Pass(), + background_task_runner); // Policy auto-generated by session manager doesn't include a timestamp, so we // need to allow missing timestamps. diff --git a/chrome/browser/chromeos/settings/session_manager_operation_unittest.cc b/chrome/browser/chromeos/settings/session_manager_operation_unittest.cc index 98d2f4e6e2..1023aa03d7 100644 --- a/chrome/browser/chromeos/settings/session_manager_operation_unittest.cc +++ b/chrome/browser/chromeos/settings/session_manager_operation_unittest.cc @@ -252,7 +252,8 @@ TEST_F(SessionManagerOperationTest, SignAndStoreSettings) { policy_response->ParseFromString( device_settings_test_helper_.policy_blob())); policy::DeviceCloudPolicyValidator* validator = - policy::DeviceCloudPolicyValidator::Create(policy_response.Pass()); + policy::DeviceCloudPolicyValidator::Create( + policy_response.Pass(), message_loop_.message_loop_proxy()); validator->ValidateUsername(policy_.policy_data().username()); validator->ValidateTimestamp( before, diff --git a/chrome/browser/chromeos/settings/token_encryptor.cc b/chrome/browser/chromeos/settings/token_encryptor.cc index d966091600..269e8cb85a 100644 --- a/chrome/browser/chromeos/settings/token_encryptor.cc +++ b/chrome/browser/chromeos/settings/token_encryptor.cc @@ -10,7 +10,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/sys_info.h" -#include "chromeos/cryptohome/cryptohome_library.h" +#include "chromeos/cryptohome/system_salt_getter.h" #include "crypto/encryptor.h" #include "crypto/nss_util.h" #include "crypto/sha2.h" @@ -63,7 +63,7 @@ std::string CryptohomeTokenEncryptor::DecryptWithSystemSalt( bool CryptohomeTokenEncryptor::LoadSystemSaltKey() { // Assume the system salt should be obtained beforehand at login time. if (system_salt_.empty()) - system_salt_ = CryptohomeLibrary::Get()->GetCachedSystemSalt(); + system_salt_ = SystemSaltGetter::Get()->GetCachedSystemSalt(); if (system_salt_.empty()) return false; if (!system_salt_key_.get()) diff --git a/chrome/browser/chromeos/status/data_promo_notification.cc b/chrome/browser/chromeos/status/data_promo_notification.cc index ba79302f42..d9f8ef9785 100644 --- a/chrome/browser/chromeos/status/data_promo_notification.cc +++ b/chrome/browser/chromeos/status/data_promo_notification.cc @@ -40,10 +40,6 @@ namespace chromeos { namespace { -// Time in milliseconds to delay showing of promo -// notification when Chrome window is not on screen. -const int kPromoShowDelayMs = 10000; - const int kNotificationCountPrefDefault = -1; bool GetBooleanPref(const char* pref_name) { diff --git a/chrome/browser/chromeos/system/ash_system_tray_delegate.cc b/chrome/browser/chromeos/system/ash_system_tray_delegate.cc index d2ea4b30fa..49b6f1f03c 100644 --- a/chrome/browser/chromeos/system/ash_system_tray_delegate.cc +++ b/chrome/browser/chromeos/system/ash_system_tray_delegate.cc @@ -57,6 +57,7 @@ #include "chrome/browser/chromeos/login/login_display_host_impl.h" #include "chrome/browser/chromeos/login/login_wizard.h" #include "chrome/browser/chromeos/login/startup_utils.h" +#include "chrome/browser/chromeos/login/supervised_user_manager.h" #include "chrome/browser/chromeos/login/user.h" #include "chrome/browser/chromeos/login/user_adding_screen.h" #include "chrome/browser/chromeos/login/user_manager.h" @@ -426,15 +427,17 @@ class SystemTrayDelegate : public ash::SystemTrayDelegate, virtual const std::string GetLocallyManagedUserManager() const OVERRIDE { if (GetUserLoginStatus() != ash::user::LOGGED_IN_LOCALLY_MANAGED) return std::string(); - return UserManager::Get()->GetManagerDisplayEmailForManagedUser( - chromeos::UserManager::Get()->GetActiveUser()->email()); + return UserManager::Get()->GetSupervisedUserManager()-> + GetManagerDisplayEmail( + chromeos::UserManager::Get()->GetActiveUser()->email()); } virtual const string16 GetLocallyManagedUserManagerName() const OVERRIDE { if (GetUserLoginStatus() != ash::user::LOGGED_IN_LOCALLY_MANAGED) return string16(); - return UserManager::Get()->GetManagerDisplayNameForManagedUser( - chromeos::UserManager::Get()->GetActiveUser()->email()); + return UserManager::Get()->GetSupervisedUserManager()-> + GetManagerDisplayName( + chromeos::UserManager::Get()->GetActiveUser()->email()); } virtual const string16 GetLocallyManagedUserMessage() const OVERRIDE { @@ -744,9 +747,8 @@ class SystemTrayDelegate : public ash::SystemTrayDelegate, virtual void ManageBluetoothDevices() OVERRIDE { content::RecordAction( content::UserMetricsAction("ShowBluetoothSettingsPage")); - std::string sub_page = std::string(chrome::kSearchSubPage) + "#" + - l10n_util::GetStringUTF8(IDS_OPTIONS_SETTINGS_SECTION_TITLE_BLUETOOTH); - chrome::ShowSettingsSubPage(GetAppropriateBrowser(), sub_page); + chrome::ShowSettingsSubPage(GetAppropriateBrowser(), + chrome::kBluetoothAddDeviceSubPage); } virtual void ToggleBluetooth() OVERRIDE { diff --git a/chrome/browser/chromeos/system/automatic_reboot_manager_unittest.cc b/chrome/browser/chromeos/system/automatic_reboot_manager_unittest.cc index 7aac719cfa..4ea30aadba 100644 --- a/chrome/browser/chromeos/system/automatic_reboot_manager_unittest.cc +++ b/chrome/browser/chromeos/system/automatic_reboot_manager_unittest.cc @@ -33,9 +33,9 @@ #include "chrome/test/base/testing_browser_process.h" #include "chromeos/chromeos_paths.h" #include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_dbus_thread_manager.h" #include "chromeos/dbus/fake_power_manager_client.h" #include "chromeos/dbus/fake_update_engine_client.h" -#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_details.h" #include "content/public/browser/notification_service.h" @@ -358,8 +358,7 @@ void AutomaticRebootManagerBasicTest::SetUp() { TestingBrowserProcess::GetGlobal()->SetLocalState(&local_state_); AutomaticRebootManager::RegisterPrefs(local_state_.registry()); - MockDBusThreadManagerWithoutGMock* dbus_manager = - new MockDBusThreadManagerWithoutGMock; + FakeDBusThreadManager* dbus_manager = new FakeDBusThreadManager; DBusThreadManager::InitializeForTesting(dbus_manager); power_manager_client_ = dbus_manager->fake_power_manager_client(); update_engine_client_ = dbus_manager->fake_update_engine_client(); diff --git a/chrome/browser/component_updater/ppapi_utils.cc b/chrome/browser/component_updater/ppapi_utils.cc index c59a3a13a1..34893378cb 100644 --- a/chrome/browser/component_updater/ppapi_utils.cc +++ b/chrome/browser/component_updater/ppapi_utils.cc @@ -30,6 +30,7 @@ #include "ppapi/c/dev/ppb_truetype_font_dev.h" #include "ppapi/c/dev/ppb_url_util_dev.h" #include "ppapi/c/dev/ppb_var_deprecated.h" +#include "ppapi/c/dev/ppb_var_resource_dev.h" #include "ppapi/c/dev/ppb_video_capture_dev.h" #include "ppapi/c/dev/ppb_video_decoder_dev.h" #include "ppapi/c/dev/ppb_view_dev.h" diff --git a/chrome/browser/content_settings/tab_specific_content_settings.cc b/chrome/browser/content_settings/tab_specific_content_settings.cc index 9c019ba621..ee562f3293 100644 --- a/chrome/browser/content_settings/tab_specific_content_settings.cc +++ b/chrome/browser/content_settings/tab_specific_content_settings.cc @@ -29,7 +29,6 @@ #include "content/public/browser/navigation_entry.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/render_view_host.h" -#include "content/public/browser/render_view_host_observer.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_delegate.h" #include "net/cookies/canonical_cookie.h" @@ -43,22 +42,6 @@ using content::WebContents; DEFINE_WEB_CONTENTS_USER_DATA_KEY(TabSpecificContentSettings); -namespace { - -class InterstitialHostObserver : public content::RenderViewHostObserver { - public: - explicit InterstitialHostObserver(RenderViewHost* rvh) - : content::RenderViewHostObserver(rvh) {} - - // content::RenderViewHostObserver overrides. - virtual void RenderViewHostInitialized() OVERRIDE { - Send(new ChromeViewMsg_SetAsInterstitial(routing_id())); - delete this; - } -}; - -} // namespace - TabSpecificContentSettings::SiteDataObserver::SiteDataObserver( TabSpecificContentSettings* tab_specific_content_settings) : tab_specific_content_settings_(tab_specific_content_settings) { @@ -664,8 +647,8 @@ void TabSpecificContentSettings::SetPepperBrokerAllowed(bool allowed) { void TabSpecificContentSettings::RenderViewForInterstitialPageCreated( RenderViewHost* render_view_host) { // We want to tell the renderer-side code to ignore content settings for this - // page but we must wait until the RenderView is created. - new InterstitialHostObserver(render_view_host); + // page. + Send(new ChromeViewMsg_SetAsInterstitial(routing_id())); } bool TabSpecificContentSettings::OnMessageReceived( diff --git a/chrome/browser/crash_handler_host_linux.cc b/chrome/browser/crash_handler_host_linux.cc deleted file mode 100644 index 41b0f55f54..0000000000 --- a/chrome/browser/crash_handler_host_linux.cc +++ /dev/null @@ -1,530 +0,0 @@ -// Copyright (c) 2012 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/crash_handler_host_linux.h" - -#include <stdint.h> -#include <stdlib.h> -#include <sys/socket.h> -#include <sys/syscall.h> -#include <unistd.h> - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/files/file_path.h" -#include "base/format_macros.h" -#include "base/linux_util.h" -#include "base/logging.h" -#include "base/memory/singleton.h" -#include "base/message_loop/message_loop.h" -#include "base/path_service.h" -#include "base/posix/eintr_wrapper.h" -#include "base/rand_util.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/threading/thread.h" -#include "breakpad/src/client/linux/handler/exception_handler.h" -#include "breakpad/src/client/linux/minidump_writer/linux_dumper.h" -#include "breakpad/src/client/linux/minidump_writer/minidump_writer.h" -#include "chrome/app/breakpad_linux_impl.h" -#include "chrome/common/chrome_paths.h" -#include "chrome/common/env_vars.h" -#include "content/public/browser/browser_thread.h" - -#if defined(OS_ANDROID) -#include <sys/linux-syscalls.h> - -#define SYS_read __NR_read -#endif - -using content::BrowserThread; -using google_breakpad::ExceptionHandler; - -namespace { - -// The length of the control message: -const unsigned kControlMsgSize = - CMSG_SPACE(2*sizeof(int)) + CMSG_SPACE(sizeof(struct ucred)); -// The length of the regular payload: -const unsigned kCrashContextSize = sizeof(ExceptionHandler::CrashContext); - -// Handles the crash dump and frees the allocated BreakpadInfo struct. -void CrashDumpTask(CrashHandlerHostLinux* handler, BreakpadInfo* info) { - if (handler->IsShuttingDown()) - return; - - HandleCrashDump(*info); - delete[] info->filename; - delete[] info->process_type; - delete[] info->distro; - delete info->crash_keys; - delete info; -} - -} // namespace - -// Since classes derived from CrashHandlerHostLinux are singletons, it's only -// destroyed at the end of the processes lifetime, which is greater in span than -// the lifetime of the IO message loop. Thus, all calls to base::Bind() use -// non-refcounted pointers. - -CrashHandlerHostLinux::CrashHandlerHostLinux() - : shutting_down_(false), - worker_pool_token_(BrowserThread::GetBlockingPool()->GetSequenceToken()) { - int fds[2]; - // We use SOCK_SEQPACKET rather than SOCK_DGRAM to prevent the process from - // sending datagrams to other sockets on the system. The sandbox may prevent - // the process from calling socket() to create new sockets, but it'll still - // inherit some sockets. With PF_UNIX+SOCK_DGRAM, it can call sendmsg to send - // a datagram to any (abstract) socket on the same system. With - // SOCK_SEQPACKET, this is prevented. - CHECK_EQ(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds), 0); - static const int on = 1; - - // Enable passcred on the server end of the socket - CHECK_EQ(setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)), 0); - - process_socket_ = fds[0]; - browser_socket_ = fds[1]; - - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&CrashHandlerHostLinux::Init, base::Unretained(this))); -} - -CrashHandlerHostLinux::~CrashHandlerHostLinux() { - (void) HANDLE_EINTR(close(process_socket_)); - (void) HANDLE_EINTR(close(browser_socket_)); -} - -void CrashHandlerHostLinux::Init() { - base::MessageLoopForIO* ml = base::MessageLoopForIO::current(); - CHECK(ml->WatchFileDescriptor( - browser_socket_, true /* persistent */, - base::MessageLoopForIO::WATCH_READ, - &file_descriptor_watcher_, this)); - ml->AddDestructionObserver(this); -} - -void CrashHandlerHostLinux::InitCrashUploaderThread() { - SetProcessType(); - uploader_thread_.reset( - new base::Thread(std::string(process_type_ + "_crash_uploader").c_str())); - uploader_thread_->Start(); -} - -void CrashHandlerHostLinux::OnFileCanWriteWithoutBlocking(int fd) { - NOTREACHED(); -} - -void CrashHandlerHostLinux::OnFileCanReadWithoutBlocking(int fd) { - DCHECK_EQ(fd, browser_socket_); - - // A process has crashed and has signaled us by writing a datagram - // to the death signal socket. The datagram contains the crash context needed - // for writing the minidump as well as a file descriptor and a credentials - // block so that they can't lie about their pid. - // - // The message sender is in chrome/app/breakpad_linux.cc. - - struct msghdr msg = {0}; - struct iovec iov[kCrashIovSize]; - - // Freed in WriteDumpFile(); - char* crash_context = new char[kCrashContextSize]; - // Freed in CrashDumpTask(); - char* distro = new char[kDistroSize + 1]; -#if defined(ADDRESS_SANITIZER) - asan_report_str_ = new char[kMaxAsanReportSize + 1]; -#endif - - // Freed in CrashDumpTask(). - CrashKeyStorage* crash_keys = new CrashKeyStorage; - google_breakpad::SerializedNonAllocatingMap* serialized_crash_keys; - size_t crash_keys_size = crash_keys->Serialize( - const_cast<const google_breakpad::SerializedNonAllocatingMap**>( - &serialized_crash_keys)); - - char* tid_buf_addr = NULL; - int tid_fd = -1; - uint64_t uptime; - size_t oom_size; - char control[kControlMsgSize]; - const ssize_t expected_msg_size = - kCrashContextSize + - kDistroSize + 1 + - sizeof(tid_buf_addr) + sizeof(tid_fd) + - sizeof(uptime) + -#if defined(ADDRESS_SANITIZER) - kMaxAsanReportSize + 1 + -#endif - sizeof(oom_size) + - crash_keys_size; - iov[0].iov_base = crash_context; - iov[0].iov_len = kCrashContextSize; - iov[1].iov_base = distro; - iov[1].iov_len = kDistroSize + 1; - iov[2].iov_base = &tid_buf_addr; - iov[2].iov_len = sizeof(tid_buf_addr); - iov[3].iov_base = &tid_fd; - iov[3].iov_len = sizeof(tid_fd); - iov[4].iov_base = &uptime; - iov[4].iov_len = sizeof(uptime); - iov[5].iov_base = &oom_size; - iov[5].iov_len = sizeof(oom_size); - iov[6].iov_base = serialized_crash_keys; - iov[6].iov_len = crash_keys_size; -#if defined(ADDRESS_SANITIZER) - iov[7].iov_base = asan_report_str_; - iov[7].iov_len = kMaxAsanReportSize + 1; -#endif - msg.msg_iov = iov; - msg.msg_iovlen = kCrashIovSize; - msg.msg_control = control; - msg.msg_controllen = kControlMsgSize; - - const ssize_t msg_size = HANDLE_EINTR(recvmsg(browser_socket_, &msg, 0)); - if (msg_size != expected_msg_size) { - LOG(ERROR) << "Error reading from death signal socket. Crash dumping" - << " is disabled." - << " msg_size:" << msg_size - << " errno:" << errno; - file_descriptor_watcher_.StopWatchingFileDescriptor(); - return; - } - - if (msg.msg_controllen != kControlMsgSize || - msg.msg_flags & ~MSG_TRUNC) { - LOG(ERROR) << "Received death signal message with the wrong size;" - << " msg.msg_controllen:" << msg.msg_controllen - << " msg.msg_flags:" << msg.msg_flags - << " kCrashContextSize:" << kCrashContextSize - << " kControlMsgSize:" << kControlMsgSize; - return; - } - - // Walk the control payload an extract the file descriptor and validated pid. - pid_t crashing_pid = -1; - int partner_fd = -1; - int signal_fd = -1; - for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr; - hdr = CMSG_NXTHDR(&msg, hdr)) { - if (hdr->cmsg_level != SOL_SOCKET) - continue; - if (hdr->cmsg_type == SCM_RIGHTS) { - const unsigned len = hdr->cmsg_len - - (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr); - DCHECK_EQ(len % sizeof(int), 0u); - const unsigned num_fds = len / sizeof(int); - if (num_fds != 2) { - // A nasty process could try and send us too many descriptors and - // force a leak. - LOG(ERROR) << "Death signal contained wrong number of descriptors;" - << " num_fds:" << num_fds; - for (unsigned i = 0; i < num_fds; ++i) - (void) HANDLE_EINTR(close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i])); - return; - } else { - partner_fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[0]; - signal_fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[1]; - } - } else if (hdr->cmsg_type == SCM_CREDENTIALS) { - const struct ucred *cred = - reinterpret_cast<struct ucred*>(CMSG_DATA(hdr)); - crashing_pid = cred->pid; - } - } - - if (crashing_pid == -1 || partner_fd == -1 || signal_fd == -1) { - LOG(ERROR) << "Death signal message didn't contain all expected control" - << " messages"; - if (partner_fd >= 0) - (void) HANDLE_EINTR(close(partner_fd)); - if (signal_fd >= 0) - (void) HANDLE_EINTR(close(signal_fd)); - return; - } - - // Kernel bug workaround (broken in 2.6.30 and 2.6.32, working in 2.6.38). - // The kernel doesn't translate PIDs in SCM_CREDENTIALS across PID - // namespaces. Thus |crashing_pid| might be garbage from our point of view. - // In the future we can remove this workaround, but we have to wait a couple - // of years to be sure that it's worked its way out into the world. - // TODO(thestig) Remove the workaround when Ubuntu Lucid is deprecated. - - // The crashing process closes its copy of the signal_fd immediately after - // calling sendmsg(). We can thus not reliably look for with with - // FindProcessHoldingSocket(). But by necessity, it has to keep the - // partner_fd open until the crashdump is complete. - ino_t inode_number; - if (!base::FileDescriptorGetInode(&inode_number, partner_fd)) { - LOG(WARNING) << "Failed to get inode number for passed socket"; - (void) HANDLE_EINTR(close(partner_fd)); - (void) HANDLE_EINTR(close(signal_fd)); - return; - } - (void) HANDLE_EINTR(close(partner_fd)); - - pid_t actual_crashing_pid = -1; - if (!base::FindProcessHoldingSocket(&actual_crashing_pid, inode_number)) { - LOG(WARNING) << "Failed to find process holding other end of crash reply " - "socket"; - (void) HANDLE_EINTR(close(signal_fd)); - return; - } - - crashing_pid = actual_crashing_pid; - - // The crashing TID set inside the compromised context via - // sys_gettid() in ExceptionHandler::HandleSignal might be wrong (if - // the kernel supports PID namespacing) and may need to be - // translated. - // - // We expect the crashing thread to be in sys_read(), waiting for us to - // write to |signal_fd|. Most newer kernels where we have the different pid - // namespaces also have /proc/[pid]/syscall, so we can look through - // |actual_crashing_pid|'s thread group and find the thread that's in the - // read syscall with the right arguments. - - std::string expected_syscall_data; - // /proc/[pid]/syscall is formatted as follows: - // syscall_number arg1 ... arg6 sp pc - // but we just check syscall_number through arg3. - base::StringAppendF(&expected_syscall_data, "%d 0x%x %p 0x1 ", - SYS_read, tid_fd, tid_buf_addr); - bool syscall_supported = false; - pid_t crashing_tid = - base::FindThreadIDWithSyscall(crashing_pid, - expected_syscall_data, - &syscall_supported); - if (crashing_tid == -1) { - // We didn't find the thread we want. Maybe it didn't reach - // sys_read() yet or the thread went away. We'll just take a - // guess here and assume the crashing thread is the thread group - // leader. If procfs syscall is not supported by the kernel, then - // we assume the kernel also does not support TID namespacing and - // trust the TID passed by the crashing process. - LOG(WARNING) << "Could not translate tid - assuming crashing thread is " - "thread group leader; syscall_supported=" << syscall_supported; - crashing_tid = crashing_pid; - } - - ExceptionHandler::CrashContext* bad_context = - reinterpret_cast<ExceptionHandler::CrashContext*>(crash_context); - bad_context->tid = crashing_tid; - - // Freed in CrashDumpTask(); - BreakpadInfo* info = new BreakpadInfo; - - info->fd = -1; - info->process_type_length = process_type_.length(); - char* process_type_str = new char[info->process_type_length + 1]; - process_type_.copy(process_type_str, info->process_type_length); - process_type_str[info->process_type_length] = '\0'; - info->process_type = process_type_str; - - info->distro_length = strlen(distro); - info->distro = distro; -#if defined(OS_ANDROID) - // Nothing gets uploaded in android. - info->upload = false; -#else - info->upload = (getenv(env_vars::kHeadless) == NULL); -#endif - - info->crash_keys = crash_keys; - -#if defined(ADDRESS_SANITIZER) - info->asan_report_str = asan_report_str_; - info->asan_report_length = strlen(asan_report_str_); -#endif - info->process_start_time = uptime; - info->oom_size = oom_size; - - BrowserThread::GetBlockingPool()->PostSequencedWorkerTask( - worker_pool_token_, - FROM_HERE, - base::Bind(&CrashHandlerHostLinux::WriteDumpFile, - base::Unretained(this), - info, - crashing_pid, - crash_context, - signal_fd)); -} - -void CrashHandlerHostLinux::WriteDumpFile(BreakpadInfo* info, - pid_t crashing_pid, - char* crash_context, - int signal_fd) { - DCHECK(BrowserThread::GetBlockingPool()->IsRunningSequenceOnCurrentThread( - worker_pool_token_)); - - base::FilePath dumps_path("/tmp"); - PathService::Get(base::DIR_TEMP, &dumps_path); - if (!info->upload) - PathService::Get(chrome::DIR_CRASH_DUMPS, &dumps_path); - const uint64 rand = base::RandUint64(); - const std::string minidump_filename = - base::StringPrintf("%s/chromium-%s-minidump-%016" PRIx64 ".dmp", - dumps_path.value().c_str(), - process_type_.c_str(), - rand); - - if (!google_breakpad::WriteMinidump(minidump_filename.c_str(), - kMaxMinidumpFileSize, - crashing_pid, crash_context, - kCrashContextSize, - google_breakpad::MappingList(), - google_breakpad::AppMemoryList())) { - LOG(ERROR) << "Failed to write crash dump for pid " << crashing_pid; - } -#if defined(ADDRESS_SANITIZER) - // Create a temporary file holding the AddressSanitizer report. - const std::string log_filename = - base::StringPrintf("%s/chromium-%s-minidump-%016" PRIx64 ".log", - dumps_path.value().c_str(), - process_type_.c_str(), - rand); - FILE* logfile = fopen(log_filename.c_str(), "w"); - CHECK(logfile); - fprintf(logfile, "%s", asan_report_str_); - fclose(logfile); -#endif - - delete[] crash_context; - - // Freed in CrashDumpTask(); - char* minidump_filename_str = new char[minidump_filename.length() + 1]; - minidump_filename.copy(minidump_filename_str, minidump_filename.length()); - minidump_filename_str[minidump_filename.length()] = '\0'; - info->filename = minidump_filename_str; -#if defined(ADDRESS_SANITIZER) - char* minidump_log_filename_str = new char[minidump_filename.length() + 1]; - minidump_filename.copy(minidump_log_filename_str, minidump_filename.length()); - memcpy(minidump_log_filename_str + minidump_filename.length() - 3, "log", 3); - minidump_log_filename_str[minidump_filename.length()] = '\0'; - info->log_filename = minidump_log_filename_str; -#endif - info->pid = crashing_pid; - - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&CrashHandlerHostLinux::QueueCrashDumpTask, - base::Unretained(this), - info, - signal_fd)); -} - -void CrashHandlerHostLinux::QueueCrashDumpTask(BreakpadInfo* info, - int signal_fd) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - - // Send the done signal to the process: it can exit now. - struct msghdr msg = {0}; - struct iovec done_iov; - done_iov.iov_base = const_cast<char*>("\x42"); - done_iov.iov_len = 1; - msg.msg_iov = &done_iov; - msg.msg_iovlen = 1; - - (void) HANDLE_EINTR(sendmsg(signal_fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL)); - (void) HANDLE_EINTR(close(signal_fd)); - - uploader_thread_->message_loop()->PostTask( - FROM_HERE, - base::Bind(&CrashDumpTask, base::Unretained(this), info)); -} - -void CrashHandlerHostLinux::WillDestroyCurrentMessageLoop() { - file_descriptor_watcher_.StopWatchingFileDescriptor(); - - // If we are quitting and there are crash dumps in the queue, turn them into - // no-ops. - shutting_down_ = true; - uploader_thread_->Stop(); -} - -bool CrashHandlerHostLinux::IsShuttingDown() const { - return shutting_down_; -} - -ExtensionCrashHandlerHostLinux::ExtensionCrashHandlerHostLinux() { - InitCrashUploaderThread(); -} - -ExtensionCrashHandlerHostLinux::~ExtensionCrashHandlerHostLinux() { -} - -void ExtensionCrashHandlerHostLinux::SetProcessType() { - process_type_ = "extension"; -} - -// static -ExtensionCrashHandlerHostLinux* ExtensionCrashHandlerHostLinux::GetInstance() { - return Singleton<ExtensionCrashHandlerHostLinux>::get(); -} - -GpuCrashHandlerHostLinux::GpuCrashHandlerHostLinux() { - InitCrashUploaderThread(); -} - -GpuCrashHandlerHostLinux::~GpuCrashHandlerHostLinux() { -} - -void GpuCrashHandlerHostLinux::SetProcessType() { - process_type_ = "gpu-process"; -} - -// static -GpuCrashHandlerHostLinux* GpuCrashHandlerHostLinux::GetInstance() { - return Singleton<GpuCrashHandlerHostLinux>::get(); -} - -PluginCrashHandlerHostLinux::PluginCrashHandlerHostLinux() { - InitCrashUploaderThread(); -} - -PluginCrashHandlerHostLinux::~PluginCrashHandlerHostLinux() { -} - -void PluginCrashHandlerHostLinux::SetProcessType() { - process_type_ = "plugin"; -} - -// static -PluginCrashHandlerHostLinux* PluginCrashHandlerHostLinux::GetInstance() { - return Singleton<PluginCrashHandlerHostLinux>::get(); -} - -PpapiCrashHandlerHostLinux::PpapiCrashHandlerHostLinux() { - InitCrashUploaderThread(); -} - -PpapiCrashHandlerHostLinux::~PpapiCrashHandlerHostLinux() { -} - -void PpapiCrashHandlerHostLinux::SetProcessType() { - process_type_ = "ppapi"; -} - -// static -PpapiCrashHandlerHostLinux* PpapiCrashHandlerHostLinux::GetInstance() { - return Singleton<PpapiCrashHandlerHostLinux>::get(); -} - -RendererCrashHandlerHostLinux::RendererCrashHandlerHostLinux() { - InitCrashUploaderThread(); -} - -RendererCrashHandlerHostLinux::~RendererCrashHandlerHostLinux() { -} - -void RendererCrashHandlerHostLinux::SetProcessType() { - process_type_ = "renderer"; -} - -// static -RendererCrashHandlerHostLinux* RendererCrashHandlerHostLinux::GetInstance() { - return Singleton<RendererCrashHandlerHostLinux>::get(); -} diff --git a/chrome/browser/crash_handler_host_linux.h b/chrome/browser/crash_handler_host_linux.h deleted file mode 100644 index 6b2703729d..0000000000 --- a/chrome/browser/crash_handler_host_linux.h +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright (c) 2012 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_CRASH_HANDLER_HOST_LINUX_H_ -#define CHROME_BROWSER_CRASH_HANDLER_HOST_LINUX_H_ - -#include <sys/types.h> - -#include <string> - -#include "base/compiler_specific.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop.h" -#include "base/threading/sequenced_worker_pool.h" - -struct BreakpadInfo; - -namespace base { -class Thread; -} - -template <typename T> struct DefaultSingletonTraits; - -// This is the base class for singleton objects which crash dump renderers and -// plugins on Linux or Android. We perform the crash dump from the browser -// because it allows us to be outside the sandbox. -// -// PluginCrashHandlerHostLinux and RendererCrashHandlerHostLinux are -// singletons that handle plugin and renderer crashes, respectively. -// -// Processes signal that they need to be dumped by sending a datagram over a -// UNIX domain socket. All processes of the same type share the client end of -// this socket which is installed in their descriptor table before exec. -class CrashHandlerHostLinux : public base::MessageLoopForIO::Watcher, - public base::MessageLoop::DestructionObserver { - public: - // Get the file descriptor which processes should be given in order to signal - // crashes to the browser. - int GetDeathSignalSocket() const { - return process_socket_; - } - - // MessagePumbLibevent::Watcher impl: - virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE; - virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE; - - // MessageLoop::DestructionObserver impl: - virtual void WillDestroyCurrentMessageLoop() OVERRIDE; - - // Whether we are shutting down or not. - bool IsShuttingDown() const; - - protected: - CrashHandlerHostLinux(); - virtual ~CrashHandlerHostLinux(); - - // Only called in concrete subclasses. - void InitCrashUploaderThread(); - - std::string process_type_; - - private: - void Init(); - - // This is here on purpose to make CrashHandlerHostLinux abstract. - virtual void SetProcessType() = 0; - - // Do work on the FILE thread for OnFileCanReadWithoutBlocking(). - void WriteDumpFile(BreakpadInfo* info, - pid_t crashing_pid, - char* crash_context, - int signal_fd); - - // Continue OnFileCanReadWithoutBlocking()'s work on the IO thread. - void QueueCrashDumpTask(BreakpadInfo* info, int signal_fd); - - int process_socket_; - int browser_socket_; - - base::MessageLoopForIO::FileDescriptorWatcher file_descriptor_watcher_; - scoped_ptr<base::Thread> uploader_thread_; - bool shutting_down_; - - // Unique sequence token so that writing crash dump won't be blocked - // by other tasks. - base::SequencedWorkerPool::SequenceToken worker_pool_token_; - -#if defined(ADDRESS_SANITIZER) - char* asan_report_str_; -#endif - - DISALLOW_COPY_AND_ASSIGN(CrashHandlerHostLinux); -}; - -class ExtensionCrashHandlerHostLinux : public CrashHandlerHostLinux { - public: - // Returns the singleton instance. - static ExtensionCrashHandlerHostLinux* GetInstance(); - - private: - friend struct DefaultSingletonTraits<ExtensionCrashHandlerHostLinux>; - ExtensionCrashHandlerHostLinux(); - virtual ~ExtensionCrashHandlerHostLinux(); - - virtual void SetProcessType() OVERRIDE; - - DISALLOW_COPY_AND_ASSIGN(ExtensionCrashHandlerHostLinux); -}; - -class GpuCrashHandlerHostLinux : public CrashHandlerHostLinux { - public: - // Returns the singleton instance. - static GpuCrashHandlerHostLinux* GetInstance(); - - private: - friend struct DefaultSingletonTraits<GpuCrashHandlerHostLinux>; - GpuCrashHandlerHostLinux(); - virtual ~GpuCrashHandlerHostLinux(); - - virtual void SetProcessType() OVERRIDE; - - DISALLOW_COPY_AND_ASSIGN(GpuCrashHandlerHostLinux); -}; - -class PluginCrashHandlerHostLinux : public CrashHandlerHostLinux { - public: - // Returns the singleton instance. - static PluginCrashHandlerHostLinux* GetInstance(); - - private: - friend struct DefaultSingletonTraits<PluginCrashHandlerHostLinux>; - PluginCrashHandlerHostLinux(); - virtual ~PluginCrashHandlerHostLinux(); - - virtual void SetProcessType() OVERRIDE; - - DISALLOW_COPY_AND_ASSIGN(PluginCrashHandlerHostLinux); -}; - -class PpapiCrashHandlerHostLinux : public CrashHandlerHostLinux { - public: - // Returns the singleton instance. - static PpapiCrashHandlerHostLinux* GetInstance(); - - private: - friend struct DefaultSingletonTraits<PpapiCrashHandlerHostLinux>; - PpapiCrashHandlerHostLinux(); - virtual ~PpapiCrashHandlerHostLinux(); - - virtual void SetProcessType() OVERRIDE; - - DISALLOW_COPY_AND_ASSIGN(PpapiCrashHandlerHostLinux); -}; - -class RendererCrashHandlerHostLinux : public CrashHandlerHostLinux { - public: - // Returns the singleton instance. - static RendererCrashHandlerHostLinux* GetInstance(); - - private: - friend struct DefaultSingletonTraits<RendererCrashHandlerHostLinux>; - RendererCrashHandlerHostLinux(); - virtual ~RendererCrashHandlerHostLinux(); - - virtual void SetProcessType() OVERRIDE; - - DISALLOW_COPY_AND_ASSIGN(RendererCrashHandlerHostLinux); -}; - -#endif // CHROME_BROWSER_CRASH_HANDLER_HOST_LINUX_H_ diff --git a/chrome/browser/devtools/OWNERS b/chrome/browser/devtools/OWNERS index 9793bcc9f5..030f974441 100644 --- a/chrome/browser/devtools/OWNERS +++ b/chrome/browser/devtools/OWNERS @@ -1,4 +1,6 @@ +kaznacheev@chromium.org pfeldman@chromium.org +vsevik@chromium.org yurys@chromium.org # Changes to embedder messages require a security review. diff --git a/chrome/browser/devtools/devtools_adb_bridge.cc b/chrome/browser/devtools/devtools_adb_bridge.cc index 40989f4f81..156bcf4a21 100644 --- a/chrome/browser/devtools/devtools_adb_bridge.cc +++ b/chrome/browser/devtools/devtools_adb_bridge.cc @@ -79,19 +79,17 @@ typedef base::Callback<void(const AndroidDevices&)> AndroidDevicesCallback; class AdbDeviceImpl : public DevToolsAdbBridge::AndroidDevice { public: - explicit AdbDeviceImpl(const std::string& serial); + AdbDeviceImpl(const std::string& serial, bool is_connected); virtual void RunCommand(const std::string& command, const CommandCallback& callback) OVERRIDE; virtual void OpenSocket(const std::string& name, const SocketCallback& callback) OVERRIDE; - virtual bool IsConnected() OVERRIDE; - private: virtual ~AdbDeviceImpl() {} }; -AdbDeviceImpl::AdbDeviceImpl(const std::string& serial) - : AndroidDevice(serial) { +AdbDeviceImpl::AdbDeviceImpl(const std::string& serial, bool is_connected) + : AndroidDevice(serial, is_connected) { } void AdbDeviceImpl::RunCommand(const std::string& command, @@ -108,11 +106,6 @@ void AdbDeviceImpl::OpenSocket(const std::string& name, AdbClientSocket::TransportQuery(kAdbPort, serial(), socket_name, callback); } -bool AdbDeviceImpl::IsConnected() { - return true; -} - - // UsbDeviceImpl -------------------------------------------------------------- class UsbDeviceImpl : public DevToolsAdbBridge::AndroidDevice { @@ -122,7 +115,6 @@ class UsbDeviceImpl : public DevToolsAdbBridge::AndroidDevice { const CommandCallback& callback) OVERRIDE; virtual void OpenSocket(const std::string& name, const SocketCallback& callback) OVERRIDE; - virtual bool IsConnected() OVERRIDE; private: void OnOpenSocket(const SocketCallback& callback, @@ -143,7 +135,7 @@ class UsbDeviceImpl : public DevToolsAdbBridge::AndroidDevice { UsbDeviceImpl::UsbDeviceImpl(AndroidUsbDevice* device) - : AndroidDevice(device->serial()), + : AndroidDevice(device->serial(), device->is_connected()), device_(device) { device_->InitOnCallerThread(); } @@ -208,10 +200,6 @@ void UsbDeviceImpl::OnRead(net::StreamSocket* socket, OnRead(socket, buffer, new_data, callback, result); } -bool UsbDeviceImpl::IsConnected() { - return device_->is_connected(); -} - // AdbCountDevicesCommand ----------------------------------------------------- @@ -351,7 +339,7 @@ void AdbPagesCommand::WrapUsbDevices(const AndroidUsbDevices& usb_devices) { DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current()); #if defined(DEBUG_DEVTOOLS) - devices_.push_back(new AdbDeviceImpl("")); // For desktop remote debugging. + devices_.push_back(new AdbDeviceImpl("", true)); // For desktop debugging. #endif // defined(DEBUG_DEVTOOLS) for (AndroidUsbDevices::const_iterator it = usb_devices.begin(); @@ -373,7 +361,8 @@ void AdbPagesCommand::ReceivedAdbDevices( for (size_t i = 0; i < serials.size(); ++i) { std::vector<std::string> tokens; Tokenize(serials[i], "\t ", &tokens); - devices_.push_back(new AdbDeviceImpl(tokens[0])); + bool offline = tokens.size() > 1 && tokens[1] == "offline"; + devices_.push_back(new AdbDeviceImpl(tokens[0], !offline)); } ProcessSerials(); } @@ -409,7 +398,7 @@ void AdbPagesCommand::ProcessSerials() { #endif // defined(DEBUG_DEVTOOLS) scoped_refptr<DevToolsAdbBridge::AndroidDevice> device = devices_.back(); - if (device->IsConnected()) { + if (device->is_connected()) { device->RunCommand(kDeviceModelCommand, base::Bind(&AdbPagesCommand::ReceivedModel, this)); } else { @@ -763,8 +752,10 @@ DevToolsAdbBridge::Factory::BuildServiceInstanceFor( // DevToolsAdbBridge::AndroidDevice ------------------------------------------- -DevToolsAdbBridge::AndroidDevice::AndroidDevice(const std::string& serial) - : serial_(serial) { +DevToolsAdbBridge::AndroidDevice::AndroidDevice(const std::string& serial, + bool is_connected) + : serial_(serial), + is_connected_(is_connected) { } void DevToolsAdbBridge::AndroidDevice::HttpQuery( @@ -1062,7 +1053,7 @@ std::string DevToolsAdbBridge::RemoteDevice::GetModel() { } bool DevToolsAdbBridge::RemoteDevice::IsConnected() { - return device_->IsConnected(); + return device_->is_connected(); } void DevToolsAdbBridge::RemoteDevice::AddBrowser( diff --git a/chrome/browser/devtools/devtools_adb_bridge.h b/chrome/browser/devtools/devtools_adb_bridge.h index 0273e081d7..ccc34042cb 100644 --- a/chrome/browser/devtools/devtools_adb_bridge.h +++ b/chrome/browser/devtools/devtools_adb_bridge.h @@ -211,13 +211,12 @@ class DevToolsAdbBridge class AndroidDevice : public base::RefCounted<AndroidDevice> { public: - explicit AndroidDevice(const std::string& serial); + AndroidDevice(const std::string& serial, bool is_connected); virtual void RunCommand(const std::string& command, const CommandCallback& callback) = 0; virtual void OpenSocket(const std::string& socket_name, const SocketCallback& callback) = 0; - virtual bool IsConnected() = 0; void HttpQuery(const std::string& la_name, const std::string& request, const CommandCallback& callback); @@ -226,6 +225,7 @@ class DevToolsAdbBridge const SocketCallback& callback); std::string serial() { return serial_; } + bool is_connected() { return is_connected_; } std::string model() { return model_; } void set_model(const std::string& model) { model_ = model; } @@ -245,6 +245,7 @@ class DevToolsAdbBridge net::StreamSocket* socket); std::string serial_; + bool is_connected_; std::string model_; DISALLOW_COPY_AND_ASSIGN(AndroidDevice); diff --git a/chrome/browser/devtools/devtools_file_helper.cc b/chrome/browser/devtools/devtools_file_helper.cc index 38e49bcaa7..d8372a40eb 100644 --- a/chrome/browser/devtools/devtools_file_helper.cc +++ b/chrome/browser/devtools/devtools_file_helper.cc @@ -152,6 +152,7 @@ std::string RegisterFileSystem(WebContents* web_contents, policy->GrantReadFileSystem(renderer_id, file_system_id); policy->GrantWriteFileSystem(renderer_id, file_system_id); policy->GrantCreateFileForFileSystem(renderer_id, file_system_id); + policy->GrantDeleteFromFileSystem(renderer_id, file_system_id); // We only need file level access for reading FileEntries. Saving FileEntries // just needs the file system to have read/write access, which is granted diff --git a/chrome/browser/devtools/devtools_sanity_browsertest.cc b/chrome/browser/devtools/devtools_sanity_browsertest.cc index 57472a6ab8..c0129ffe83 100644 --- a/chrome/browser/devtools/devtools_sanity_browsertest.cc +++ b/chrome/browser/devtools/devtools_sanity_browsertest.cc @@ -444,16 +444,18 @@ IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestBeforeUnloadEvents) { } // Tests scripts panel showing. -IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestShowScriptsTab) { +// Disabled: http://crbug.com/309822 +IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, DISABLED_TestShowScriptsTab) { RunTest("testShowScriptsTab", kDebuggerTestPage); } // Tests that scripts tab is populated with inspected scripts even if it // hadn't been shown by the moment inspected paged refreshed. // @see http://crbug.com/26312 +// Disabled: http://crbug.com/309822 IN_PROC_BROWSER_TEST_F( DevToolsSanityTest, - TestScriptsTabIsPopulatedOnInspectedPageRefresh) { + DISABLED_TestScriptsTabIsPopulatedOnInspectedPageRefresh) { // Clear inspector settings to ensure that Elements will be // current panel when DevTools window is open. content::BrowserContext* browser_context = @@ -497,22 +499,25 @@ IN_PROC_BROWSER_TEST_F(DevToolsExperimentalExtensionTest, // Tests that a content script is in the scripts list. // http://crbug.com/114104 +// Disabled: http://crbug.com/309822 IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest, - TestContentScriptIsPresent) { + DISABLED_TestContentScriptIsPresent) { LoadExtension("simple_content_script"); RunTest("testContentScriptIsPresent", kPageWithContentScript); } // Tests that scripts are not duplicated after Scripts Panel switch. +// Disabled: http://crbug.com/309822 IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, - TestNoScriptDuplicatesOnPanelSwitch) { + DISABLED_TestNoScriptDuplicatesOnPanelSwitch) { RunTest("testNoScriptDuplicatesOnPanelSwitch", kDebuggerTestPage); } // Tests that debugger works correctly if pause event occurs when DevTools // frontend is being loaded. +// Disabled: http://crbug.com/309822 IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, - TestPauseWhenLoadingDevTools) { + DISABLED_TestPauseWhenLoadingDevTools) { RunTest("testPauseWhenLoadingDevTools", kPauseWhenLoadingDevTools); } @@ -527,7 +532,9 @@ IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, #else #define MAYBE_TestPauseWhenScriptIsRunning TestPauseWhenScriptIsRunning #endif -IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, MAYBE_TestPauseWhenScriptIsRunning) { +// Disabled: http://crbug.com/309822 +IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, + DISABLED_TestPauseWhenScriptIsRunning) { RunTest("testPauseWhenScriptIsRunning", kPauseWhenScriptIsRunning); } diff --git a/chrome/browser/devtools/devtools_window.cc b/chrome/browser/devtools/devtools_window.cc index 40ccdcb3cd..a3fa06396b 100644 --- a/chrome/browser/devtools/devtools_window.cc +++ b/chrome/browser/devtools/devtools_window.cc @@ -17,6 +17,7 @@ #include "chrome/browser/extensions/api/debugger/debugger_api.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_web_contents_observer.h" #include "chrome/browser/file_select_helper.h" #include "chrome/browser/infobars/confirm_infobar_delegate.h" #include "chrome/browser/prefs/pref_service_syncable.h" @@ -572,6 +573,7 @@ DevToolsWindow::DevToolsWindow(Profile* profile, web_contents_, this)); file_helper_.reset(new DevToolsFileHelper(web_contents_, profile)); file_system_indexer_ = new DevToolsFileSystemIndexer(); + extensions::ExtensionWebContentsObserver::CreateForWebContents(web_contents_); g_instances.Get().push_back(this); @@ -621,6 +623,9 @@ GURL DevToolsWindow::GetDevToolsURL(Profile* profile, DevToolsDockSide dock_side, bool shared_worker_frontend, bool external_frontend) { + if (base_url.SchemeIs("data")) + return base_url; + std::string frontend_url( base_url.is_empty() ? chrome::kChromeUIDevToolsURL : base_url.spec()); ThemeService* tp = ThemeServiceFactory::GetForProfile(profile); diff --git a/chrome/browser/download/OWNERS b/chrome/browser/download/OWNERS index bd9d0d4fb3..93ed63f383 100644 --- a/chrome/browser/download/OWNERS +++ b/chrome/browser/download/OWNERS @@ -5,3 +5,6 @@ phajdan.jr@chromium.org rdsmith@chromium.org per-file *_file_picker_chromeos.*=achuith@chromium.org + +per-file download_dir_policy_handler*=dconnelly@chromium.org +per-file download_dir_policy_handler*=joaodasilva@chromium.org diff --git a/chrome/browser/download/download_browsertest.cc b/chrome/browser/download/download_browsertest.cc index 31d8abe656..e99192fdb8 100644 --- a/chrome/browser/download/download_browsertest.cc +++ b/chrome/browser/download/download_browsertest.cc @@ -703,6 +703,7 @@ class DownloadTest : public InProcessBrowserTest { GURL slow_download_url(URLRequestSlowDownloadJob::kUnknownSizeUrl); DownloadManager* manager = DownloadManagerForBrowser(browser()); + EXPECT_EQ(0, manager->NonMaliciousInProgressCount()); EXPECT_EQ(0, manager->InProgressCount()); if (manager->InProgressCount() != 0) return NULL; diff --git a/chrome/browser/download/download_danger_prompt.cc b/chrome/browser/download/download_danger_prompt.cc index a22eab1cf4..9c62965959 100644 --- a/chrome/browser/download/download_danger_prompt.cc +++ b/chrome/browser/download/download_danger_prompt.cc @@ -8,7 +8,6 @@ #include "base/metrics/field_trial.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/download/chrome_download_manager_delegate.h" -#include "chrome/browser/download/download_field_trial.h" #include "chrome/browser/ui/tab_modal_confirm_dialog.h" #include "chrome/browser/ui/tab_modal_confirm_dialog_delegate.h" #include "content/public/browser/download_danger_type.h" @@ -41,6 +40,7 @@ class DownloadDangerPromptImpl : public DownloadDangerPrompt, virtual string16 GetTitle() OVERRIDE; virtual string16 GetMessage() OVERRIDE; virtual string16 GetAcceptButtonTitle() OVERRIDE; + virtual string16 GetCancelButtonTitle() OVERRIDE; virtual void OnAccepted() OVERRIDE; virtual void OnCanceled() OVERRIDE; virtual void OnClosed() OVERRIDE; @@ -96,48 +96,56 @@ void DownloadDangerPromptImpl::OnDownloadUpdated( } string16 DownloadDangerPromptImpl::GetTitle() { - return l10n_util::GetStringUTF16(IDS_CONFIRM_KEEP_DANGEROUS_DOWNLOAD_TITLE); + if (show_context_) + return l10n_util::GetStringUTF16(IDS_CONFIRM_KEEP_DANGEROUS_DOWNLOAD_TITLE); + else + return l10n_util::GetStringUTF16(IDS_RESTORE_KEEP_DANGEROUS_DOWNLOAD_TITLE); } string16 DownloadDangerPromptImpl::GetMessage() { - if (!show_context_) - return l10n_util::GetStringUTF16( - IDS_PROMPT_CONFIRM_KEEP_DANGEROUS_DOWNLOAD); - switch (download_->GetDangerType()) { - case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: { - return l10n_util::GetStringFUTF16( - IDS_PROMPT_DANGEROUS_DOWNLOAD, - download_->GetFileNameToReportUser().LossyDisplayName()); - } - case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: // Fall through - case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT: - case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: { - std::string trial_condition = - base::FieldTrialList::FindFullName(kMalwareWarningFinchTrialName); - if (trial_condition.empty()) { + if (show_context_) { + switch (download_->GetDangerType()) { + case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: { + return l10n_util::GetStringFUTF16( + IDS_PROMPT_DANGEROUS_DOWNLOAD, + download_->GetFileNameToReportUser().LossyDisplayName()); + } + case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: // Fall through + case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT: + case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: { return l10n_util::GetStringFUTF16( IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT, download_->GetFileNameToReportUser().LossyDisplayName()); } - return AssembleMalwareFinchString( - trial_condition, - download_->GetFileNameToReportUser().LossyDisplayName()); - } - case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: { - return l10n_util::GetStringFUTF16( - IDS_PROMPT_UNCOMMON_DOWNLOAD_CONTENT, - download_->GetFileNameToReportUser().LossyDisplayName()); - } - case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: { - return l10n_util::GetStringFUTF16( - IDS_PROMPT_DOWNLOAD_CHANGES_SEARCH_SETTINGS, - download_->GetFileNameToReportUser().LossyDisplayName()); + case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: { + return l10n_util::GetStringFUTF16( + IDS_PROMPT_UNCOMMON_DOWNLOAD_CONTENT, + download_->GetFileNameToReportUser().LossyDisplayName()); + } + case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: { + return l10n_util::GetStringFUTF16( + IDS_PROMPT_DOWNLOAD_CHANGES_SEARCH_SETTINGS, + download_->GetFileNameToReportUser().LossyDisplayName()); + } + case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS: + case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT: + case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED: + case content::DOWNLOAD_DANGER_TYPE_MAX: { + break; + } } - case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS: - case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT: - case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED: - case content::DOWNLOAD_DANGER_TYPE_MAX: { - break; + } else { + switch (download_->GetDangerType()) { + case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: + case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT: + case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: { + return l10n_util::GetStringUTF16( + IDS_PROMPT_CONFIRM_KEEP_MALICIOUS_DOWNLOAD); + } + default: { + return l10n_util::GetStringUTF16( + IDS_PROMPT_CONFIRM_KEEP_DANGEROUS_DOWNLOAD); + } } } NOTREACHED(); @@ -145,8 +153,31 @@ string16 DownloadDangerPromptImpl::GetMessage() { } string16 DownloadDangerPromptImpl::GetAcceptButtonTitle() { - return l10n_util::GetStringUTF16( - show_context_ ? IDS_CONFIRM_DOWNLOAD : IDS_CONFIRM_DOWNLOAD_AGAIN); + if (show_context_) + return l10n_util::GetStringUTF16(IDS_CONFIRM_DOWNLOAD); + switch (download_->GetDangerType()) { + case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: + case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT: + case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: { + return l10n_util::GetStringUTF16(IDS_CONFIRM_DOWNLOAD_AGAIN_MALICIOUS); + } + default: + return l10n_util::GetStringUTF16(IDS_CONFIRM_DOWNLOAD_AGAIN); + } +} + +string16 DownloadDangerPromptImpl::GetCancelButtonTitle() { + if (show_context_) + return l10n_util::GetStringUTF16(IDS_CANCEL); + switch (download_->GetDangerType()) { + case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: + case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT: + case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: { + return l10n_util::GetStringUTF16(IDS_CONFIRM_CANCEL_AGAIN_MALICIOUS); + } + default: + return l10n_util::GetStringUTF16(IDS_CANCEL); + } } void DownloadDangerPromptImpl::OnAccepted() { diff --git a/chrome/browser/download/download_danger_prompt_browsertest.cc b/chrome/browser/download/download_danger_prompt_browsertest.cc index 02627adf92..cdd17d02dc 100644 --- a/chrome/browser/download/download_danger_prompt_browsertest.cc +++ b/chrome/browser/download/download_danger_prompt_browsertest.cc @@ -77,8 +77,10 @@ class DownloadDangerPromptTest : public InProcessBrowserTest { EXPECT_CALL(download_, GetFileNameToReportUser()).WillRepeatedly(Return( base::FilePath(FILE_PATH_LITERAL("evil.exe")))); EXPECT_CALL(download_, AddObserver(_)) - .WillOnce(SaveArg<0>(&download_observer_)); + .WillOnce(SaveArg<0>(&download_observer_)); EXPECT_CALL(download_, RemoveObserver(Eq(ByRef(download_observer_)))); + EXPECT_CALL(download_, GetDangerType()) + .WillRepeatedly(Return(content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL)); } void CreatePrompt() { diff --git a/chrome/browser/download/download_dir_policy_handler.cc b/chrome/browser/download/download_dir_policy_handler.cc new file mode 100644 index 0000000000..f1d4932ac8 --- /dev/null +++ b/chrome/browser/download/download_dir_policy_handler.cc @@ -0,0 +1,42 @@ +// Copyright 2013 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/download/download_dir_policy_handler.h" + +#include "base/files/file_path.h" +#include "base/prefs/pref_value_map.h" +#include "base/values.h" +#include "chrome/browser/download/download_prefs.h" +#include "chrome/browser/policy/policy_map.h" +#include "chrome/browser/policy/policy_path_parser.h" +#include "chrome/common/pref_names.h" +#include "policy/policy_constants.h" + +DownloadDirPolicyHandler::DownloadDirPolicyHandler() + : TypeCheckingPolicyHandler(policy::key::kDownloadDirectory, + base::Value::TYPE_STRING) {} + +DownloadDirPolicyHandler::~DownloadDirPolicyHandler() {} + +void DownloadDirPolicyHandler::ApplyPolicySettings( + const policy::PolicyMap& policies, + PrefValueMap* prefs) { + const base::Value* value = policies.GetValue(policy_name()); + base::FilePath::StringType string_value; + if (!value || !value->GetAsString(&string_value)) + return; + + base::FilePath::StringType expanded_value = + policy::path_parser::ExpandPathVariables(string_value); + // Make sure the path isn't empty, since that will point to an undefined + // location; the default location is used instead in that case. + // This is checked after path expansion because a non-empty policy value can + // lead to an empty path value after expansion (e.g. "\"\""). + if (expanded_value.empty()) + expanded_value = DownloadPrefs::GetDefaultDownloadDirectory().value(); + prefs->SetValue(prefs::kDownloadDefaultDirectory, + Value::CreateStringValue(expanded_value)); + prefs->SetValue(prefs::kPromptForDownload, + Value::CreateBooleanValue(false)); +} diff --git a/chrome/browser/download/download_dir_policy_handler.h b/chrome/browser/download/download_dir_policy_handler.h new file mode 100644 index 0000000000..3620ef6fed --- /dev/null +++ b/chrome/browser/download/download_dir_policy_handler.h @@ -0,0 +1,30 @@ +// Copyright 2013 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_DOWNLOAD_DOWNLOAD_DIR_POLICY_HANDLER_H_ +#define CHROME_BROWSER_DOWNLOAD_DOWNLOAD_DIR_POLICY_HANDLER_H_ + +#include "chrome/browser/policy/configuration_policy_handler.h" + +class PrefValueMap; + +namespace policy { +class PolicyMap; +} // namespace policy + +// ConfigurationPolicyHandler for the DownloadDirectory policy. +class DownloadDirPolicyHandler : public policy::TypeCheckingPolicyHandler { + public: + DownloadDirPolicyHandler(); + virtual ~DownloadDirPolicyHandler(); + + // ConfigurationPolicyHandler methods: + virtual void ApplyPolicySettings(const policy::PolicyMap& policies, + PrefValueMap* prefs) OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(DownloadDirPolicyHandler); +}; + +#endif // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_DIR_POLICY_HANDLER_H_ diff --git a/chrome/browser/download/download_dir_policy_handler_unittest.cc b/chrome/browser/download/download_dir_policy_handler_unittest.cc new file mode 100644 index 0000000000..c4ff387750 --- /dev/null +++ b/chrome/browser/download/download_dir_policy_handler_unittest.cc @@ -0,0 +1,35 @@ +// Copyright 2013 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/values.h" +#include "chrome/browser/download/download_dir_policy_handler.h" +#include "chrome/browser/download/download_prefs.h" +#include "chrome/browser/policy/configuration_policy_pref_store.h" +#include "chrome/browser/policy/configuration_policy_pref_store_unittest.h" +#include "chrome/browser/policy/policy_map.h" +#include "chrome/common/pref_names.h" +#include "policy/policy_constants.h" + +class DownloadDirPolicyHandlerTest + : public policy::ConfigurationPolicyPrefStoreTest {}; + +TEST_F(DownloadDirPolicyHandlerTest, SetDownloadDirectory) { + policy::PolicyMap policy; + EXPECT_FALSE(store_->GetValue(prefs::kPromptForDownload, NULL)); + policy.Set(policy::key::kDownloadDirectory, + policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, + base::Value::CreateStringValue(std::string()), + NULL); + UpdateProviderPolicy(policy); + + // Setting a DownloadDirectory should disable the PromptForDownload pref. + const base::Value* value = NULL; + EXPECT_TRUE(store_->GetValue(prefs::kPromptForDownload, &value)); + ASSERT_TRUE(value); + bool prompt_for_download = true; + bool result = value->GetAsBoolean(&prompt_for_download); + ASSERT_TRUE(result); + EXPECT_FALSE(prompt_for_download); +} diff --git a/chrome/browser/download/download_field_trial.cc b/chrome/browser/download/download_field_trial.cc deleted file mode 100644 index 58d76da584..0000000000 --- a/chrome/browser/download/download_field_trial.cc +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2013 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/download/download_field_trial.h" - -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" - -namespace { - -const char kCondition1Control[] = "Condition1Control"; -const char kCondition2Control[] = "Condition2Control"; -const char kCondition3Malicious[] = "Condition3Malicious"; -const char kCondition4Unsafe[] = "Condition4Unsafe"; -const char kCondition5Dangerous[] = "Condition5Dangerous"; -const char kCondition6Harmful[] = "Condition6Harmful"; -const char kCondition7DiscardSecond[] = "Condition7DiscardSecond"; -const char kCondition8DiscardFirst[] = "Condition8DiscardFirst"; -const char kCondition9SafeDiscard[] = "Condition9SafeDiscard"; -const char kCondition10SafeDontRun[] = "Condition10SafeDontRun"; - -} // namespace - -const char kMalwareWarningFinchTrialName[] = "MalwareDownloadWarning"; - -base::string16 AssembleMalwareFinchString( - const std::string& trial_condition, - const base::string16& elided_filename) { - // Sanity check to make sure we have a filename. - base::string16 filename; - if (elided_filename.empty()) { - filename = ASCIIToUTF16("This file"); - } else { - filename = ReplaceStringPlaceholders( - ASCIIToUTF16("File '$1'"), elided_filename, NULL); - } - - // Set the message text according to the condition. - if (trial_condition == kCondition1Control) { - return ASCIIToUTF16("This file appears malicious."); - } - base::string16 message_text; - if (trial_condition == kCondition2Control) { - message_text = ASCIIToUTF16("$1 appears malicious."); - } else if (trial_condition == kCondition3Malicious) { - message_text = ASCIIToUTF16("$1 is malicious."); - } else if (trial_condition == kCondition4Unsafe) { - message_text = ASCIIToUTF16("$1 is unsafe."); - } else if (trial_condition == kCondition5Dangerous) { - message_text = ASCIIToUTF16("$1 is dangerous."); - } else if (trial_condition == kCondition6Harmful) { - message_text = ASCIIToUTF16("$1 is harmful."); - } else if (trial_condition == kCondition7DiscardSecond) { - message_text = ASCIIToUTF16( - "$1 is malicious. Discard this file to stay safe."); - } else if (trial_condition == kCondition8DiscardFirst) { - message_text = ASCIIToUTF16( - "Discard this file to stay safe. $1 is malicious."); - } else if (trial_condition == kCondition9SafeDiscard) { - message_text = ASCIIToUTF16("$1 is malicious. To stay safe, discard it."); - } else if (trial_condition == kCondition10SafeDontRun) { - message_text = ASCIIToUTF16("$1 is malicious. To stay safe, don't run it."); - } else { - // We use the second control as a default for other conditions that don't - // change the warning string. - message_text = ASCIIToUTF16("$1 appears malicious."); - } - - return ReplaceStringPlaceholders(message_text, filename, NULL); -} diff --git a/chrome/browser/download/download_field_trial.h b/chrome/browser/download/download_field_trial.h deleted file mode 100644 index 8f5d721b2f..0000000000 --- a/chrome/browser/download/download_field_trial.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2013 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_DOWNLOAD_DOWNLOAD_FIELD_TRIAL_H_ -#define CHROME_BROWSER_DOWNLOAD_DOWNLOAD_FIELD_TRIAL_H_ - -#include <string> - -#include "base/strings/string16.h" - -// Summer/Fall 2013 Finch experiment strings --------------------------------- -// Only deployed to English speakers, don't need translation. - -extern const char kMalwareWarningFinchTrialName[]; - -// Helper for getting the appropriate message for a Finch trial. -// You should only invoke this if you believe you're in the kFinchTrialName -// finch trial; if you aren't, use the default string and don't invoke this. -base::string16 AssembleMalwareFinchString( - const std::string& trial_condition, - const base::string16& elided_filename); - -#endif // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_FIELD_TRIAL_H_ diff --git a/chrome/browser/download/download_field_trial_unittest.cc b/chrome/browser/download/download_field_trial_unittest.cc deleted file mode 100644 index 314497b6e4..0000000000 --- a/chrome/browser/download/download_field_trial_unittest.cc +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2013 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/download/download_field_trial.h" - -#include "base/strings/utf_string_conversions.h" -#include "testing/gtest/include/gtest/gtest.h" - -TEST(DownloadFieldTrialTest, FinchStrings) { - const std::string malicious_condition("Condition3Malicious"); - EXPECT_EQ(ASCIIToUTF16("This file is malicious."), - AssembleMalwareFinchString(malicious_condition, base::string16())); - EXPECT_EQ(ASCIIToUTF16("File 'malware.exe' is malicious."), - AssembleMalwareFinchString(malicious_condition, - ASCIIToUTF16("malware.exe"))); -} diff --git a/chrome/browser/download/download_item_model.cc b/chrome/browser/download/download_item_model.cc index d7983808ac..0823a40df3 100644 --- a/chrome/browser/download/download_item_model.cc +++ b/chrome/browser/download/download_item_model.cc @@ -13,7 +13,6 @@ #include "base/supports_user_data.h" #include "base/time/time.h" #include "chrome/browser/download/download_crx_util.h" -#include "chrome/browser/download/download_field_trial.h" #include "chrome/browser/safe_browsing/download_feedback_service.h" #include "content/public/browser/download_danger_type.h" #include "content/public/browser/download_interrupt_reasons.h" @@ -346,11 +345,7 @@ string16 DownloadItemModel::GetWarningText(const gfx::FontList& font_list, base_width); switch (download_->GetDangerType()) { case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: { - std::string trial_condition = - base::FieldTrialList::FindFullName(kMalwareWarningFinchTrialName); - if (trial_condition.empty()) - return l10n_util::GetStringUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_URL); - return AssembleMalwareFinchString(trial_condition, elided_filename); + return l10n_util::GetStringUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_URL); } case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: { if (download_crx_util::IsExtensionDownload(*download_)) { @@ -363,13 +358,8 @@ string16 DownloadItemModel::GetWarningText(const gfx::FontList& font_list, } case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT: case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: { - std::string trial_condition = - base::FieldTrialList::FindFullName(kMalwareWarningFinchTrialName); - if (trial_condition.empty()) { - return l10n_util::GetStringFUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT, - elided_filename); - } - return AssembleMalwareFinchString(trial_condition, elided_filename); + return l10n_util::GetStringFUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT, + elided_filename); } case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: { return l10n_util::GetStringFUTF16(IDS_PROMPT_UNCOMMON_DOWNLOAD_CONTENT, @@ -422,7 +412,7 @@ bool DownloadItemModel::IsDangerous() const { return download_->IsDangerous(); } -bool DownloadItemModel::IsMalicious() const { +bool DownloadItemModel::MightBeMalicious() const { if (!IsDangerous()) return false; switch (download_->GetDangerType()) { @@ -447,6 +437,33 @@ bool DownloadItemModel::IsMalicious() const { return false; } +// If you change this definition of malicious, also update +// DownloadManagerImpl::NonMaliciousInProgressCount. +bool DownloadItemModel::IsMalicious() const { + if (!MightBeMalicious()) + return false; + switch (download_->GetDangerType()) { + case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: + case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT: + case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: + return true; + + case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS: + case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT: + case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED: + case content::DOWNLOAD_DANGER_TYPE_MAX: + case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: + // We shouldn't get any of these due to the MightBeMalicious() test above. + NOTREACHED(); + // Fallthrough. + case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: + case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: + return false; + } + NOTREACHED(); + return false; +} + bool DownloadItemModel::ShouldAllowDownloadFeedback() const { if (!IsDangerous()) return false; diff --git a/chrome/browser/download/download_item_model.h b/chrome/browser/download/download_item_model.h index 62e937cab3..5a2c141ac5 100644 --- a/chrome/browser/download/download_item_model.h +++ b/chrome/browser/download/download_item_model.h @@ -81,6 +81,10 @@ class DownloadItemModel { bool IsDangerous() const; // Is this considered a malicious download? Implies IsDangerous(). + bool MightBeMalicious() const; + + // Is this considered a malicious download with very high confidence? + // Implies IsDangerous() and MightBeMalicious(). bool IsMalicious() const; // Is safe browsing download feedback feature available for this download? diff --git a/chrome/browser/download/download_service.cc b/chrome/browser/download/download_service.cc index bb75459fb5..d5f1f420e0 100644 --- a/chrome/browser/download/download_service.cc +++ b/chrome/browser/download/download_service.cc @@ -88,24 +88,26 @@ bool DownloadService::HasCreatedDownloadManager() { return download_manager_created_; } -int DownloadService::DownloadCount() const { +int DownloadService::NonMaliciousDownloadCount() const { if (!download_manager_created_) return 0; - return BrowserContext::GetDownloadManager(profile_)->InProgressCount(); + return BrowserContext::GetDownloadManager(profile_)-> + NonMaliciousInProgressCount(); } // static -int DownloadService::DownloadCountAllProfiles() { +int DownloadService::NonMaliciousDownloadCountAllProfiles() { std::vector<Profile*> profiles( g_browser_process->profile_manager()->GetLoadedProfiles()); int count = 0; for (std::vector<Profile*>::iterator it = profiles.begin(); it < profiles.end(); ++it) { - count += DownloadServiceFactory::GetForBrowserContext(*it)->DownloadCount(); + count += DownloadServiceFactory::GetForBrowserContext(*it)-> + NonMaliciousDownloadCount(); if ((*it)->HasOffTheRecordProfile()) count += DownloadServiceFactory::GetForBrowserContext( - (*it)->GetOffTheRecordProfile())->DownloadCount(); + (*it)->GetOffTheRecordProfile())->NonMaliciousDownloadCount(); } return count; diff --git a/chrome/browser/download/download_service.h b/chrome/browser/download/download_service.h index 3c3f6b6675..80cce15467 100644 --- a/chrome/browser/download/download_service.h +++ b/chrome/browser/download/download_service.h @@ -44,11 +44,12 @@ class DownloadService : public BrowserContextKeyedService { // Has a download manager been created? bool HasCreatedDownloadManager(); - // Number of downloads associated with this instance of the service. - int DownloadCount() const; + // Number of non-malicious downloads associated with this instance of the + // service. + int NonMaliciousDownloadCount() const; - // Number of downloads associated with all profiles. - static int DownloadCountAllProfiles(); + // Number of non-malicious downloads associated with all profiles. + static int NonMaliciousDownloadCountAllProfiles(); // Sets the DownloadManagerDelegate associated with this object and // its DownloadManager. Takes ownership of |delegate|, and destroys diff --git a/chrome/browser/download/download_shelf_context_menu.cc b/chrome/browser/download/download_shelf_context_menu.cc index 76de09f827..9bd9d6ba8f 100644 --- a/chrome/browser/download/download_shelf_context_menu.cc +++ b/chrome/browser/download/download_shelf_context_menu.cc @@ -55,10 +55,12 @@ ui::SimpleMenuModel* DownloadShelfContextMenu::GetMenuModel() { DownloadItemModel download_model(download_item_); // We shouldn't be opening a context menu for a dangerous download, unless it // is a malicious download. - DCHECK(!download_model.IsDangerous() || download_model.IsMalicious()); + DCHECK(!download_model.IsDangerous() || download_model.MightBeMalicious()); if (download_model.IsMalicious()) model = GetMaliciousMenuModel(); + else if (download_model.MightBeMalicious()) + model = GetMaybeMaliciousMenuModel(); else if (download_item_->GetState() == DownloadItem::COMPLETE) model = GetFinishedMenuModel(); else if (download_item_->GetState() == DownloadItem::INTERRUPTED) @@ -307,17 +309,39 @@ ui::SimpleMenuModel* DownloadShelfContextMenu::GetInterruptedMenuModel() { return interrupted_download_menu_model_.get(); } -ui::SimpleMenuModel* DownloadShelfContextMenu::GetMaliciousMenuModel() { +ui::SimpleMenuModel* DownloadShelfContextMenu::GetMaybeMaliciousMenuModel() { + if (maybe_malicious_download_menu_model_) + return maybe_malicious_download_menu_model_.get(); + + maybe_malicious_download_menu_model_.reset(new ui::SimpleMenuModel(this)); + + maybe_malicious_download_menu_model_->AddItemWithStringId( + DISCARD, IDS_DOWNLOAD_MENU_DISCARD); + maybe_malicious_download_menu_model_->AddItemWithStringId( + KEEP, IDS_DOWNLOAD_MENU_KEEP); + maybe_malicious_download_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); + maybe_malicious_download_menu_model_->AddItemWithStringId( + LEARN_MORE_SCANNING, IDS_DOWNLOAD_MENU_LEARN_MORE_SCANNING); + LOG(INFO) << "GetMaybeMaliciousMenuModel"; + return maybe_malicious_download_menu_model_.get(); +} + +ui::SimpleMenuModel* +DownloadShelfContextMenu::GetMaliciousMenuModel() { if (malicious_download_menu_model_) return malicious_download_menu_model_.get(); malicious_download_menu_model_.reset(new ui::SimpleMenuModel(this)); - malicious_download_menu_model_->AddItemWithStringId( - DISCARD, IDS_DOWNLOAD_MENU_DISCARD); - malicious_download_menu_model_->AddItemWithStringId( - KEEP, IDS_DOWNLOAD_MENU_KEEP); - malicious_download_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); + // If the primary action is "record & discard", this also puts a plain + // "discard" option in the dropdown. Otherwise this dropdown doesn't need it + // because there is no alternative besides "learn more". + DownloadItemModel download_model(download_item_); + if (download_model.ShouldAllowDownloadFeedback()) { + maybe_malicious_download_menu_model_->AddItemWithStringId( + DISCARD, IDS_DOWNLOAD_MENU_DISCARD); + } + LOG(INFO) << "GetMaliciousMenuModel"; malicious_download_menu_model_->AddItemWithStringId( LEARN_MORE_SCANNING, IDS_DOWNLOAD_MENU_LEARN_MORE_SCANNING); diff --git a/chrome/browser/download/download_shelf_context_menu.h b/chrome/browser/download/download_shelf_context_menu.h index b495c25e8e..345aaaf20b 100644 --- a/chrome/browser/download/download_shelf_context_menu.h +++ b/chrome/browser/download/download_shelf_context_menu.h @@ -69,6 +69,7 @@ class DownloadShelfContextMenu : public ui::SimpleMenuModel::Delegate, ui::SimpleMenuModel* GetInProgressMenuModel(); ui::SimpleMenuModel* GetFinishedMenuModel(); ui::SimpleMenuModel* GetInterruptedMenuModel(); + ui::SimpleMenuModel* GetMaybeMaliciousMenuModel(); ui::SimpleMenuModel* GetMaliciousMenuModel(); // We show slightly different menus if the download is in progress vs. if the @@ -76,6 +77,7 @@ class DownloadShelfContextMenu : public ui::SimpleMenuModel::Delegate, scoped_ptr<ui::SimpleMenuModel> in_progress_download_menu_model_; scoped_ptr<ui::SimpleMenuModel> finished_download_menu_model_; scoped_ptr<ui::SimpleMenuModel> interrupted_download_menu_model_; + scoped_ptr<ui::SimpleMenuModel> maybe_malicious_download_menu_model_; scoped_ptr<ui::SimpleMenuModel> malicious_download_menu_model_; // Information source. diff --git a/chrome/browser/drive/drive_api_service.cc b/chrome/browser/drive/drive_api_service.cc index 31a873ec02..ccd60f5b9a 100644 --- a/chrome/browser/drive/drive_api_service.cc +++ b/chrome/browser/drive/drive_api_service.cc @@ -84,11 +84,16 @@ const char kDriveAppsReadonlyScope[] = // Mime type to create a directory. const char kFolderMimeType[] = "application/vnd.google-apps.folder"; -// Expected max number of files resources in a http request. -// Be careful not to use something too small because it might overload the -// server. Be careful not to use something too large because it takes longer -// time to fetch the result without UI response. -const int kMaxNumFilesResourcePerRequest = 500; +// Max number of file entries to be fetched in a single http request. +// +// The larger the number is, +// - The total running time to fetch the whole file list will become shorter. +// - The running time for a single request tends to become longer. +// Since the file list fetching is a completely background task, for our side, +// only the total time matters. However, the server seems to have a time limit +// per single request, which disables us to set the largest value (1000). +// TODO(kinaba): make it larger when the server gets faster. +const int kMaxNumFilesResourcePerRequest = 250; const int kMaxNumFilesResourcePerRequestForSearch = 50; // For performance, we declare all fields we use. diff --git a/chrome/browser/drive/drive_api_util.cc b/chrome/browser/drive/drive_api_util.cc index bde7103ff9..c892aa05fb 100644 --- a/chrome/browser/drive/drive_api_util.cc +++ b/chrome/browser/drive/drive_api_util.cc @@ -33,6 +33,8 @@ const char kGooglePresentationMimeType[] = const char kGoogleSpreadsheetMimeType[] = "application/vnd.google-apps.spreadsheet"; const char kGoogleTableMimeType[] = "application/vnd.google-apps.table"; +const char kGoogleFormMimeType[] = "application/vnd.google-apps.form"; +const char kDriveFolderMimeType[] = "application/vnd.google-apps.folder"; ScopedVector<std::string> CopyScopedVectorString( const ScopedVector<std::string>& source) { @@ -120,6 +122,9 @@ ConvertInstalledAppToAppResource( return resource.Pass(); } +// Returns the argument string. +std::string Identity(const std::string& resource_id) { return resource_id; } + } // namespace @@ -231,6 +236,10 @@ std::string CanonicalizeResourceId(const std::string& resource_id) { return resource_id; } +ResourceIdCanonicalizer GetIdentityResourceIdCanonicalizer() { + return base::Bind(&Identity); +} + const char kDocsListScope[] = "https://docs.google.com/feeds/"; const char kDriveAppsScope[] = "https://www.googleapis.com/auth/drive.apps"; @@ -307,7 +316,10 @@ scoped_ptr<google_apis::FileResource> ConvertResourceEntryToFileResource( } file->set_download_url(entry.download_url()); - file->set_mime_type(entry.content_mime_type()); + if (entry.is_folder()) + file->set_mime_type(kDriveFolderMimeType); + else + file->set_mime_type(entry.content_mime_type()); file->set_md5_checksum(entry.file_md5()); file->set_file_size(entry.file_size()); @@ -378,6 +390,8 @@ google_apis::DriveEntryKind GetKind( return google_apis::ENTRY_KIND_DRAWING; if (mime_type == kGoogleTableMimeType) return google_apis::ENTRY_KIND_TABLE; + if (mime_type == kGoogleFormMimeType) + return google_apis::ENTRY_KIND_FORM; if (mime_type == "application/pdf") return google_apis::ENTRY_KIND_PDF; return google_apis::ENTRY_KIND_FILE; diff --git a/chrome/browser/drive/drive_api_util.h b/chrome/browser/drive/drive_api_util.h index 3bef862a73..fe741ee980 100644 --- a/chrome/browser/drive/drive_api_util.h +++ b/chrome/browser/drive/drive_api_util.h @@ -8,6 +8,7 @@ #include <string> #include "base/memory/scoped_ptr.h" +#include "chrome/browser/drive/drive_service_interface.h" #include "chrome/browser/google_apis/drive_common_callbacks.h" #include "chrome/browser/google_apis/drive_entry_kinds.h" #include "chrome/browser/google_apis/gdata_errorcode.h" @@ -61,6 +62,9 @@ std::string ExtractResourceIdFromUrl(const GURL& url); // into the new format. std::string CanonicalizeResourceId(const std::string& resource_id); +// Returns a ResourceIdCanonicalizer which returns the argument. +ResourceIdCanonicalizer GetIdentityResourceIdCanonicalizer(); + // Note: Following constants and a function are used to support GetShareUrl on // Drive API v2. Unfortunately, there is no support on Drive API v2, so we need // to fall back to GData WAPI for the GetShareUrl. Thus, these are shared by diff --git a/chrome/browser/drive/drive_uploader.cc b/chrome/browser/drive/drive_uploader.cc index 43d434f948..30ab0b9c1b 100644 --- a/chrome/browser/drive/drive_uploader.cc +++ b/chrome/browser/drive/drive_uploader.cc @@ -287,12 +287,8 @@ void DriveUploader::OnUploadLocationReceived( << "] for [" << upload_file_info->file_path.value() << "]"; if (code != HTTP_SUCCESS) { - // TODO(achuith): Handle error codes from Google Docs server. - if (code == HTTP_PRECONDITION) { - // ETag mismatch. - UploadFailed(upload_file_info.Pass(), HTTP_CONFLICT); - return; - } + if (code == HTTP_PRECONDITION) + code = HTTP_CONFLICT; // ETag mismatch. UploadFailed(upload_file_info.Pass(), code); return; } diff --git a/chrome/browser/drive/dummy_drive_service.cc b/chrome/browser/drive/dummy_drive_service.cc index de84f86202..14afaecd02 100644 --- a/chrome/browser/drive/dummy_drive_service.cc +++ b/chrome/browser/drive/dummy_drive_service.cc @@ -5,6 +5,7 @@ #include "chrome/browser/drive/dummy_drive_service.h" #include "base/bind.h" +#include "chrome/browser/drive/drive_api_util.h" using google_apis::AboutResourceCallback; using google_apis::AppListCallback; @@ -23,13 +24,6 @@ using google_apis::UploadRangeCallback; namespace drive { -namespace { - -// Returns the argument string. -std::string Identity(const std::string& resource_id) { return resource_id; } - -} // namespace - DummyDriveService::DummyDriveService() {} DummyDriveService::~DummyDriveService() {} @@ -43,7 +37,7 @@ void DummyDriveService::RemoveObserver(DriveServiceObserver* observer) {} bool DummyDriveService::CanSendRequest() const { return true; } ResourceIdCanonicalizer DummyDriveService::GetResourceIdCanonicalizer() const { - return base::Bind(&Identity); + return util::GetIdentityResourceIdCanonicalizer(); } bool DummyDriveService::HasAccessToken() const { return true; } diff --git a/chrome/browser/drive/fake_drive_service.cc b/chrome/browser/drive/fake_drive_service.cc index e6611efed9..352e517b2f 100644 --- a/chrome/browser/drive/fake_drive_service.cc +++ b/chrome/browser/drive/fake_drive_service.cc @@ -132,9 +132,6 @@ void EntryActionCallbackAdapter( callback.Run(error); } -// Returns the argument string. -std::string Identity(const std::string& resource_id) { return resource_id; } - } // namespace struct FakeDriveService::UploadSession { @@ -296,7 +293,7 @@ bool FakeDriveService::CanSendRequest() const { } ResourceIdCanonicalizer FakeDriveService::GetResourceIdCanonicalizer() const { - return base::Bind(&Identity); + return util::GetIdentityResourceIdCanonicalizer(); } bool FakeDriveService::HasAccessToken() const { diff --git a/chrome/browser/drive/fake_drive_service_unittest.cc b/chrome/browser/drive/fake_drive_service_unittest.cc index 453a914501..128fdf7f8b 100644 --- a/chrome/browser/drive/fake_drive_service_unittest.cc +++ b/chrome/browser/drive/fake_drive_service_unittest.cc @@ -104,10 +104,6 @@ class FakeDriveServiceTest : public testing::Test { FakeDriveService fake_service_; }; -void AppendProgressCallbackResult(std::vector<int64>* values, int64 progress) { - values->push_back(progress); -} - TEST_F(FakeDriveServiceTest, GetAllResourceList) { ASSERT_TRUE(fake_service_.LoadResourceListForWapi( "gdata/root_feed.json")); diff --git a/chrome/browser/drive/gdata_wapi_service.cc b/chrome/browser/drive/gdata_wapi_service.cc index efe3bea999..963c6ae569 100644 --- a/chrome/browser/drive/gdata_wapi_service.cc +++ b/chrome/browser/drive/gdata_wapi_service.cc @@ -123,9 +123,6 @@ void ConvertAppListAndRun( callback.Run(error, app_list.Pass()); } -// Returns the argument string. -std::string Identity(const std::string& resource_id) { return resource_id; } - } // namespace GDataWapiService::GDataWapiService( @@ -185,7 +182,7 @@ bool GDataWapiService::CanSendRequest() const { } ResourceIdCanonicalizer GDataWapiService::GetResourceIdCanonicalizer() const { - return base::Bind(&Identity); + return util::GetIdentityResourceIdCanonicalizer(); } std::string GDataWapiService::GetRootResourceId() const { diff --git a/chrome/browser/errorpage_browsertest.cc b/chrome/browser/errorpage_browsertest.cc index 020a0f1d43..15a1590863 100644 --- a/chrome/browser/errorpage_browsertest.cc +++ b/chrome/browser/errorpage_browsertest.cc @@ -129,6 +129,7 @@ class TestFailProvisionalLoadObserver : public content::WebContentsObserver { // This method is invoked when the provisional load failed. virtual void DidFailProvisionalLoad( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& validated_url, int error_code, diff --git a/chrome/browser/extensions/active_tab_apitest.cc b/chrome/browser/extensions/active_tab_apitest.cc index d6a6d1daa2..1ec7e32c05 100644 --- a/chrome/browser/extensions/active_tab_apitest.cc +++ b/chrome/browser/extensions/active_tab_apitest.cc @@ -43,7 +43,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_ActiveTab) { // Granting to the extension should give it access to page.html. { ResultCatcher catcher; - service->toolbar_model()->ExecuteBrowserAction(extension, browser(), NULL); + service->toolbar_model()->ExecuteBrowserAction( + extension, browser(), NULL, true); EXPECT_TRUE(catcher.GetNextResult()) << message_; } diff --git a/chrome/browser/extensions/activity_log/activity_log.cc b/chrome/browser/extensions/activity_log/activity_log.cc index c0fb023a83..1e8b34aeda 100644 --- a/chrome/browser/extensions/activity_log/activity_log.cc +++ b/chrome/browser/extensions/activity_log/activity_log.cc @@ -47,6 +47,10 @@ namespace { using extensions::Action; using constants::kArgUrlPlaceholder; +// If DOM API methods start with this string, we flag them as being of type +// DomActionType::XHR. +const char kDomXhrPrefix[] = "XMLHttpRequest."; + // Specifies a possible action to take to get an extracted URL in the ApiInfo // structure below. enum Transformation { @@ -532,6 +536,18 @@ void ActivityLog::LogAction(scoped_refptr<Action> action) { // mask out incognito URLs if appropriate. ExtractUrls(action, profile_); + // Mark DOM XHR requests as such, for easier processing later. + if (action->action_type() == Action::ACTION_DOM_ACCESS && + StartsWithASCII(action->api_name(), kDomXhrPrefix, true) && + action->other()) { + DictionaryValue* other = action->mutable_other(); + int dom_verb = -1; + if (other->GetInteger(constants::kActionDomVerb, &dom_verb) && + dom_verb == DomActionType::METHOD) { + other->SetInteger(constants::kActionDomVerb, DomActionType::XHR); + } + } + if (uma_policy_) uma_policy_->ProcessAction(action); if (IsDatabaseEnabled() && database_policy_) diff --git a/chrome/browser/extensions/activity_log/activity_log_unittest.cc b/chrome/browser/extensions/activity_log/activity_log_unittest.cc index 664c679939..bfb7748386 100644 --- a/chrome/browser/extensions/activity_log/activity_log_unittest.cc +++ b/chrome/browser/extensions/activity_log/activity_log_unittest.cc @@ -7,6 +7,7 @@ #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/synchronization/waitable_event.h" +#include "chrome/browser/extensions/activity_log/activity_action_constants.h" #include "chrome/browser/extensions/activity_log/activity_log.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/test_extension_system.h" @@ -110,12 +111,23 @@ class ActivityLogTest : public ChromeRenderViewHostTestHarness { static void RetrieveActions_ArgUrlExtraction( scoped_ptr<std::vector<scoped_refptr<Action> > > i) { + const DictionaryValue* other = NULL; + int dom_verb = -1; + ASSERT_EQ(4U, i->size()); scoped_refptr<Action> action = i->at(0); ASSERT_EQ("XMLHttpRequest.open", action->api_name()); ASSERT_EQ("[\"POST\",\"\\u003Carg_url\\u003E\"]", ActivityLogPolicy::Util::Serialize(action->args())); ASSERT_EQ("http://api.google.com/", action->arg_url().spec()); + // Test that the dom_verb field was changed to XHR (from METHOD). This + // could be tested on all retrieved XHR actions but it would be redundant, + // so just test once. + other = action->other(); + ASSERT_TRUE(other); + ASSERT_TRUE(other->GetInteger(activity_log_constants::kActionDomVerb, + &dom_verb)); + ASSERT_EQ(DomActionType::XHR, dom_verb); action = i->at(1); ASSERT_EQ("XMLHttpRequest.open", action->api_name()); @@ -241,6 +253,8 @@ TEST_F(ActivityLogTest, ArgUrlExtraction) { action->set_page_url(GURL("http://www.google.com/")); action->mutable_args()->AppendString("POST"); action->mutable_args()->AppendString("http://api.google.com/"); + action->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb, + DomActionType::METHOD); activity_log->LogAction(action); // Submit a DOM API call with a relative URL in the argument, which should be @@ -252,6 +266,8 @@ TEST_F(ActivityLogTest, ArgUrlExtraction) { action->set_page_url(GURL("http://www.google.com/")); action->mutable_args()->AppendString("POST"); action->mutable_args()->AppendString("/api/"); + action->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb, + DomActionType::METHOD); activity_log->LogAction(action); // Submit a DOM API call with a relative URL but no base page URL against @@ -262,6 +278,8 @@ TEST_F(ActivityLogTest, ArgUrlExtraction) { "XMLHttpRequest.open"); action->mutable_args()->AppendString("POST"); action->mutable_args()->AppendString("/api/"); + action->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb, + DomActionType::METHOD); activity_log->LogAction(action); // Submit an API call with an embedded URL. diff --git a/chrome/browser/extensions/api/api_resource.cc b/chrome/browser/extensions/api/api_resource.cc index ccbdd77e7f..c8ddd4025a 100644 --- a/chrome/browser/extensions/api/api_resource.cc +++ b/chrome/browser/extensions/api/api_resource.cc @@ -15,7 +15,7 @@ ApiResource::ApiResource(const std::string& owner_extension_id) ApiResource::~ApiResource() { } -bool ApiResource::persistent() const { +bool ApiResource::IsPersistent() const { return true; // backward-compatible behavior. } diff --git a/chrome/browser/extensions/api/api_resource.h b/chrome/browser/extensions/api/api_resource.h index cc4e3ff626..2911cf3e12 100644 --- a/chrome/browser/extensions/api/api_resource.h +++ b/chrome/browser/extensions/api/api_resource.h @@ -23,7 +23,9 @@ class ApiResource { return owner_extension_id_; } - virtual bool persistent() const; + // If this method returns |true|, the resource remains open when the + // owning extension is suspended due to inactivity. + virtual bool IsPersistent() const; static const content::BrowserThread::ID kThreadId = content::BrowserThread::IO; diff --git a/chrome/browser/extensions/api/api_resource_manager.h b/chrome/browser/extensions/api/api_resource_manager.h index d39c41b35a..fab663af64 100644 --- a/chrome/browser/extensions/api/api_resource_manager.h +++ b/chrome/browser/extensions/api/api_resource_manager.h @@ -24,6 +24,7 @@ namespace extensions { namespace api { +class TCPServerSocketEventDispatcher; class TCPSocketEventDispatcher; class UDPSocketEventDispatcher; } @@ -153,6 +154,7 @@ class ApiResourceManager : public ProfileKeyedAPI, } private: + friend class api::TCPServerSocketEventDispatcher; friend class api::TCPSocketEventDispatcher; friend class api::UDPSocketEventDispatcher; friend class ProfileKeyedAPIFactory<ApiResourceManager<T> >; @@ -286,7 +288,7 @@ class ApiResourceManager : public ProfileKeyedAPI, } else { linked_ptr<T> ptr = api_resource_map_[*it]; T* resource = ptr.get(); - erase = (resource && !resource->persistent()); + erase = (resource && !resource->IsPersistent()); } if (erase) { diff --git a/chrome/browser/extensions/api/cast_channel/cast_channel_api.cc b/chrome/browser/extensions/api/cast_channel/cast_channel_api.cc index 4f216c9dfe..8a5db589a2 100644 --- a/chrome/browser/extensions/api/cast_channel/cast_channel_api.cc +++ b/chrome/browser/extensions/api/cast_channel/cast_channel_api.cc @@ -4,6 +4,7 @@ #include "chrome/browser/extensions/api/cast_channel/cast_channel_api.h" +#include "base/json/json_writer.h" #include "base/values.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/extensions/api/cast_channel/cast_socket.h" @@ -30,11 +31,20 @@ using cast_channel::ReadyState; using content::BrowserThread; namespace { -const long kUnknownChannelId = -1; + +// T is an extension dictionary (MessageInfo or ChannelInfo) +template <class T> +std::string ParamToString(const T& info) { + scoped_ptr<base::DictionaryValue> dict = info.ToValue(); + std::string out; + base::JSONWriter::Write(dict.get(), &out); + return out; +} + } // namespace CastChannelAPI::CastChannelAPI(Profile* profile) - : profile_(profile) { + : profile_(profile) { DCHECK(profile_); } @@ -75,6 +85,8 @@ void CastChannelAPI::OnMessage(const CastSocket* socket, socket->FillChannelInfo(&channel_info); scoped_ptr<base::ListValue> results = OnMessage::Create(channel_info, message_info); + DVLOG(1) << "Sending message " << ParamToString(message_info) + << " to channel " << ParamToString(channel_info); scoped_ptr<Event> event(new Event(OnMessage::kEventName, results.Pass())); extensions::ExtensionSystem::Get(profile_)->event_router()-> DispatchEventToExtension(socket->owner_extension_id(), event.Pass()); @@ -83,8 +95,7 @@ void CastChannelAPI::OnMessage(const CastSocket* socket, CastChannelAPI::~CastChannelAPI() {} CastChannelAsyncApiFunction::CastChannelAsyncApiFunction() - : socket_(NULL), channel_id_(kUnknownChannelId), manager_(NULL), - error_(cast_channel::CHANNEL_ERROR_NONE) { } + : manager_(NULL), error_(cast_channel::CHANNEL_ERROR_NONE) { } CastChannelAsyncApiFunction::~CastChannelAsyncApiFunction() { } @@ -97,64 +108,62 @@ bool CastChannelAsyncApiFunction::Respond() { return error_ != cast_channel::CHANNEL_ERROR_NONE; } -ApiResourceManager<api::cast_channel::CastSocket>* -CastChannelAsyncApiFunction::GetSocketManager() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - return manager_; -} - -CastSocket* CastChannelAsyncApiFunction::GetSocket(long channel_id) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DCHECK(!socket_); - DCHECK_EQ(channel_id_, kUnknownChannelId); - CastSocket* socket = GetSocketManager()->Get(extension_->id(), channel_id); - if (socket) { - socket_ = socket; - channel_id_ = channel_id; +CastSocket* CastChannelAsyncApiFunction::GetSocketOrCompleteWithError( + int channel_id) { + CastSocket* socket = GetSocket(channel_id); + if (!socket) { + SetResultFromError(cast_channel::CHANNEL_ERROR_INVALID_CHANNEL_ID); + AsyncWorkCompleted(); } return socket; } -void CastChannelAsyncApiFunction::RemoveSocketIfError() { - if (error_ != cast_channel::CHANNEL_ERROR_NONE) - RemoveSocket(); -} - -void CastChannelAsyncApiFunction::RemoveSocket() { +int CastChannelAsyncApiFunction::AddSocket(CastSocket* socket) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DCHECK(socket_); - DCHECK(channel_id_ != kUnknownChannelId); - GetSocketManager()->Remove(extension_->id(), channel_id_); - channel_id_ = kUnknownChannelId; - socket_ = NULL; + DCHECK(socket); + DCHECK(manager_); + return manager_->Add(socket); } -void CastChannelAsyncApiFunction::SetResultFromChannelInfo( - const ChannelInfo& channel_info) { +void CastChannelAsyncApiFunction::RemoveSocket(int channel_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - SetResult(channel_info.ToValue().release()); + DCHECK(manager_); + manager_->Remove(extension_->id(), channel_id); } -void CastChannelAsyncApiFunction::SetResultFromSocket() { - DCHECK(socket_); +void CastChannelAsyncApiFunction::SetResultFromSocket(int channel_id) { + CastSocket* socket = GetSocket(channel_id); + DCHECK(socket); ChannelInfo channel_info; - socket_->FillChannelInfo(&channel_info); - error_ = socket_->error_state(); + socket->FillChannelInfo(&channel_info); + error_ = socket->error_state(); SetResultFromChannelInfo(channel_info); } -void CastChannelAsyncApiFunction::SetResultFromError( - const std::string& url, ChannelError error) { +void CastChannelAsyncApiFunction::SetResultFromError(ChannelError error) { ChannelInfo channel_info; - channel_info.channel_id = channel_id_; - channel_info.url = url; + channel_info.channel_id = -1; + channel_info.url = ""; channel_info.ready_state = cast_channel::READY_STATE_CLOSED; channel_info.error_state = error; SetResultFromChannelInfo(channel_info); error_ = error; } -CastChannelOpenFunction::CastChannelOpenFunction() { } +CastSocket* CastChannelAsyncApiFunction::GetSocket(int channel_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(manager_); + return manager_->Get(extension_->id(), channel_id); +} + +void CastChannelAsyncApiFunction::SetResultFromChannelInfo( + const ChannelInfo& channel_info) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + SetResult(channel_info.ToValue().release()); +} + +CastChannelOpenFunction::CastChannelOpenFunction() + : new_channel_id_(0) { } CastChannelOpenFunction::~CastChannelOpenFunction() { } @@ -171,21 +180,16 @@ bool CastChannelOpenFunction::Prepare() { void CastChannelOpenFunction::AsyncWorkStart() { DCHECK(api_); - socket_ = new CastSocket(extension_->id(), GURL(params_->url), - api_, g_browser_process->net_log()); - socket_->Connect(base::Bind(&CastChannelOpenFunction::OnOpen, this)); + CastSocket* socket = new CastSocket(extension_->id(), GURL(params_->url), + api_, g_browser_process->net_log()); + int new_channel_id = AddSocket(socket); + socket->set_id(new_channel_id); + socket->Connect(base::Bind(&CastChannelOpenFunction::OnOpen, this)); } void CastChannelOpenFunction::OnOpen(int result) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - if (result == net::OK) { - socket_->set_id(GetSocketManager()->Add(socket_)); - SetResultFromSocket(); - } else { - SetResultFromError(params_->url, - cast_channel::CHANNEL_ERROR_CONNECT_ERROR); - } - RemoveSocketIfError(); + SetResultFromSocket(new_channel_id_); AsyncWorkCompleted(); } @@ -200,25 +204,20 @@ bool CastChannelSendFunction::Prepare() { } void CastChannelSendFunction::AsyncWorkStart() { - if (!GetSocket(params_->channel.channel_id)) { - SetResultFromError("", - cast_channel::CHANNEL_ERROR_INVALID_CHANNEL_ID); - AsyncWorkCompleted(); - return; - } - socket_->SendMessage(params_->message, - base::Bind(&CastChannelSendFunction::OnSend, this)); + CastSocket* socket = GetSocketOrCompleteWithError( + params_->channel.channel_id); + if (socket) + socket->SendMessage(params_->message, + base::Bind(&CastChannelSendFunction::OnSend, this)); } void CastChannelSendFunction::OnSend(int result) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (result < 0) { - SetResultFromError("", - cast_channel::CHANNEL_ERROR_SOCKET_ERROR); + SetResultFromError(cast_channel::CHANNEL_ERROR_SOCKET_ERROR); } else { - SetResultFromSocket(); + SetResultFromSocket(params_->channel.channel_id); } - RemoveSocketIfError(); AsyncWorkCompleted(); } @@ -233,23 +232,21 @@ bool CastChannelCloseFunction::Prepare() { } void CastChannelCloseFunction::AsyncWorkStart() { - if (!GetSocket(params_->channel.channel_id)) { - SetResultFromError("", cast_channel::CHANNEL_ERROR_INVALID_CHANNEL_ID); - AsyncWorkCompleted(); - return; - } - socket_->Close(base::Bind(&CastChannelCloseFunction::OnClose, this)); + CastSocket* socket = GetSocketOrCompleteWithError( + params_->channel.channel_id); + if (socket) + socket->Close(base::Bind(&CastChannelCloseFunction::OnClose, this)); } void CastChannelCloseFunction::OnClose(int result) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DVLOG(1) << "CastChannelCloseFunction::OnClose result = " << result; if (result < 0) { - SetResultFromError("", cast_channel::CHANNEL_ERROR_SOCKET_ERROR); - RemoveSocketIfError(); + SetResultFromError(cast_channel::CHANNEL_ERROR_SOCKET_ERROR); } else { - SetResultFromSocket(); - RemoveSocket(); + int channel_id = params_->channel.channel_id; + SetResultFromSocket(channel_id); + RemoveSocket(channel_id); } AsyncWorkCompleted(); } diff --git a/chrome/browser/extensions/api/cast_channel/cast_channel_api.h b/chrome/browser/extensions/api/cast_channel/cast_channel_api.h index fe0b8878d6..5aea438d41 100644 --- a/chrome/browser/extensions/api/cast_channel/cast_channel_api.h +++ b/chrome/browser/extensions/api/cast_channel/cast_channel_api.h @@ -63,40 +63,38 @@ class CastChannelAsyncApiFunction : public AsyncApiFunction { virtual bool PrePrepare() OVERRIDE; virtual bool Respond() OVERRIDE; - ApiResourceManager<cast_channel::CastSocket>* - GetSocketManager(); + // Returns the socket corresponding to |channel_id| if one exists. Otherwise, + // sets the function result with CHANNEL_ERROR_INVALID_CHANNEL_ID, completes + // the function, and returns null. + cast_channel::CastSocket* GetSocketOrCompleteWithError(int channel_id); - // Returns the socket corresponding to |channel_id| if one exists. If found, - // sets |socket_| and |channel_id_|. - cast_channel::CastSocket* GetSocket(long channel_id); + // Adds |socket| to |manager_| and returns the new channel_id. |manager_| + // assumes ownership of |socket|. + int AddSocket(cast_channel::CastSocket* socket); - // Sets the ChannelInfo result from the state of the CastSocket used by the - // function. - void SetResultFromSocket(); + // Removes the CastSocket corresponding to |channel_id| from the resource + // manager. + void RemoveSocket(int channel_id); - // Sets the ChannelInfo result from |url| and |error|, when there is no - // CastSocket associated with the function. - void SetResultFromError(const std::string& url, - cast_channel::ChannelError error); + // Sets the function result to a ChannelInfo obtained from the state of the + // CastSocket corresponding to |channel_id|. + void SetResultFromSocket(int channel_id); - // Destroys the CastSocket used by the function. - void RemoveSocket(); - - // Destroys the CastSocket used by the function, but only when an error - // has occurred.. - void RemoveSocketIfError(); - - // The socket being used by the function. Set lazily. - cast_channel::CastSocket* socket_; - - // The id of the socket being used by the function. Set lazily. - long channel_id_; + // Sets the function result to a ChannelInfo with |error|. + void SetResultFromError(cast_channel::ChannelError error); private: + // Returns the socket corresponding to |channel_id| if one exists, or null + // otherwise. + cast_channel::CastSocket* GetSocket(int channel_id); + + // Sets the function result from |channel_info|. void SetResultFromChannelInfo( const cast_channel::ChannelInfo& channel_info); + // The API resource manager for CastSockets. ApiResourceManager<cast_channel::CastSocket>* manager_; + // The result of the function. cast_channel::ChannelError error_; }; @@ -119,7 +117,8 @@ class CastChannelOpenFunction : public CastChannelAsyncApiFunction { void OnOpen(int result); scoped_ptr<cast_channel::Open::Params> params_; - // Ptr to the API object. + // The id of the newly opened socket. + int new_channel_id_; CastChannelAPI* api_; DISALLOW_COPY_AND_ASSIGN(CastChannelOpenFunction); diff --git a/chrome/browser/extensions/api/cast_channel/cast_socket.cc b/chrome/browser/extensions/api/cast_channel/cast_socket.cc index a4e7b18fb7..e384587d4f 100644 --- a/chrome/browser/extensions/api/cast_channel/cast_socket.cc +++ b/chrome/browser/extensions/api/cast_channel/cast_socket.cc @@ -7,15 +7,26 @@ #include <string.h> #include "base/bind.h" +#include "base/callback_helpers.h" #include "base/lazy_instance.h" #include "base/strings/string_number_conversions.h" #include "base/sys_byteorder.h" #include "chrome/browser/extensions/api/cast_channel/cast_channel.pb.h" #include "chrome/browser/extensions/api/cast_channel/cast_message_util.h" #include "net/base/address_list.h" +#include "net/base/host_port_pair.h" #include "net/base/net_errors.h" #include "net/base/net_util.h" +#include "net/cert/cert_verifier.h" +#include "net/cert/x509_certificate.h" +#include "net/http/transport_security_state.h" +#include "net/socket/client_socket_factory.h" +#include "net/socket/client_socket_handle.h" +#include "net/socket/ssl_client_socket.h" +#include "net/socket/stream_socket.h" #include "net/socket/tcp_client_socket.h" +#include "net/ssl/ssl_config_service.h" +#include "net/ssl/ssl_info.h" namespace { @@ -56,10 +67,18 @@ const uint32 kMaxMessageSize = 65536; CastSocket::CastSocket(const std::string& owner_extension_id, const GURL& url, CastSocket::Delegate* delegate, net::NetLog* net_log) : - ApiResource(owner_extension_id), channel_id_(0), url_(url), - delegate_(delegate), error_state_(CHANNEL_ERROR_NONE), - ready_state_(READY_STATE_NONE), write_callback_pending_(false), - read_callback_pending_(false), current_message_size_(0), net_log_(net_log) { + ApiResource(owner_extension_id), + channel_id_(0), + url_(url), + delegate_(delegate), + is_secure_(false), + error_state_(CHANNEL_ERROR_NONE), + ready_state_(READY_STATE_NONE), + write_callback_pending_(false), + read_callback_pending_(false), + current_message_size_(0), + net_log_(net_log), + next_state_(CONN_STATE_NONE) { DCHECK(net_log_); net_log_source_.type = net::NetLog::SOURCE_SOCKET; net_log_source_.id = net_log_->NextID(); @@ -78,12 +97,59 @@ const GURL& CastSocket::url() const { return url_; } -net::TCPClientSocket* CastSocket::CreateSocket( - const net::AddressList& addresses, - net::NetLog* net_log, - const net::NetLog::Source& source) { - // TODO(mfoltz,munjal): Create a TLS socket. - return new net::TCPClientSocket(addresses, net_log, source); +bool CastSocket::ExtractPeerCert(std::string* cert) { + CHECK(peer_cert_.empty()); + net::SSLInfo ssl_info; + if (!socket_->GetSSLInfo(&ssl_info) || !ssl_info.cert.get()) + return false; + bool result = net::X509Certificate::GetDEREncoded( + ssl_info.cert->os_cert_handle(), cert); + if (result) + DVLOG(1) << "Successfully extracted peer certificate: " << *cert; + return result; +} + +scoped_ptr<net::TCPClientSocket> CastSocket::CreateTcpSocket() { + net::AddressList addresses(ip_endpoint_); + scoped_ptr<net::TCPClientSocket> tcp_socket( + new net::TCPClientSocket(addresses, net_log_, net_log_source_)); + // Enable keepalive + tcp_socket->SetKeepAlive(true, kTcpKeepAliveDelaySecs); + return tcp_socket.Pass(); +} + +scoped_ptr<net::SSLClientSocket> CastSocket::CreateSslSocket() { + net::SSLConfig ssl_config; + // If a peer cert was extracted in a previous attempt to connect, then + // whitelist that cert. + if (!peer_cert_.empty()) { + net::SSLConfig::CertAndStatus cert_and_status; + cert_and_status.cert_status = net::CERT_STATUS_AUTHORITY_INVALID; + cert_and_status.der_cert = peer_cert_; + ssl_config.allowed_bad_certs.push_back(cert_and_status); + } + + cert_verifier_.reset(net::CertVerifier::CreateDefault()); + transport_security_state_.reset(new net::TransportSecurityState); + net::SSLClientSocketContext context; + // CertVerifier and TransportSecurityState are owned by us, not the + // context object. + context.cert_verifier = cert_verifier_.get(); + context.transport_security_state = transport_security_state_.get(); + + scoped_ptr<net::ClientSocketHandle> connection(new net::ClientSocketHandle); + connection->SetSocket(tcp_socket_.PassAs<net::StreamSocket>()); + net::HostPortPair host_and_port = net::HostPortPair::FromIPEndPoint( + ip_endpoint_); + + return net::ClientSocketFactory::GetDefaultFactory()->CreateSSLClientSocket( + connection.Pass(), host_and_port, ssl_config, context); +} + +void CastSocket::OnConnectComplete(int result) { + int rv = DoConnectLoop(result); + if (rv != net::ERR_IO_PENDING) + DoConnectCallback(rv); } void CastSocket::Connect(const net::CompletionCallback& callback) { @@ -100,34 +166,103 @@ void CastSocket::Connect(const net::CompletionCallback& callback) { callback.Run(result); return; } - ready_state_ = READY_STATE_CONNECTING; - net::AddressList address_list(ip_endpoint_); - socket_.reset(CreateSocket(address_list, net_log_, net_log_source_)); - - // Enable TCP_NODELAY, as we want to minimize message latency. - socket_->SetNoDelay(true); - // Enable keepalive - socket_->SetKeepAlive(true, kTcpKeepAliveDelaySecs); connect_callback_ = callback; - socket_->Connect(base::Bind(&CastSocket::OnConnectComplete, - AsWeakPtr())); + next_state_ = CONN_STATE_TCP_CONNECT; + int rv = DoConnectLoop(net::OK); + if (rv != net::ERR_IO_PENDING) + DoConnectCallback(rv); } -void CastSocket::OnConnectComplete(int result) { - DCHECK(CalledOnValidThread()); - DVLOG(1) << "OnConnectComplete result = " << result; - ready_state_ = (result == net::OK) ? - READY_STATE_OPEN : READY_STATE_CLOSED; - connect_callback_.Run(result); - connect_callback_.Reset(); +// This method performs the state machine transitions for connection flow. +// There are two entry points to this method: +// 1. public Connect method: this starts the flow +// 2. OnConnectComplete: callback method called when an async operation +// is done. OnConnectComplete calls this method to continue the state +// machine transitions. +int CastSocket::DoConnectLoop(int result) { + // Network operations can either finish sycnronously or asynchronously. + // This method executes the state machine transitions in a loop so that + // correct state transitions happen even when network operations finish + // synchronously. + int rv = result; + do { + ConnectionState state = next_state_; + // All the Do* methods do not set next_state_ in case of an + // error. So set next_state_ to NONE to figure out if the Do* + // method changed state or not. + next_state_ = CONN_STATE_NONE; + switch (state) { + case CONN_STATE_TCP_CONNECT: + rv = DoTcpConnect(); + break; + case CONN_STATE_TCP_CONNECT_COMPLETE: + rv = DoTcpConnectComplete(rv); + break; + case CONN_STATE_SSL_CONNECT: + DCHECK_EQ(net::OK, rv); + rv = DoSslConnect(); + break; + case CONN_STATE_SSL_CONNECT_COMPLETE: + rv = DoSslConnectComplete(rv); + break; + default: + NOTREACHED() << "BUG in CastSocket state machine code"; + break; + } + } while (rv != net::ERR_IO_PENDING && next_state_ != CONN_STATE_NONE); + // Get out of the loop either when: + // a. A network operation is pending, OR + // b. The Do* method called did not change state + + return rv; +} + +int CastSocket::DoTcpConnect() { + next_state_ = CONN_STATE_TCP_CONNECT_COMPLETE; + tcp_socket_ = CreateTcpSocket(); + return tcp_socket_->Connect( + base::Bind(&CastSocket::OnConnectComplete, AsWeakPtr())); +} + +int CastSocket::DoTcpConnectComplete(int result) { + if (result == net::OK) + next_state_ = CONN_STATE_SSL_CONNECT; + return result; +} + +int CastSocket::DoSslConnect() { + next_state_ = CONN_STATE_SSL_CONNECT_COMPLETE; + socket_ = CreateSslSocket(); + return socket_->Connect( + base::Bind(&CastSocket::OnConnectComplete, AsWeakPtr())); +} + +int CastSocket::DoSslConnectComplete(int result) { // TODO(mfoltz,munjal): Authenticate the channel if is_secure_ == true. - ReadData(); + if (result == net::ERR_CERT_AUTHORITY_INVALID && + peer_cert_.empty() && + ExtractPeerCert(&peer_cert_)) { + next_state_ = CONN_STATE_TCP_CONNECT; + } + return result; +} + +void CastSocket::DoConnectCallback(int result) { + ready_state_ = (result == net::OK) ? READY_STATE_OPEN : READY_STATE_CLOSED; + error_state_ = (result == net::OK) ? + CHANNEL_ERROR_NONE : CHANNEL_ERROR_CONNECT_ERROR; + base::ResetAndReturn(&connect_callback_).Run(result); + if (result == net::OK) + ReadData(); } void CastSocket::Close(const net::CompletionCallback& callback) { DCHECK(CalledOnValidThread()); DVLOG(1) << "Close ReadyState = " << ready_state_; + tcp_socket_.reset(NULL); socket_.reset(NULL); + cert_verifier_.reset(NULL); + transport_security_state_.reset(NULL); ready_state_ = READY_STATE_CLOSED; callback.Run(net::OK); } @@ -135,7 +270,7 @@ void CastSocket::Close(const net::CompletionCallback& callback) { void CastSocket::SendMessage(const MessageInfo& message, const net::CompletionCallback& callback) { DCHECK(CalledOnValidThread()); - DVLOG(1) << "Send::ReadyState " << ready_state_; + DVLOG(1) << "Send ReadyState " << ready_state_; int result = net::ERR_FAILED; if (ready_state_ != READY_STATE_OPEN) { callback.Run(result); diff --git a/chrome/browser/extensions/api/cast_channel/cast_socket.h b/chrome/browser/extensions/api/cast_channel/cast_socket.h index c542fb9055..e9c5a82ccf 100644 --- a/chrome/browser/extensions/api/cast_channel/cast_socket.h +++ b/chrome/browser/extensions/api/cast_channel/cast_socket.h @@ -25,7 +25,10 @@ namespace net { class AddressList; +class CertVerifier; +class SSLClientSocket; class TCPClientSocket; +class TransportSecurityState; } namespace extensions { @@ -64,7 +67,8 @@ class CastSocket : public ApiResource, // Creates a new CastSocket to |url|. |owner_extension_id| is the id of the // extension that opened the socket. CastSocket(const std::string& owner_extension_id, - const GURL& url, CastSocket::Delegate* delegate, + const GURL& url, + CastSocket::Delegate* delegate, net::NetLog* net_log); virtual ~CastSocket(); @@ -104,10 +108,14 @@ class CastSocket : public ApiResource, void FillChannelInfo(ChannelInfo* channel_info) const; protected: - // Factory method for sockets. - virtual net::TCPClientSocket* CreateSocket(const net::AddressList& addresses, - net::NetLog* net_log, - const net::NetLog::Source& source); + // Creates an instance of TCPClientSocket. + virtual scoped_ptr<net::TCPClientSocket> CreateTcpSocket(); + // Creates an instance of SSLClientSocket. + virtual scoped_ptr<net::SSLClientSocket> CreateSslSocket(); + // Extracts peer certificate from SSLClientSocket instance when the socket + // is in cert error state. + // Returns whether certificate is successfully extracted. + virtual bool ExtractPeerCert(std::string* cert); private: friend class ApiResourceManager<CastSocket>; @@ -115,13 +123,45 @@ class CastSocket : public ApiResource, return "CastSocketManager"; } + // Internal connection states. + enum ConnectionState { + CONN_STATE_NONE, + CONN_STATE_TCP_CONNECT, + CONN_STATE_TCP_CONNECT_COMPLETE, + CONN_STATE_SSL_CONNECT, + CONN_STATE_SSL_CONNECT_COMPLETE, + }; + + ///////////////////////////////////////////////////////////////////////////// + // Following methods work together to implement the following flow: + // 1. Create a new TCP socket and connect to it + // 2. Create a new SSL socket and try connecting to it + // 3. If connection fails due to invalid cert authority, then extract the + // peer certificate from the error. + // 4. Whitelist the peer certificate and try #1 and #2 again. + + // Main method that performs connection state transitions. + int DoConnectLoop(int result); + // Each of the below Do* method is executed in the corresponding + // connection state. For e.g. when connection state is TCP_CONNECT + // DoTcpConnect is called, and so on. + int DoTcpConnect(); + int DoTcpConnectComplete(int result); + int DoSslConnect(); + int DoSslConnectComplete(int result); + int DoSslConnectRetry(); + ///////////////////////////////////////////////////////////////////////////// + + // Callback method for callbacks from underlying sockets. + void OnConnectComplete(int result); + + // Runs the external connection callback and resets it. + void DoConnectCallback(int result); + // Verifies that the URL is a valid cast:// or casts:// URL and sets url_ to // the result. bool ParseChannelUrl(const GURL& url); - // Called when the socket is connected. - void OnConnectComplete(int result); - // Writes data to the socket from the WriteRequest at the head of the queue. // Calls OnWriteData() on completion. void WriteData(); @@ -183,11 +223,17 @@ class CastSocket : public ApiResource, // The NetLog source for this service. net::NetLog::Source net_log_source_; - // Owned ptr to the underlying socket. - // - // NOTE(mfoltz): We'll have to refactor this to allow substitution of an - // SSLClientSocket, since the APIs are different. - scoped_ptr<net::TCPClientSocket> socket_; + // Next connection state to transition to. + ConnectionState next_state_; + // Owned ptr to the underlying TCP socket. + scoped_ptr<net::TCPClientSocket> tcp_socket_; + // Owned ptr to the underlying SSL socket. + scoped_ptr<net::SSLClientSocket> socket_; + // Certificate of the peer. This field may be empty if the peer + // certificate is not yet fetched. + std::string peer_cert_; + scoped_ptr<net::CertVerifier> cert_verifier_; + scoped_ptr<net::TransportSecurityState> transport_security_state_; // Callback invoked when the socket is connected. net::CompletionCallback connect_callback_; diff --git a/chrome/browser/extensions/api/cast_channel/cast_socket_unittest.cc b/chrome/browser/extensions/api/cast_channel/cast_socket_unittest.cc index 45cf51c3b5..fdb94f0cc5 100644 --- a/chrome/browser/extensions/api/cast_channel/cast_socket_unittest.cc +++ b/chrome/browser/extensions/api/cast_channel/cast_socket_unittest.cc @@ -10,7 +10,11 @@ #include "net/base/capturing_net_log.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" +#include "net/base/net_log.h" +#include "net/socket/socket_test_util.h" +#include "net/socket/ssl_client_socket.h" #include "net/socket/tcp_client_socket.h" +#include "net/ssl/ssl_info.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -33,8 +37,8 @@ class MockCastSocketDelegate : public CastSocket::Delegate { class MockTCPClientSocket : public net::TCPClientSocket { public: - explicit MockTCPClientSocket(const net::AddressList& addresses) : - TCPClientSocket(addresses, NULL, net::NetLog::Source()) { } + explicit MockTCPClientSocket() : + TCPClientSocket(net::AddressList(), NULL, net::NetLog::Source()) { } virtual ~MockTCPClientSocket() { } MOCK_METHOD1(Connect, int(const net::CompletionCallback& callback)); @@ -47,6 +51,23 @@ class MockTCPClientSocket : public net::TCPClientSocket { MOCK_METHOD0(Disconnect, void()); }; +class MockSSLClientSocket : public net::MockClientSocket { + public: + MockSSLClientSocket() : MockClientSocket(net::BoundNetLog()) { } + virtual ~MockSSLClientSocket() { } + + MOCK_METHOD1(Connect, int(const net::CompletionCallback& callback)); + MOCK_METHOD3(Read, int(net::IOBuffer* buf, int buf_len, + const net::CompletionCallback& callback)); + MOCK_METHOD3(Write, int(net::IOBuffer* buf, int buf_len, + const net::CompletionCallback& callback)); + MOCK_METHOD0(Disconnect, void()); + MOCK_CONST_METHOD0(WasEverUsed, bool()); + MOCK_CONST_METHOD0(UsingTCPFastOpen, bool()); + MOCK_CONST_METHOD0(WasNpnNegotiated, bool()); + MOCK_METHOD1(GetSSLInfo, bool(net::SSLInfo*)); +}; + class CompleteHandler { public: CompleteHandler() {} @@ -61,43 +82,72 @@ class TestCastSocket : public CastSocket { public: explicit TestCastSocket(MockCastSocketDelegate* delegate) : CastSocket("abcdefg", GURL("cast://192.0.0.1:8009"), delegate, - &capturing_net_log_), owns_socket_(true) { - net::AddressList addresses; - mock_tcp_socket_ = new MockTCPClientSocket(addresses); + &capturing_net_log_), + mock_tcp_socket_(new MockTCPClientSocket()), + mock_ssl_socket_(new MockSSLClientSocket()), + owns_tcp_socket_(true), + owns_ssl_socket_(true), + extract_cert_result_(true) { } virtual ~TestCastSocket() { - if (owns_socket_) { + if (owns_tcp_socket_) { DCHECK(mock_tcp_socket_); delete mock_tcp_socket_; } + if (owns_ssl_socket_) { + DCHECK(mock_ssl_socket_); + delete mock_ssl_socket_; + } } virtual void Close(const net::CompletionCallback& callback) OVERRIDE { - if (!owns_socket_) + if (!owns_tcp_socket_) mock_tcp_socket_ = NULL; + if (!owns_ssl_socket_) + mock_ssl_socket_ = NULL; CastSocket::Close(callback); } - // Ptr to the mock socket. Ownership is transferred to CastSocket when it is - // returned from CreateSocket(). CastSocket will destroy it on Close(), - // so don't refer to |mock_tcp_socket_| afterwards. + void CreateNewSockets() { + owns_tcp_socket_ = true; + mock_tcp_socket_ = new MockTCPClientSocket(); + owns_ssl_socket_ = true; + mock_ssl_socket_ = new MockSSLClientSocket(); + } + + void SetExtractCertResult(bool value) { + extract_cert_result_ = value; + } + MockTCPClientSocket* mock_tcp_socket_; + MockSSLClientSocket* mock_ssl_socket_; protected: - // Transfers ownership of |mock_tcp_socket_| to CastSocket. - virtual net::TCPClientSocket* CreateSocket( - const net::AddressList& addresses, - net::NetLog* net_log, - const net::NetLog::Source& source) OVERRIDE { - owns_socket_ = false; - return mock_tcp_socket_; + virtual scoped_ptr<net::TCPClientSocket> CreateTcpSocket() OVERRIDE { + owns_tcp_socket_ = false; + return scoped_ptr<net::TCPClientSocket>(mock_tcp_socket_); + } + + virtual scoped_ptr<net::SSLClientSocket> CreateSslSocket() OVERRIDE { + owns_ssl_socket_ = false; + return scoped_ptr<net::SSLClientSocket>(mock_ssl_socket_); + } + + virtual bool ExtractPeerCert(std::string* cert) OVERRIDE { + if (extract_cert_result_) + cert->assign("dummy_test_cert"); + return extract_cert_result_; } private: net::CapturingNetLog capturing_net_log_; // Whether this object or the parent owns |mock_tcp_socket_|. - bool owns_socket_; + bool owns_tcp_socket_; + // Whether this object or the parent owns |mock_ssl_socket_|. + bool owns_ssl_socket_; + // Simulated result of peer cert extraction. + bool extract_cert_result_; }; class CastSocketTest : public testing::Test { @@ -120,30 +170,51 @@ class CastSocketTest : public testing::Test { base::Unretained(&handler_))); } - // Sets expectations when the socket is connected. Connecting the socket also + // Sets an expectation on the TCP socket Connect method. Connect method is + // setup to return net::ERR_IO_PENDING and store the callback passed to it + // in |callback|. + void ExpectTCPConnect(net::CompletionCallback* callback) { + EXPECT_CALL(mock_tcp_socket(), Connect(A<const net::CompletionCallback&>())) + .Times(1) + .WillOnce(DoAll(SaveArg<0>(callback), Return(net::ERR_IO_PENDING))); + } + + // Sets an expectation on the SSL socket Connect method. Connect method is + // setup to return net::ERR_IO_PENDING and store the callback passed to it + // in |callback|. + void ExpectSSLConnect(net::CompletionCallback* callback) { + EXPECT_CALL(mock_ssl_socket(), Connect(A<const net::CompletionCallback&>())) + .Times(1) + .WillOnce(DoAll(SaveArg<0>(callback), Return(net::ERR_IO_PENDING))); + } + + // Sets an expectation on the SSL socket Read method. Read method is setup + // to return net::ERR_IO_PENDING and to be called |times| number of times. + void ExpectSSLRead(int times) { + EXPECT_CALL(mock_ssl_socket(), Read(A<net::IOBuffer*>(), + A<int>(), + A<const net::CompletionCallback&>())) + .Times(times) + .WillOnce(Return(net::ERR_IO_PENDING)); + } + + // Sets expectations when the socket is connected. Connecting the socket also // starts the read loop; we expect the call to Read(), but never fire the read // callback. void ConnectHelper() { - net::CompletionCallback connect_callback; - EXPECT_CALL(mock_tcp_socket(), SetNoDelay(true)) - .Times(1) - .WillOnce(Return(true)); - EXPECT_CALL(mock_tcp_socket(), SetKeepAlive(true, A<int>())) - .Times(1) - .WillOnce(Return(true)); - EXPECT_CALL(mock_tcp_socket(), Connect(A<const net::CompletionCallback&>())) - .Times(1) - .WillOnce(DoAll(SaveArg<0>(&connect_callback), Return(net::OK))); + net::CompletionCallback connect_callback1; + net::CompletionCallback connect_callback2; + + ExpectTCPConnect(&connect_callback1); + ExpectSSLConnect(&connect_callback2); EXPECT_CALL(handler_, OnConnectComplete(net::OK)); - EXPECT_CALL(mock_tcp_socket(), Read(A<net::IOBuffer*>(), - A<int>(), - A<const net::CompletionCallback&>())) - .Times(1) - .WillOnce(Return(net::ERR_IO_PENDING)); + ExpectSSLRead(1); socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete, base::Unretained(&handler_))); - connect_callback.Run(net::OK); + connect_callback1.Run(net::OK); + connect_callback2.Run(net::OK); + EXPECT_EQ(cast_channel::READY_STATE_OPEN, socket_->ready_state()); EXPECT_EQ(cast_channel::CHANNEL_ERROR_NONE, socket_->error_state()); } @@ -155,6 +226,12 @@ class CastSocketTest : public testing::Test { return *mock_socket; } + MockSSLClientSocket& mock_ssl_socket() { + MockSSLClientSocket* mock_socket = socket_->mock_ssl_socket_; + DCHECK(mock_socket); + return *mock_socket; + } + MockCastSocketDelegate mock_delegate_; scoped_ptr<TestCastSocket> socket_; CompleteHandler handler_; @@ -198,12 +275,106 @@ TEST_F(CastSocketTest, TestConnectAndClose) { EXPECT_EQ(cast_channel::CHANNEL_ERROR_NONE, socket_->error_state()); } -// Tests writing a single message where the completion is signaled via callback. +// Test that when first connection attempt fails with certificate authority +// invalid error, a second connection attempt is made with peer cert +// whitelisted. +TEST_F(CastSocketTest, TestTwoStepConnect) { + // Expectations for the initial connect call + net::CompletionCallback tcp_connect_callback1; + net::CompletionCallback ssl_connect_callback1; + + ExpectTCPConnect(&tcp_connect_callback1); + ExpectSSLConnect(&ssl_connect_callback1); + + // Start connect flow + socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete, + base::Unretained(&handler_))); + tcp_connect_callback1.Run(net::OK); + + // Expectations for the second connect call + socket_->CreateNewSockets(); + net::CompletionCallback tcp_connect_callback2; + net::CompletionCallback ssl_connect_callback2; + ExpectTCPConnect(&tcp_connect_callback2); + ExpectSSLConnect(&ssl_connect_callback2); + EXPECT_CALL(handler_, OnConnectComplete(net::OK)); + ExpectSSLRead(1); + + // Trigger callbacks for the first connect + ssl_connect_callback1.Run(net::ERR_CERT_AUTHORITY_INVALID); + + // Trigger callbacks for the second connect + tcp_connect_callback2.Run(net::OK); + ssl_connect_callback2.Run(net::OK); + + EXPECT_EQ(cast_channel::READY_STATE_OPEN, socket_->ready_state()); + EXPECT_EQ(cast_channel::CHANNEL_ERROR_NONE, socket_->error_state()); +} + +// Test that connection will be attempted a maximum of 2 times even if the +// second attempt also returns certificate authority invalid error. +TEST_F(CastSocketTest, TestMaxTwoConnectAttempts) { + net::CompletionCallback tcp_connect_callback1; + net::CompletionCallback ssl_connect_callback1; + + // Expectations for the initial connect call + ExpectTCPConnect(&tcp_connect_callback1); + ExpectSSLConnect(&ssl_connect_callback1); + + // Start connect flow + socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete, + base::Unretained(&handler_))); + tcp_connect_callback1.Run(net::OK); + + socket_->CreateNewSockets(); + net::CompletionCallback tcp_connect_callback2; + net::CompletionCallback ssl_connect_callback2; + + // Expectations for the second connect call + ExpectTCPConnect(&tcp_connect_callback2); + ExpectSSLConnect(&ssl_connect_callback2); + EXPECT_CALL(handler_, OnConnectComplete(net::ERR_CERT_AUTHORITY_INVALID)); + + // Trigger callbacks for the first connect + ssl_connect_callback1.Run(net::ERR_CERT_AUTHORITY_INVALID); + + // Trigger callbacks for the second connect + tcp_connect_callback2.Run(net::OK); + ssl_connect_callback2.Run(net::ERR_CERT_AUTHORITY_INVALID); + + EXPECT_EQ(cast_channel::READY_STATE_CLOSED, socket_->ready_state()); + EXPECT_EQ(cast_channel::CHANNEL_ERROR_CONNECT_ERROR, socket_->error_state()); +} + +TEST_F(CastSocketTest, TestCertExtractionFailure) { + net::CompletionCallback connect_callback1; + net::CompletionCallback connect_callback2; + + ExpectTCPConnect(&connect_callback1); + ExpectSSLConnect(&connect_callback2); + + socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete, + base::Unretained(&handler_))); + connect_callback1.Run(net::OK); + + EXPECT_CALL(handler_, OnConnectComplete(net::ERR_CERT_AUTHORITY_INVALID)); + + // Set cert extraction to fail + socket_->SetExtractCertResult(false); + // Attempt to connect results in ERR_CERT_AUTHORTY_INVALID + connect_callback2.Run(net::ERR_CERT_AUTHORITY_INVALID); + + EXPECT_EQ(cast_channel::READY_STATE_CLOSED, socket_->ready_state()); + EXPECT_EQ(cast_channel::CHANNEL_ERROR_CONNECT_ERROR, socket_->error_state()); +} + +// Tests writing a single message where the completion is signaled via +// callback. TEST_F(CastSocketTest, TestWriteViaCallback) { ConnectHelper(); net::CompletionCallback write_callback; - EXPECT_CALL(mock_tcp_socket(), + EXPECT_CALL(mock_ssl_socket(), Write(A<net::IOBuffer*>(), 39, A<const net::CompletionCallback&>())) @@ -223,7 +394,7 @@ TEST_F(CastSocketTest, TestWriteViaCallback) { TEST_F(CastSocketTest, TestWrite) { ConnectHelper(); - EXPECT_CALL(mock_tcp_socket(), + EXPECT_CALL(mock_ssl_socket(), Write(A<net::IOBuffer*>(), 39, A<const net::CompletionCallback&>())) @@ -250,7 +421,7 @@ TEST_F(CastSocketTest, TestWriteMany) { net::CompletionCallback write_callback; for (int i = 0; i < 4; i++) { - EXPECT_CALL(mock_tcp_socket(), + EXPECT_CALL(mock_ssl_socket(), Write(A<net::IOBuffer*>(), sizes[i], A<const net::CompletionCallback&>())) @@ -280,7 +451,7 @@ TEST_F(CastSocketTest, TestWriteError) { ConnectHelper(); net::CompletionCallback write_callback; - EXPECT_CALL(mock_tcp_socket(), + EXPECT_CALL(mock_ssl_socket(), Write(A<net::IOBuffer*>(), 39, A<const net::CompletionCallback&>())) @@ -299,28 +470,28 @@ TEST_F(CastSocketTest, TestWriteError) { // Tests reading a single message. TEST_F(CastSocketTest, TestRead) { - net::CompletionCallback connect_callback; + net::CompletionCallback connect_callback1; + net::CompletionCallback connect_callback2; net::CompletionCallback read_callback; std::string message_data; ASSERT_TRUE(CastSocket::Serialize(test_proto_, &message_data)); - EXPECT_CALL(mock_tcp_socket(), SetNoDelay(true)) - .Times(1) - .WillOnce(Return(true)); - EXPECT_CALL(mock_tcp_socket(), SetKeepAlive(true, A<int>())) - .Times(1) - .WillOnce(Return(true)); EXPECT_CALL(mock_tcp_socket(), Connect(A<const net::CompletionCallback&>())) - .Times(1) - .WillOnce(DoAll(SaveArg<0>(&connect_callback), Return(net::OK))); + .Times(1) + .WillOnce(DoAll(SaveArg<0>(&connect_callback1), + Return(net::ERR_IO_PENDING))); + EXPECT_CALL(mock_ssl_socket(), Connect(A<const net::CompletionCallback&>())) + .Times(1) + .WillOnce(DoAll(SaveArg<0>(&connect_callback2), + Return(net::ERR_IO_PENDING))); EXPECT_CALL(handler_, OnConnectComplete(net::OK)); - EXPECT_CALL(mock_tcp_socket(), Read(A<net::IOBuffer*>(), + EXPECT_CALL(mock_ssl_socket(), Read(A<net::IOBuffer*>(), A<int>(), A<const net::CompletionCallback&>())) - .Times(3) - .WillRepeatedly(DoAll(SaveArg<2>(&read_callback), - Return(net::ERR_IO_PENDING))); + .Times(3) + .WillRepeatedly(DoAll(SaveArg<2>(&read_callback), + Return(net::ERR_IO_PENDING))); // Expect the test message to be read and invoke the delegate. EXPECT_CALL(mock_delegate_, @@ -329,7 +500,8 @@ TEST_F(CastSocketTest, TestRead) { // Connect the socket. socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete, base::Unretained(&handler_))); - connect_callback.Run(net::OK); + connect_callback1.Run(net::OK); + connect_callback2.Run(net::OK); // Put the test header and message into the io_buffers and invoke the read // callbacks. @@ -346,8 +518,10 @@ TEST_F(CastSocketTest, TestRead) { // Tests reading multiple messages. TEST_F(CastSocketTest, TestReadMany) { - net::CompletionCallback connect_callback; + net::CompletionCallback connect_callback1; + net::CompletionCallback connect_callback2; net::CompletionCallback read_callback; + std::string messages[4]; messages[0] = "Hello, World!"; messages[1] = "Goodbye, World!"; @@ -362,17 +536,16 @@ TEST_F(CastSocketTest, TestReadMany) { ASSERT_TRUE(CastSocket::Serialize(test_proto_, &message_data[i])); } - EXPECT_CALL(mock_tcp_socket(), SetNoDelay(true)) - .Times(1) - .WillOnce(Return(true)); - EXPECT_CALL(mock_tcp_socket(), SetKeepAlive(true, A<int>())) - .Times(1) - .WillOnce(Return(true)); EXPECT_CALL(mock_tcp_socket(), Connect(A<const net::CompletionCallback&>())) - .Times(1) - .WillOnce(DoAll(SaveArg<0>(&connect_callback), Return(net::OK))); + .Times(1) + .WillOnce(DoAll(SaveArg<0>(&connect_callback1), + Return(net::ERR_IO_PENDING))); + EXPECT_CALL(mock_ssl_socket(), Connect(A<const net::CompletionCallback&>())) + .Times(1) + .WillOnce(DoAll(SaveArg<0>(&connect_callback2), + Return(net::ERR_IO_PENDING))); EXPECT_CALL(handler_, OnConnectComplete(net::OK)); - EXPECT_CALL(mock_tcp_socket(), Read(A<net::IOBuffer*>(), + EXPECT_CALL(mock_ssl_socket(), Read(A<net::IOBuffer*>(), A<int>(), A<const net::CompletionCallback&>())) .Times(9) @@ -387,7 +560,8 @@ TEST_F(CastSocketTest, TestReadMany) { // Connect the socket. socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete, base::Unretained(&handler_))); - connect_callback.Run(net::OK); + connect_callback1.Run(net::OK); + connect_callback2.Run(net::OK); // Put the test headers and messages into the io_buffer and invoke the read // callbacks. @@ -406,20 +580,20 @@ TEST_F(CastSocketTest, TestReadMany) { // Tests error on reading. TEST_F(CastSocketTest, TestReadError) { - net::CompletionCallback connect_callback; + net::CompletionCallback connect_callback1; + net::CompletionCallback connect_callback2; net::CompletionCallback read_callback; - EXPECT_CALL(mock_tcp_socket(), SetNoDelay(true)) - .Times(1) - .WillOnce(Return(true)); - EXPECT_CALL(mock_tcp_socket(), SetKeepAlive(true, A<int>())) - .Times(1) - .WillOnce(Return(true)); EXPECT_CALL(mock_tcp_socket(), Connect(A<const net::CompletionCallback&>())) - .Times(1) - .WillOnce(DoAll(SaveArg<0>(&connect_callback), Return(net::OK))); + .Times(1) + .WillOnce(DoAll(SaveArg<0>(&connect_callback1), + Return(net::ERR_IO_PENDING))); + EXPECT_CALL(mock_ssl_socket(), Connect(A<const net::CompletionCallback&>())) + .Times(1) + .WillOnce(DoAll(SaveArg<0>(&connect_callback2), + Return(net::ERR_IO_PENDING))); EXPECT_CALL(handler_, OnConnectComplete(net::OK)); - EXPECT_CALL(mock_tcp_socket(), Read(A<net::IOBuffer*>(), + EXPECT_CALL(mock_ssl_socket(), Read(A<net::IOBuffer*>(), A<int>(), A<const net::CompletionCallback&>())) .WillOnce(DoAll(SaveArg<2>(&read_callback), @@ -430,7 +604,8 @@ TEST_F(CastSocketTest, TestReadError) { // Connect the socket. socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete, base::Unretained(&handler_))); - connect_callback.Run(net::OK); + connect_callback1.Run(net::OK); + connect_callback2.Run(net::OK); // Cause an error. read_callback.Run(net::ERR_SOCKET_NOT_CONNECTED); diff --git a/chrome/browser/extensions/api/commands/command_service.cc b/chrome/browser/extensions/api/commands/command_service.cc index 4b3ef78758..5282cf0a1d 100644 --- a/chrome/browser/extensions/api/commands/command_service.cc +++ b/chrome/browser/extensions/api/commands/command_service.cc @@ -18,6 +18,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/accelerator_utils.h" #include "chrome/common/extensions/api/commands/commands_handler.h" +#include "chrome/common/extensions/feature_switch.h" #include "chrome/common/pref_names.h" #include "components/user_prefs/pref_registry_syncable.h" #include "content/public/browser/notification_details.h" @@ -30,6 +31,7 @@ namespace { const char kExtension[] = "extension"; const char kCommandName[] = "command_name"; +const char kGlobal[] = "global"; // A preference that indicates that the initial keybindings for the given // extension have been set. @@ -155,18 +157,21 @@ bool CommandService::GetNamedCommands(const std::string& extension_id, extensions::CommandMap::const_iterator iter = commands->begin(); for (; iter != commands->end(); ++iter) { - ui::Accelerator shortcut_assigned = - FindShortcutForCommand(extension_id, iter->second.command_name()); + // Look up to see if the user has overridden how the command should work. + extensions::Command saved_command = + FindCommandByName(extension_id, iter->second.command_name()); + ui::Accelerator shortcut_assigned = saved_command.accelerator(); if (type == ACTIVE_ONLY && shortcut_assigned.key_code() == ui::VKEY_UNKNOWN) continue; extensions::Command command = iter->second; - if (scope != ANY_SCOPE && ((scope == GLOBAL) != command.global())) + if (scope != ANY_SCOPE && ((scope == GLOBAL) != saved_command.global())) continue; if (shortcut_assigned.key_code() != ui::VKEY_UNKNOWN) command.set_accelerator(shortcut_assigned); + command.set_global(saved_command.global()); (*command_map)[iter->second.command_name()] = command; } @@ -178,7 +183,8 @@ bool CommandService::AddKeybindingPref( const ui::Accelerator& accelerator, std::string extension_id, std::string command_name, - bool allow_overrides) { + bool allow_overrides, + bool global) { if (accelerator.key_code() == ui::VKEY_UNKNOWN) return false; @@ -194,6 +200,7 @@ bool CommandService::AddKeybindingPref( base::DictionaryValue* keybinding = new base::DictionaryValue(); keybinding->SetString(kExtension, extension_id); keybinding->SetString(kCommandName, command_name); + keybinding->SetBoolean(kGlobal, global); bindings->Set(key, keybinding); @@ -231,16 +238,30 @@ void CommandService::Observe( void CommandService::UpdateKeybindingPrefs(const std::string& extension_id, const std::string& command_name, const std::string& keystroke) { + extensions::Command command = FindCommandByName(extension_id, command_name); + // The extension command might be assigned another shortcut. Remove that // shortcut before proceeding. RemoveKeybindingPrefs(extension_id, command_name); ui::Accelerator accelerator = Command::StringToAccelerator(keystroke, command_name); - AddKeybindingPref(accelerator, extension_id, command_name, true); + AddKeybindingPref(accelerator, extension_id, command_name, + true, command.global()); +} + +void CommandService::ToggleScope(const std::string& extension_id, + const std::string& command_name) { + extensions::Command command = FindCommandByName(extension_id, command_name); + + // Pre-existing shortcuts must be removed before proceeding because the + // handlers for global and non-global extensions are not one and the same. + RemoveKeybindingPrefs(extension_id, command_name); + AddKeybindingPref(command.accelerator(), extension_id, + command_name, true, !command.global()); } -ui::Accelerator CommandService::FindShortcutForCommand( +Command CommandService::FindCommandByName( const std::string& extension_id, const std::string& command) { const base::DictionaryValue* bindings = profile_->GetPrefs()->GetDictionary(prefs::kExtensionCommands); @@ -257,15 +278,18 @@ ui::Accelerator CommandService::FindShortcutForCommand( item->GetString(kCommandName, &command_name); if (command != command_name) continue; + bool global = false; + if (FeatureSwitch::global_commands()->IsEnabled()) + item->GetBoolean(kGlobal, &global); std::string shortcut = it.key(); if (StartsWithASCII(shortcut, Command::CommandPlatform() + ":", true)) shortcut = shortcut.substr(Command::CommandPlatform().length() + 1); - return Command::StringToAccelerator(shortcut, command_name); + return Command(command_name, string16(), shortcut, global); } - return ui::Accelerator(); + return Command(); } void CommandService::AssignInitialKeybindings(const Extension* extension) { @@ -289,7 +313,8 @@ void CommandService::AssignInitialKeybindings(const Extension* extension) { AddKeybindingPref(iter->second.accelerator(), extension->id(), iter->second.command_name(), - false); // Overwriting not allowed. + false, // Overwriting not allowed. + iter->second.global()); } } @@ -301,7 +326,8 @@ void CommandService::AssignInitialKeybindings(const Extension* extension) { AddKeybindingPref(browser_action_command->accelerator(), extension->id(), browser_action_command->command_name(), - false); // Overwriting not allowed. + false, // Overwriting not allowed. + false); // Browser actions can't be global. } } @@ -313,7 +339,8 @@ void CommandService::AssignInitialKeybindings(const Extension* extension) { AddKeybindingPref(page_action_command->accelerator(), extension->id(), page_action_command->command_name(), - false); // Overwriting not allowed. + false, // Overwriting not allowed. + false); // Page actions can't be global. } } @@ -325,7 +352,8 @@ void CommandService::AssignInitialKeybindings(const Extension* extension) { AddKeybindingPref(script_badge_command->accelerator(), extension->id(), script_badge_command->command_name(), - false); // Overwriting not allowed. + false, // Overwriting not allowed. + false); // Script badges can't be global. } } } @@ -407,8 +435,10 @@ bool CommandService::GetExtensionActionCommand( if (!requested_command) return false; - ui::Accelerator shortcut_assigned = - FindShortcutForCommand(extension_id, requested_command->command_name()); + // Look up to see if the user has overridden how the command should work. + extensions::Command saved_command = + FindCommandByName(extension_id, requested_command->command_name()); + ui::Accelerator shortcut_assigned = saved_command.accelerator(); if (active) *active = (shortcut_assigned.key_code() != ui::VKEY_UNKNOWN); diff --git a/chrome/browser/extensions/api/commands/command_service.h b/chrome/browser/extensions/api/commands/command_service.h index ac553aa318..cedacd770f 100644 --- a/chrome/browser/extensions/api/commands/command_service.h +++ b/chrome/browser/extensions/api/commands/command_service.h @@ -115,12 +115,14 @@ class CommandService : public ProfileKeyedAPI, // |allow_overrides| is false, the keybinding must be free for the change to // be recorded (as determined by the master list in |user_prefs|). If // |allow_overwrites| is true, any previously recorded keybinding for this - // |accelerator| will be overwritten. Returns true if the change was - // successfully recorded. + // |accelerator| will be overwritten. If |global| is true, the command will + // be registered as a global command (be active even when Chrome does not have + // focus. Returns true if the change was successfully recorded. bool AddKeybindingPref(const ui::Accelerator& accelerator, std::string extension_id, std::string command_name, - bool allow_overrides); + bool allow_overrides, + bool global); // Removes all keybindings for a given extension by its |extension_id|. // |command_name| is optional and if specified, causes only the command with @@ -135,12 +137,15 @@ class CommandService : public ProfileKeyedAPI, const std::string& command_name, const std::string& keystroke); - // Finds the shortcut assigned to a command with the name |command_name| - // within an extension with id |extension_id|. Returns an empty Accelerator - // object (with keycode VKEY_UNKNOWN) if no shortcut is assigned or the - // command is not found. - ui::Accelerator FindShortcutForCommand(const std::string& extension_id, - const std::string& command); + // Toggles the scope of the keybinding. + void ToggleScope(const std::string& extension_id, + const std::string& command_name); + + // Finds the command with the name |command_name| within an extension with id + // |extension_id| . Returns an empty Command object (with keycode + // VKEY_UNKNOWN) if the command is not found. + Command FindCommandByName(const std::string& extension_id, + const std::string& command); // Overridden from content::NotificationObserver. virtual void Observe(int type, diff --git a/chrome/browser/extensions/api/commands/command_service_browsertest.cc b/chrome/browser/extensions/api/commands/command_service_browsertest.cc index 2ed81c2db2..1a1a6c1adf 100644 --- a/chrome/browser/extensions/api/commands/command_service_browsertest.cc +++ b/chrome/browser/extensions/api/commands/command_service_browsertest.cc @@ -42,9 +42,8 @@ IN_PROC_BROWSER_TEST_F(CommandServiceTest, RemoveShortcutSurvivesUpdate) { EXPECT_TRUE(service->GetExtensionById(kId, false) != NULL); // Verify it has a command of Alt+Shift+F. - ui::Accelerator accelerator = - command_service->FindShortcutForCommand( - kId, manifest_values::kBrowserActionCommandEvent); + ui::Accelerator accelerator = command_service->FindCommandByName( + kId, manifest_values::kBrowserActionCommandEvent).accelerator(); EXPECT_EQ(ui::VKEY_F, accelerator.key_code()); EXPECT_FALSE(accelerator.IsCtrlDown()); EXPECT_TRUE(accelerator.IsShiftDown()); @@ -55,8 +54,8 @@ IN_PROC_BROWSER_TEST_F(CommandServiceTest, RemoveShortcutSurvivesUpdate) { kId, manifest_values::kBrowserActionCommandEvent); // Verify it got removed. - accelerator = command_service->FindShortcutForCommand( - kId, manifest_values::kBrowserActionCommandEvent); + accelerator = command_service->FindCommandByName( + kId, manifest_values::kBrowserActionCommandEvent).accelerator(); EXPECT_EQ(ui::VKEY_UNKNOWN, accelerator.key_code()); // Update to version 2. @@ -64,8 +63,8 @@ IN_PROC_BROWSER_TEST_F(CommandServiceTest, RemoveShortcutSurvivesUpdate) { EXPECT_TRUE(service->GetExtensionById(kId, false) != NULL); // Verify it is still set to nothing. - accelerator = command_service->FindShortcutForCommand( - kId, manifest_values::kBrowserActionCommandEvent); + accelerator = command_service->FindCommandByName( + kId, manifest_values::kBrowserActionCommandEvent).accelerator(); EXPECT_EQ(ui::VKEY_UNKNOWN, accelerator.key_code()); } diff --git a/chrome/browser/extensions/api/commands/commands.cc b/chrome/browser/extensions/api/commands/commands.cc index 7e2a10bae4..0b6f0e54c6 100644 --- a/chrome/browser/extensions/api/commands/commands.cc +++ b/chrome/browser/extensions/api/commands/commands.cc @@ -60,9 +60,9 @@ bool GetAllCommandsFunction::RunImpl() { for (extensions::CommandMap::const_iterator iter = named_commands.begin(); iter != named_commands.end(); ++iter) { - ui::Accelerator shortcut_assigned = - command_service->FindShortcutForCommand( - extension_->id(), iter->second.command_name()); + extensions::Command command = command_service->FindCommandByName( + extension_->id(), iter->second.command_name()); + ui::Accelerator shortcut_assigned = command.accelerator(); active = (shortcut_assigned.key_code() != ui::VKEY_UNKNOWN); command_list->Append(CreateCommandValue(iter->second, active)); diff --git a/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc b/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc index dbc714aea8..e17de512bf 100644 --- a/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc +++ b/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc @@ -191,7 +191,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionContentSettingsApiTest, MAYBE_Standard) { CheckContentSettingsSet(); // The settings should not be reset when the extension is reloaded. - ReloadExtension(last_loaded_extension_id_); + ReloadExtension(last_loaded_extension_id()); CheckContentSettingsSet(); // Uninstalling and installing the extension (without running the test that @@ -199,7 +199,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionContentSettingsApiTest, MAYBE_Standard) { content::WindowedNotificationObserver observer( chrome::NOTIFICATION_EXTENSION_UNINSTALLED, content::NotificationService::AllSources()); - UninstallExtension(last_loaded_extension_id_); + UninstallExtension(last_loaded_extension_id()); observer.Wait(); CheckContentSettingsDefault(); diff --git a/chrome/browser/extensions/api/declarative/declarative_rule.h b/chrome/browser/extensions/api/declarative/declarative_rule.h index d1dab32782..84dffc21a8 100644 --- a/chrome/browser/extensions/api/declarative/declarative_rule.h +++ b/chrome/browser/extensions/api/declarative/declarative_rule.h @@ -21,6 +21,7 @@ #include "base/stl_util.h" #include "base/time/time.h" #include "chrome/common/extensions/api/events.h" +#include "chrome/common/extensions/extension.h" #include "extensions/common/matcher/url_matcher.h" namespace base { @@ -39,6 +40,7 @@ namespace extensions { // // // Arguments passed through from DeclarativeConditionSet::Create. // static scoped_ptr<ConditionT> Create( +// const Extension* extension, // URLMatcherConditionFactory* url_matcher_condition_factory, // // Except this argument gets elements of the AnyVector. // const base::Value& definition, @@ -57,10 +59,11 @@ class DeclarativeConditionSet { typedef std::vector<linked_ptr<const ConditionT> > Conditions; typedef typename Conditions::const_iterator const_iterator; - // Factory method that creates a DeclarativeConditionSet according to the JSON - // array |conditions| passed by the extension API. Sets |error| and returns - // NULL in case of an error. + // Factory method that creates a DeclarativeConditionSet for |extension| + // according to the JSON array |conditions| passed by the extension API. Sets + // |error| and returns NULL in case of an error. static scoped_ptr<DeclarativeConditionSet> Create( + const Extension* extension, URLMatcherConditionFactory* url_matcher_condition_factory, const AnyVector& conditions, std::string* error); @@ -112,6 +115,7 @@ class DeclarativeConditionSet { // // // Arguments passed through from ActionSet::Create. // static scoped_ptr<ActionT> Create( +// const Extension* extension, // // Except this argument gets elements of the AnyVector. // const base::Value& definition, // std::string* error, bool* bad_message); @@ -139,10 +143,11 @@ class DeclarativeActionSet { explicit DeclarativeActionSet(const Actions& actions); - // Factory method that instantiates a DeclarativeActionSet according to - // |actions| which represents the array of actions received from the - // extension API. - static scoped_ptr<DeclarativeActionSet> Create(const AnyVector& actions, + // Factory method that instantiates a DeclarativeActionSet for |extension| + // according to |actions| which represents the array of actions received from + // the extension API. + static scoped_ptr<DeclarativeActionSet> Create(const Extension* extension, + const AnyVector& actions, std::string* error, bool* bad_message); @@ -202,9 +207,9 @@ class DeclarativeRule { scoped_ptr<ActionSet> actions, Priority priority); - // Creates a DeclarativeRule for an extension given a json definition. The + // Creates a DeclarativeRule for |extension| given a json definition. The // format of each condition and action's json is up to the specific ConditionT - // and ActionT. + // and ActionT. |extension| may be NULL in tests. // // Before constructing the final rule, calls check_consistency(conditions, // actions, error) and returns NULL if it fails. Pass NULL if no consistency @@ -212,7 +217,7 @@ class DeclarativeRule { // the returned rule is internally consistent. static scoped_ptr<DeclarativeRule> Create( URLMatcherConditionFactory* url_matcher_condition_factory, - const std::string& extension_id, + const Extension* extension, base::Time extension_installation_time, linked_ptr<JsonRule> rule, ConsistencyChecker check_consistency, @@ -288,6 +293,7 @@ void DeclarativeConditionSet<ConditionT>::GetURLMatcherConditionSets( template<typename ConditionT> scoped_ptr<DeclarativeConditionSet<ConditionT> > DeclarativeConditionSet<ConditionT>::Create( + const Extension* extension, URLMatcherConditionFactory* url_matcher_condition_factory, const AnyVector& conditions, std::string* error) { @@ -296,8 +302,8 @@ DeclarativeConditionSet<ConditionT>::Create( for (AnyVector::const_iterator i = conditions.begin(); i != conditions.end(); ++i) { CHECK(i->get()); - scoped_ptr<ConditionT> condition = - ConditionT::Create(url_matcher_condition_factory, **i, error); + scoped_ptr<ConditionT> condition = ConditionT::Create( + extension, url_matcher_condition_factory, **i, error); if (!error->empty()) return scoped_ptr<DeclarativeConditionSet>(); result.push_back(make_linked_ptr(condition.release())); @@ -346,6 +352,7 @@ DeclarativeActionSet<ActionT>::DeclarativeActionSet(const Actions& actions) template<typename ActionT> scoped_ptr<DeclarativeActionSet<ActionT> > DeclarativeActionSet<ActionT>::Create( + const Extension* extension, const AnyVector& actions, std::string* error, bool* bad_message) { @@ -357,7 +364,7 @@ DeclarativeActionSet<ActionT>::Create( i != actions.end(); ++i) { CHECK(i->get()); scoped_refptr<const ActionT> action = - ActionT::Create(**i, error, bad_message); + ActionT::Create(extension, **i, error, bad_message); if (!error->empty() || *bad_message) return scoped_ptr<DeclarativeActionSet>(); result.push_back(action); @@ -423,7 +430,7 @@ template<typename ConditionT, typename ActionT> scoped_ptr<DeclarativeRule<ConditionT, ActionT> > DeclarativeRule<ConditionT, ActionT>::Create( URLMatcherConditionFactory* url_matcher_condition_factory, - const std::string& extension_id, + const Extension* extension, base::Time extension_installation_time, linked_ptr<JsonRule> rule, ConsistencyChecker check_consistency, @@ -431,14 +438,14 @@ DeclarativeRule<ConditionT, ActionT>::Create( scoped_ptr<DeclarativeRule> error_result; scoped_ptr<ConditionSet> conditions = ConditionSet::Create( - url_matcher_condition_factory, rule->conditions, error); + extension, url_matcher_condition_factory, rule->conditions, error); if (!error->empty()) return error_result.Pass(); CHECK(conditions.get()); bool bad_message = false; scoped_ptr<ActionSet> actions = - ActionSet::Create(rule->actions, error, &bad_message); + ActionSet::Create(extension, rule->actions, error, &bad_message); if (bad_message) { // TODO(battre) Export concept of bad_message to caller, the extension // should be killed in case it is true. @@ -459,7 +466,7 @@ DeclarativeRule<ConditionT, ActionT>::Create( CHECK(rule->priority.get()); int priority = *(rule->priority); - GlobalRuleId rule_id(extension_id, *(rule->id)); + GlobalRuleId rule_id(extension->id(), *(rule->id)); Tags tags = rule->tags ? *rule->tags : Tags(); return scoped_ptr<DeclarativeRule>( new DeclarativeRule(rule_id, tags, extension_installation_time, diff --git a/chrome/browser/extensions/api/declarative/declarative_rule_unittest.cc b/chrome/browser/extensions/api/declarative/declarative_rule_unittest.cc index 3808188e15..fee8401b5d 100644 --- a/chrome/browser/extensions/api/declarative/declarative_rule_unittest.cc +++ b/chrome/browser/extensions/api/declarative/declarative_rule_unittest.cc @@ -8,6 +8,7 @@ #include "base/message_loop/message_loop.h" #include "base/test/values_test_util.h" #include "base/values.h" +#include "chrome/common/extensions/extension_builder.h" #include "extensions/common/matcher/url_matcher_constants.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -23,6 +24,14 @@ linked_ptr<T> ScopedToLinkedPtr(scoped_ptr<T> ptr) { return linked_ptr<T>(ptr.release()); } +scoped_ptr<DictionaryValue> SimpleManifest() { + return DictionaryBuilder() + .Set("name", "extension") + .Set("manifest_version", 2) + .Set("version", "1.0") + .Build(); +} + } // namespace struct RecordingCondition { @@ -37,6 +46,7 @@ struct RecordingCondition { } static scoped_ptr<RecordingCondition> Create( + const Extension* extension, URLMatcherConditionFactory* url_matcher_condition_factory, const base::Value& condition, std::string* error) { @@ -61,9 +71,8 @@ TEST(DeclarativeConditionTest, ErrorConditionSet) { conditions.push_back(ScopedToLinkedPtr(ParseJson("{\"bad_key\": 2}"))); std::string error; - scoped_ptr<RecordingConditionSet> result = - RecordingConditionSet::Create(matcher.condition_factory(), - conditions, &error); + scoped_ptr<RecordingConditionSet> result = RecordingConditionSet::Create( + NULL, matcher.condition_factory(), conditions, &error); EXPECT_EQ("Found error key", error); ASSERT_FALSE(result); } @@ -76,9 +85,8 @@ TEST(DeclarativeConditionTest, CreateConditionSet) { // Test insertion std::string error; - scoped_ptr<RecordingConditionSet> result = - RecordingConditionSet::Create(matcher.condition_factory(), - conditions, &error); + scoped_ptr<RecordingConditionSet> result = RecordingConditionSet::Create( + NULL, matcher.condition_factory(), conditions, &error); EXPECT_EQ("", error); ASSERT_TRUE(result); EXPECT_EQ(2u, result->conditions().size()); @@ -120,7 +128,8 @@ struct FulfillableCondition { } static scoped_ptr<FulfillableCondition> Create( - URLMatcherConditionFactory* /*url_matcher_condition_factory*/, + const Extension* extension, + URLMatcherConditionFactory* url_matcher_condition_factory, const base::Value& condition, std::string* error) { scoped_ptr<FulfillableCondition> result(new FulfillableCondition()); @@ -157,7 +166,7 @@ TEST(DeclarativeConditionTest, FulfillConditionSet) { // Test insertion std::string error; scoped_ptr<FulfillableConditionSet> result = - FulfillableConditionSet::Create(NULL, conditions, &error); + FulfillableConditionSet::Create(NULL, NULL, conditions, &error); ASSERT_EQ("", error); ASSERT_TRUE(result); EXPECT_EQ(4u, result->conditions().size()); @@ -205,7 +214,8 @@ class SummingAction : public base::RefCounted<SummingAction> { SummingAction(int increment, int min_priority) : increment_(increment), min_priority_(min_priority) {} - static scoped_refptr<const SummingAction> Create(const base::Value& action, + static scoped_refptr<const SummingAction> Create(const Extension* extension, + const base::Value& action, std::string* error, bool* bad_message) { int increment = 0; @@ -255,7 +265,7 @@ TEST(DeclarativeActionTest, ErrorActionSet) { std::string error; bool bad = false; scoped_ptr<SummingActionSet> result = - SummingActionSet::Create(actions, &error, &bad); + SummingActionSet::Create(NULL, actions, &error, &bad); EXPECT_EQ("the error", error); EXPECT_FALSE(bad); EXPECT_FALSE(result); @@ -263,7 +273,7 @@ TEST(DeclarativeActionTest, ErrorActionSet) { actions.clear(); actions.push_back(ScopedToLinkedPtr(ParseJson("{\"value\": 1}"))); actions.push_back(ScopedToLinkedPtr(ParseJson("{\"bad\": 3}"))); - result = SummingActionSet::Create(actions, &error, &bad); + result = SummingActionSet::Create(NULL, actions, &error, &bad); EXPECT_EQ("", error); EXPECT_TRUE(bad); EXPECT_FALSE(result); @@ -280,7 +290,7 @@ TEST(DeclarativeActionTest, ApplyActionSet) { std::string error; bool bad = false; scoped_ptr<SummingActionSet> result = - SummingActionSet::Create(actions, &error, &bad); + SummingActionSet::Create(NULL, actions, &error, &bad); EXPECT_EQ("", error); EXPECT_FALSE(bad); ASSERT_TRUE(result); @@ -312,13 +322,17 @@ TEST(DeclarativeRuleTest, Create) { json_rule.get())); const char kExtensionId[] = "ext1"; + scoped_refptr<Extension> extension = ExtensionBuilder() + .SetManifest(SimpleManifest()) + .SetID(kExtensionId) + .Build(); base::Time install_time = base::Time::Now(); URLMatcher matcher; std::string error; scoped_ptr<Rule> rule(Rule::Create(matcher.condition_factory(), - kExtensionId, + extension.get(), install_time, json_rule, Rule::ConsistencyChecker(), @@ -365,6 +379,10 @@ TEST(DeclarativeRuleTest, CheckConsistency) { std::string error; linked_ptr<Rule::JsonRule> json_rule(new Rule::JsonRule); const char kExtensionId[] = "ext1"; + scoped_refptr<Extension> extension = ExtensionBuilder() + .SetManifest(SimpleManifest()) + .SetID(kExtensionId) + .Build(); ASSERT_TRUE(Rule::JsonRule::Populate( *ParseJson("{ \n" @@ -381,9 +399,12 @@ TEST(DeclarativeRuleTest, CheckConsistency) { " \"priority\": 200 \n" "}"), json_rule.get())); - scoped_ptr<Rule> rule( - Rule::Create(matcher.condition_factory(), kExtensionId, base::Time(), - json_rule, base::Bind(AtLeastOneCondition), &error)); + scoped_ptr<Rule> rule(Rule::Create(matcher.condition_factory(), + extension.get(), + base::Time(), + json_rule, + base::Bind(AtLeastOneCondition), + &error)); EXPECT_TRUE(rule); EXPECT_EQ("", error); @@ -400,8 +421,12 @@ TEST(DeclarativeRuleTest, CheckConsistency) { " \"priority\": 200 \n" "}"), json_rule.get())); - rule = Rule::Create(matcher.condition_factory(), kExtensionId, base::Time(), - json_rule, base::Bind(AtLeastOneCondition), &error); + rule = Rule::Create(matcher.condition_factory(), + extension.get(), + base::Time(), + json_rule, + base::Bind(AtLeastOneCondition), + &error); EXPECT_FALSE(rule); EXPECT_EQ("No conditions", error); } diff --git a/chrome/browser/extensions/api/declarative/rules_registry_with_cache.cc b/chrome/browser/extensions/api/declarative/rules_registry_with_cache.cc index e285eb761e..a452130fb0 100644 --- a/chrome/browser/extensions/api/declarative/rules_registry_with_cache.cc +++ b/chrome/browser/extensions/api/declarative/rules_registry_with_cache.cc @@ -16,6 +16,7 @@ #include "chrome/browser/extensions/extension_prefs.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/state_store.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/extensions/extension.h" @@ -402,7 +403,7 @@ RulesRegistryWithCache::RuleStorageOnUI::ReadRulesForInstalledExtensions() { (*i)->HasAPIPermission(APIPermission::kDeclarativeWebRequest); bool respects_off_the_record = !(profile_->IsOffTheRecord()) || - extension_service->IsIncognitoEnabled((*i)->id()); + extension_util::IsIncognitoEnabled((*i)->id(), extension_service); if (needs_apis_storing_rules && respects_off_the_record) ReadFromStorage((*i)->id()); } diff --git a/chrome/browser/extensions/api/declarative_content/content_action.cc b/chrome/browser/extensions/api/declarative_content/content_action.cc index a0531e0413..97ad637ef8 100644 --- a/chrome/browser/extensions/api/declarative_content/content_action.cc +++ b/chrome/browser/extensions/api/declarative_content/content_action.cc @@ -27,6 +27,8 @@ namespace { // Error messages. const char kInvalidInstanceTypeError[] = "An action has an invalid instanceType: %s"; +const char kNoPageAction[] = + "Can't use declarativeContent.ShowPageAction without a page action"; #define INPUT_FORMAT_VALIDATE(test) do { \ if (!(test)) { \ @@ -44,6 +46,18 @@ class ShowPageAction : public ContentAction { public: ShowPageAction() {} + static scoped_refptr<ContentAction> Create(const Extension* extension, + const base::DictionaryValue* dict, + std::string* error, + bool* bad_message) { + // We can't show a page action if the extension doesn't have one. + if (ActionInfo::GetPageActionInfo(extension) == NULL) { + *error = kNoPageAction; + return scoped_refptr<ContentAction>(); + } + return scoped_refptr<ContentAction>(new ShowPageAction); + } + // Implementation of ContentAction: virtual Type GetType() const OVERRIDE { return ACTION_SHOW_PAGE_ACTION; } virtual void Apply(const std::string& extension_id, @@ -80,33 +94,25 @@ class ShowPageAction : public ContentAction { DISALLOW_COPY_AND_ASSIGN(ShowPageAction); }; -// Helper function for ContentActions that can be instantiated by just -// calling the constructor. -template <class T> -scoped_refptr<ContentAction> CallConstructorFactoryMethod( - const base::DictionaryValue* dict, - std::string* error, - bool* bad_message) { - return scoped_refptr<ContentAction>(new T); -} - struct ContentActionFactory { - // Factory methods for ContentAction instances. |dict| contains the json - // dictionary that describes the action. |error| is used to return error - // messages in case the extension passed an action that was syntactically - // correct but semantically incorrect. |bad_message| is set to true in case - // |dict| does not confirm to the validated JSON specification. - typedef scoped_refptr<ContentAction> - (* FactoryMethod)(const base::DictionaryValue* /* dict */, - std::string* /* error */, - bool* /* bad_message */); + // Factory methods for ContentAction instances. |extension| is the extension + // for which the action is being created. |dict| contains the json dictionary + // that describes the action. |error| is used to return error messages in case + // the extension passed an action that was syntactically correct but + // semantically incorrect. |bad_message| is set to true in case |dict| does + // not confirm to the validated JSON specification. + typedef scoped_refptr<ContentAction>(*FactoryMethod)( + const Extension* /* extension */, + const base::DictionaryValue* /* dict */, + std::string* /* error */, + bool* /* bad_message */); // Maps the name of a declarativeContent action type to the factory // function creating it. std::map<std::string, FactoryMethod> factory_methods; ContentActionFactory() { factory_methods[keys::kShowPageAction] = - &CallConstructorFactoryMethod<ShowPageAction>; + &ShowPageAction::Create; } }; @@ -125,6 +131,7 @@ ContentAction::~ContentAction() {} // static scoped_refptr<ContentAction> ContentAction::Create( + const Extension* extension, const base::Value& json_action, std::string* error, bool* bad_message) { @@ -142,7 +149,8 @@ scoped_refptr<ContentAction> ContentAction::Create( std::map<std::string, ContentActionFactory::FactoryMethod>::iterator factory_method_iter = factory.factory_methods.find(instance_type); if (factory_method_iter != factory.factory_methods.end()) - return (*factory_method_iter->second)(action_dict, error, bad_message); + return (*factory_method_iter->second)( + extension, action_dict, error, bad_message); *error = base::StringPrintf(kInvalidInstanceTypeError, instance_type.c_str()); return scoped_refptr<ContentAction>(); diff --git a/chrome/browser/extensions/api/declarative_content/content_action.h b/chrome/browser/extensions/api/declarative_content/content_action.h index 799f5c1b90..404719feda 100644 --- a/chrome/browser/extensions/api/declarative_content/content_action.h +++ b/chrome/browser/extensions/api/declarative_content/content_action.h @@ -23,6 +23,7 @@ class WebContents; } namespace extensions { +class Extension; // Base class for all ContentActions of the declarative content API. class ContentAction : public base::RefCounted<ContentAction> { @@ -57,9 +58,10 @@ class ContentAction : public base::RefCounted<ContentAction> { // Sets |error| and returns NULL in case of a semantic error that cannot // be caught by schema validation. Sets |bad_message| and returns NULL // in case the input is syntactically unexpected. - static scoped_refptr<ContentAction> Create(const base::Value& json_action, - std::string* error, - bool* bad_message); + static scoped_refptr<ContentAction> Create(const Extension* extension, + const base::Value& json_action, + std::string* error, + bool* bad_message); protected: friend class base::RefCounted<ContentAction>; diff --git a/chrome/browser/extensions/api/declarative_content/content_action_unittest.cc b/chrome/browser/extensions/api/declarative_content/content_action_unittest.cc index 049cc6c530..47d2a70511 100644 --- a/chrome/browser/extensions/api/declarative_content/content_action_unittest.cc +++ b/chrome/browser/extensions/api/declarative_content/content_action_unittest.cc @@ -9,6 +9,7 @@ #include "chrome/browser/extensions/extension_action_manager.h" #include "chrome/browser/extensions/extension_tab_util.h" #include "chrome/browser/extensions/test_extension_environment.h" +#include "chrome/common/extensions/extension_builder.h" #include "chrome/test/base/testing_profile.h" #include "content/public/browser/web_contents.h" #include "testing/gmock/include/gmock/gmock.h" @@ -28,21 +29,21 @@ TEST(DeclarativeContentActionTest, InvalidCreation) { // Test wrong data type passed. error.clear(); - result = ContentAction::Create(*ParseJson("[]"), &error, &bad_message); + result = ContentAction::Create(NULL, *ParseJson("[]"), &error, &bad_message); EXPECT_TRUE(bad_message); EXPECT_EQ("", error); EXPECT_FALSE(result.get()); // Test missing instanceType element. error.clear(); - result = ContentAction::Create(*ParseJson("{}"), &error, &bad_message); + result = ContentAction::Create(NULL, *ParseJson("{}"), &error, &bad_message); EXPECT_TRUE(bad_message); EXPECT_EQ("", error); EXPECT_FALSE(result.get()); // Test wrong instanceType element. error.clear(); - result = ContentAction::Create(*ParseJson( + result = ContentAction::Create(NULL, *ParseJson( "{\n" " \"instanceType\": \"declarativeContent.UnknownType\",\n" "}"), @@ -51,23 +52,45 @@ TEST(DeclarativeContentActionTest, InvalidCreation) { EXPECT_FALSE(result.get()); } +TEST(DeclarativeContentActionTest, ShowPageActionWithoutPageAction) { + TestExtensionEnvironment env; + + const Extension* extension = env.MakeExtension(base::DictionaryValue()); + std::string error; + bool bad_message = false; + scoped_refptr<const ContentAction> result = ContentAction::Create( + extension, + *ParseJson( + "{\n" + " \"instanceType\": \"declarativeContent.ShowPageAction\",\n" + "}"), + &error, + &bad_message); + EXPECT_THAT(error, testing::HasSubstr("without a page action")); + EXPECT_FALSE(bad_message); + ASSERT_FALSE(result.get()); +} + TEST(DeclarativeContentActionTest, ShowPageAction) { TestExtensionEnvironment env; + const Extension* extension = env.MakeExtension( + *ParseJson("{\"page_action\": { \"default_title\": \"Extension\" } }")); std::string error; bool bad_message = false; scoped_refptr<const ContentAction> result = ContentAction::Create( - *ParseJson("{\n" - " \"instanceType\": \"declarativeContent.ShowPageAction\",\n" - "}"), - &error, &bad_message); + extension, + *ParseJson( + "{\n" + " \"instanceType\": \"declarativeContent.ShowPageAction\",\n" + "}"), + &error, + &bad_message); EXPECT_EQ("", error); EXPECT_FALSE(bad_message); ASSERT_TRUE(result.get()); EXPECT_EQ(ContentAction::ACTION_SHOW_PAGE_ACTION, result->GetType()); - const Extension* extension = env.MakeExtension( - *ParseJson("{\"page_action\": { \"default_title\": \"Extension\" } }")); ExtensionAction* page_action = ExtensionActionManager::Get(env.profile())->GetPageAction(*extension); scoped_ptr<content::WebContents> contents = env.MakeTab(); diff --git a/chrome/browser/extensions/api/declarative_content/content_condition.cc b/chrome/browser/extensions/api/declarative_content/content_condition.cc index 1d83093e10..74319f8337 100644 --- a/chrome/browser/extensions/api/declarative_content/content_condition.cc +++ b/chrome/browser/extensions/api/declarative_content/content_condition.cc @@ -63,6 +63,7 @@ bool ContentCondition::IsFulfilled( // static scoped_ptr<ContentCondition> ContentCondition::Create( + const Extension* extension, URLMatcherConditionFactory* url_matcher_condition_factory, const base::Value& condition, std::string* error) { diff --git a/chrome/browser/extensions/api/declarative_content/content_condition.h b/chrome/browser/extensions/api/declarative_content/content_condition.h index abb86d60bd..389f301e78 100644 --- a/chrome/browser/extensions/api/declarative_content/content_condition.h +++ b/chrome/browser/extensions/api/declarative_content/content_condition.h @@ -62,6 +62,7 @@ class ContentCondition { // description |condition| passed by the extension API. |condition| should be // an instance of declarativeContent.PageStateMatcher. static scoped_ptr<ContentCondition> Create( + const Extension* extension, URLMatcherConditionFactory* url_matcher_condition_factory, const base::Value& condition, std::string* error); diff --git a/chrome/browser/extensions/api/declarative_content/content_condition_unittest.cc b/chrome/browser/extensions/api/declarative_content/content_condition_unittest.cc index 557fbff8f5..f4cdb8ef6e 100644 --- a/chrome/browser/extensions/api/declarative_content/content_condition_unittest.cc +++ b/chrome/browser/extensions/api/declarative_content/content_condition_unittest.cc @@ -24,12 +24,13 @@ TEST(DeclarativeContentConditionTest, UnknownConditionName) { URLMatcher matcher; std::string error; scoped_ptr<ContentCondition> result = ContentCondition::Create( + NULL, matcher.condition_factory(), *base::test::ParseJson( - "{\n" - " \"invalid\": \"foobar\",\n" - " \"instanceType\": \"declarativeContent.PageStateMatcher\",\n" - "}"), + "{\n" + " \"invalid\": \"foobar\",\n" + " \"instanceType\": \"declarativeContent.PageStateMatcher\",\n" + "}"), &error); EXPECT_THAT(error, HasSubstr("Unknown condition attribute")); EXPECT_FALSE(result); @@ -41,6 +42,7 @@ TEST(DeclarativeContentConditionTest, WrongPageUrlDatatype) { URLMatcher matcher; std::string error; scoped_ptr<ContentCondition> result = ContentCondition::Create( + NULL, matcher.condition_factory(), *base::test::ParseJson( "{\n" @@ -58,6 +60,7 @@ TEST(DeclarativeContentConditionTest, WrongCssDatatype) { URLMatcher matcher; std::string error; scoped_ptr<ContentCondition> result = ContentCondition::Create( + NULL, matcher.condition_factory(), *base::test::ParseJson( "{\n" @@ -76,6 +79,7 @@ TEST(DeclarativeContentConditionTest, ConditionWithUrlAndCss) { std::string error; scoped_ptr<ContentCondition> result = ContentCondition::Create( + NULL, matcher.condition_factory(), *base::test::ParseJson( "{\n" diff --git a/chrome/browser/extensions/api/declarative_content/content_rules_registry.cc b/chrome/browser/extensions/api/declarative_content/content_rules_registry.cc index 63b8947b70..99f1490824 100644 --- a/chrome/browser/extensions/api/declarative_content/content_rules_registry.cc +++ b/chrome/browser/extensions/api/declarative_content/content_rules_registry.cc @@ -8,6 +8,7 @@ #include "chrome/browser/extensions/api/declarative_content/content_action.h" #include "chrome/browser/extensions/api/declarative_content/content_condition.h" #include "chrome/browser/extensions/api/declarative_content/content_constants.h" +#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/extensions/extension_tab_util.h" #include "chrome/browser/profiles/profile.h" @@ -137,6 +138,11 @@ ContentRulesRegistry::GetMatches( std::string ContentRulesRegistry::AddRulesImpl( const std::string& extension_id, const std::vector<linked_ptr<RulesRegistry::Rule> >& rules) { + ExtensionService* service = + ExtensionSystem::Get(profile_)->extension_service(); + const Extension* extension = service->GetInstalledExtension(extension_id); + DCHECK(extension) << "Must have extension with id " << extension_id; + base::Time extension_installation_time = GetExtensionInstallationTime(extension_id); @@ -150,7 +156,7 @@ std::string ContentRulesRegistry::AddRulesImpl( scoped_ptr<ContentRule> content_rule( ContentRule::Create(url_matcher_.condition_factory(), - extension_id, + extension, extension_installation_time, *rule, ContentRule::ConsistencyChecker(), diff --git a/chrome/browser/extensions/api/declarative_content/declarative_content_apitest.cc b/chrome/browser/extensions/api/declarative_content/declarative_content_apitest.cc index 33c179d37f..395012cf20 100644 --- a/chrome/browser/extensions/api/declarative_content/declarative_content_apitest.cc +++ b/chrome/browser/extensions/api/declarative_content/declarative_content_apitest.cc @@ -28,21 +28,27 @@ const char kDeclarativeContentManifest[] = " \"background\": {\n" " \"scripts\": [\"background.js\"]\n" " },\n" + " \"page_action\": {},\n" " \"permissions\": [\n" " \"declarativeContent\"\n" - " ],\n" - " \"page_action\": {}\n" + " ]\n" "}\n"; const char kBackgroundHelpers[] = "var PageStateMatcher = chrome.declarativeContent.PageStateMatcher;\n" "var ShowPageAction = chrome.declarativeContent.ShowPageAction;\n" "var onPageChanged = chrome.declarativeContent.onPageChanged;\n" + "var Reply = window.domAutomationController.send.bind(\n" + " window.domAutomationController);\n" "\n" "function setRules(rules, responseString) {\n" " onPageChanged.removeRules(undefined, function() {\n" " onPageChanged.addRules(rules, function() {\n" - " window.domAutomationController.send(responseString);\n" + " if (chrome.runtime.lastError) {\n" + " Reply(chrome.runtime.lastError.message);\n" + " return;\n" + " }\n" + " Reply(responseString);\n" " });\n" " });\n" "};\n"; @@ -233,6 +239,37 @@ IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, } IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, + ShowPageActionWithoutPageAction) { + std::string manifest_without_page_action = kDeclarativeContentManifest; + ReplaceSubstringsAfterOffset( + &manifest_without_page_action, 0, "\"page_action\": {},", ""); + ext_dir_.WriteManifest(manifest_without_page_action); + ext_dir_.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundHelpers); + const Extension* extension = LoadExtension(ext_dir_.unpacked_path()); + ASSERT_TRUE(extension); + + EXPECT_THAT(ExecuteScriptInBackgroundPage( + extension->id(), + "setRules([{\n" + " conditions: [new PageStateMatcher({\n" + " pageUrl: {hostPrefix: \"test\"}})],\n" + " actions: [new ShowPageAction()]\n" + "}], 'test_rule');\n"), + testing::HasSubstr("without a page action")); + + content::WebContents* const tab = + browser()->tab_strip_model()->GetWebContentsAt(0); + NavigateInRenderer(tab, GURL("http://test/")); + + EXPECT_EQ(NULL, + ExtensionActionManager::Get(browser()->profile())-> + GetPageAction(*extension)); + EXPECT_EQ(0, + browser()->window()->GetLocationBar()->GetLocationBarForTesting()-> + PageActionCount()); +} + +IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, CanonicalizesPageStateMatcherCss) { ext_dir_.WriteManifest(kDeclarativeContentManifest); ext_dir_.WriteFile( diff --git a/chrome/browser/extensions/api/declarative_webrequest/webrequest_action.cc b/chrome/browser/extensions/api/declarative_webrequest/webrequest_action.cc index fb38c55156..4da726e9fb 100644 --- a/chrome/browser/extensions/api/declarative_webrequest/webrequest_action.cc +++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_action.cc @@ -467,6 +467,7 @@ bool WebRequestAction::HasPermission(const ExtensionInfoMap* extension_info_map, // static scoped_refptr<const WebRequestAction> WebRequestAction::Create( + const Extension* extension, const base::Value& json_action, std::string* error, bool* bad_message) { diff --git a/chrome/browser/extensions/api/declarative_webrequest/webrequest_action.h b/chrome/browser/extensions/api/declarative_webrequest/webrequest_action.h index faa70cfa5d..07fa95c869 100644 --- a/chrome/browser/extensions/api/declarative_webrequest/webrequest_action.h +++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_action.h @@ -134,6 +134,7 @@ class WebRequestAction : public base::RefCounted<WebRequestAction> { // be caught by schema validation. Sets |bad_message| and returns NULL // in case the input is syntactically unexpected. static scoped_refptr<const WebRequestAction> Create( + const Extension* extension, const base::Value& json_action, std::string* error, bool* bad_message); diff --git a/chrome/browser/extensions/api/declarative_webrequest/webrequest_action_unittest.cc b/chrome/browser/extensions/api/declarative_webrequest/webrequest_action_unittest.cc index 41d3d9194b..0c4925fdb6 100644 --- a/chrome/browser/extensions/api/declarative_webrequest/webrequest_action_unittest.cc +++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_action_unittest.cc @@ -57,7 +57,7 @@ scoped_ptr<WebRequestActionSet> CreateSetOfActions(const char* json) { bool bad_message = false; scoped_ptr<WebRequestActionSet> action_set( - WebRequestActionSet::Create(actions, &error, &bad_message)); + WebRequestActionSet::Create(NULL, actions, &error, &bad_message)); EXPECT_EQ("", error); EXPECT_FALSE(bad_message); CHECK(action_set); @@ -180,28 +180,28 @@ TEST(WebRequestActionTest, CreateAction) { // Test wrong data type passed. error.clear(); base::ListValue empty_list; - result = WebRequestAction::Create(empty_list, &error, &bad_message); + result = WebRequestAction::Create(NULL, empty_list, &error, &bad_message); EXPECT_TRUE(bad_message); EXPECT_FALSE(result.get()); // Test missing instanceType element. base::DictionaryValue input; error.clear(); - result = WebRequestAction::Create(input, &error, &bad_message); + result = WebRequestAction::Create(NULL, input, &error, &bad_message); EXPECT_TRUE(bad_message); EXPECT_FALSE(result.get()); // Test wrong instanceType element. input.SetString(keys::kInstanceTypeKey, kUnknownActionType); error.clear(); - result = WebRequestAction::Create(input, &error, &bad_message); + result = WebRequestAction::Create(NULL, input, &error, &bad_message); EXPECT_NE("", error); EXPECT_FALSE(result.get()); // Test success input.SetString(keys::kInstanceTypeKey, keys::kCancelRequestType); error.clear(); - result = WebRequestAction::Create(input, &error, &bad_message); + result = WebRequestAction::Create(NULL, input, &error, &bad_message); EXPECT_EQ("", error); EXPECT_FALSE(bad_message); ASSERT_TRUE(result.get()); @@ -217,7 +217,7 @@ TEST(WebRequestActionTest, CreateActionSet) { // Test empty input. error.clear(); - result = WebRequestActionSet::Create(input, &error, &bad_message); + result = WebRequestActionSet::Create(NULL, input, &error, &bad_message); EXPECT_TRUE(error.empty()) << error; EXPECT_FALSE(bad_message); ASSERT_TRUE(result.get()); @@ -233,7 +233,7 @@ TEST(WebRequestActionTest, CreateActionSet) { // Test success. input.push_back(linked_ptr<base::Value>(correct_action.DeepCopy())); error.clear(); - result = WebRequestActionSet::Create(input, &error, &bad_message); + result = WebRequestActionSet::Create(NULL, input, &error, &bad_message); EXPECT_TRUE(error.empty()) << error; EXPECT_FALSE(bad_message); ASSERT_TRUE(result.get()); @@ -245,7 +245,7 @@ TEST(WebRequestActionTest, CreateActionSet) { // Test failure. input.push_back(linked_ptr<base::Value>(incorrect_action.DeepCopy())); error.clear(); - result = WebRequestActionSet::Create(input, &error, &bad_message); + result = WebRequestActionSet::Create(NULL, input, &error, &bad_message); EXPECT_NE("", error); EXPECT_FALSE(result.get()); } diff --git a/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.cc b/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.cc index 3ac29d6ce4..1e1ddec101 100644 --- a/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.cc +++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.cc @@ -122,6 +122,7 @@ void WebRequestCondition::GetURLMatcherConditionSets( // static scoped_ptr<WebRequestCondition> WebRequestCondition::Create( + const Extension* extension, URLMatcherConditionFactory* url_matcher_condition_factory, const base::Value& condition, std::string* error) { diff --git a/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.h b/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.h index e017a6ab6b..a9c9355482 100644 --- a/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.h +++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.h @@ -78,6 +78,7 @@ class WebRequestCondition { // Factory method that instantiates a WebRequestCondition according to // the description |condition| passed by the extension API. static scoped_ptr<WebRequestCondition> Create( + const Extension* extension, URLMatcherConditionFactory* url_matcher_condition_factory, const base::Value& condition, std::string* error); diff --git a/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_unittest.cc b/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_unittest.cc index 9fcacc56cb..d30d1a6b3b 100644 --- a/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_unittest.cc +++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_unittest.cc @@ -31,11 +31,12 @@ TEST(WebRequestConditionTest, CreateCondition) { // Test wrong condition name passed. error.clear(); result = WebRequestCondition::Create( + NULL, matcher.condition_factory(), *base::test::ParseJson( - "{ \"invalid\": \"foobar\", \n" - " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n" - "}"), + "{ \"invalid\": \"foobar\", \n" + " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n" + "}"), &error); EXPECT_FALSE(error.empty()); EXPECT_FALSE(result.get()); @@ -43,12 +44,13 @@ TEST(WebRequestConditionTest, CreateCondition) { // Test wrong datatype in host_suffix. error.clear(); result = WebRequestCondition::Create( + NULL, matcher.condition_factory(), *base::test::ParseJson( - "{ \n" - " \"url\": [], \n" - " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n" - "}"), + "{ \n" + " \"url\": [], \n" + " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n" + "}"), &error); EXPECT_FALSE(error.empty()); EXPECT_FALSE(result.get()); @@ -56,13 +58,14 @@ TEST(WebRequestConditionTest, CreateCondition) { // Test success (can we support multiple criteria?) error.clear(); result = WebRequestCondition::Create( + NULL, matcher.condition_factory(), *base::test::ParseJson( - "{ \n" - " \"resourceType\": [\"main_frame\"], \n" - " \"url\": { \"hostSuffix\": \"example.com\" }, \n" - " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n" - "}"), + "{ \n" + " \"resourceType\": [\"main_frame\"], \n" + " \"url\": { \"hostSuffix\": \"example.com\" }, \n" + " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n" + "}"), &error); EXPECT_EQ("", error); ASSERT_TRUE(result.get()); @@ -102,12 +105,13 @@ TEST(WebRequestConditionTest, CreateConditionFirstPartyForCookies) { scoped_ptr<WebRequestCondition> result; result = WebRequestCondition::Create( + NULL, matcher.condition_factory(), *base::test::ParseJson( - "{ \n" - " \"firstPartyForCookiesUrl\": { \"hostPrefix\": \"fpfc\"}, \n" - " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n" - "}"), + "{ \n" + " \"firstPartyForCookiesUrl\": { \"hostPrefix\": \"fpfc\"}, \n" + " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n" + "}"), &error); EXPECT_EQ("", error); ASSERT_TRUE(result.get()); @@ -146,11 +150,12 @@ TEST(WebRequestConditionTest, NoUrlAttributes) { // The empty condition. error.clear(); scoped_ptr<WebRequestCondition> condition_empty = WebRequestCondition::Create( + NULL, matcher.condition_factory(), *base::test::ParseJson( - "{ \n" - " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n" - "}"), + "{ \n" + " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n" + "}"), &error); EXPECT_EQ("", error); ASSERT_TRUE(condition_empty.get()); @@ -159,14 +164,16 @@ TEST(WebRequestConditionTest, NoUrlAttributes) { error.clear(); scoped_ptr<WebRequestCondition> condition_no_url_true = WebRequestCondition::Create( + NULL, matcher.condition_factory(), *base::test::ParseJson( - "{ \n" - " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n" - // There is no "1st party for cookies" URL in the requests below, - // therefore all requests are considered first party for cookies. - " \"thirdPartyForCookies\": false, \n" - "}"), + "{ \n" + " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", " + "\n" + // There is no "1st party for cookies" URL in the requests below, + // therefore all requests are considered first party for cookies. + " \"thirdPartyForCookies\": false, \n" + "}"), &error); EXPECT_EQ("", error); ASSERT_TRUE(condition_no_url_true.get()); @@ -175,12 +182,14 @@ TEST(WebRequestConditionTest, NoUrlAttributes) { error.clear(); scoped_ptr<WebRequestCondition> condition_no_url_false = WebRequestCondition::Create( + NULL, matcher.condition_factory(), *base::test::ParseJson( - "{ \n" - " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n" - " \"thirdPartyForCookies\": true, \n" - "}"), + "{ \n" + " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", " + "\n" + " \"thirdPartyForCookies\": true, \n" + "}"), &error); EXPECT_EQ("", error); ASSERT_TRUE(condition_no_url_false.get()); @@ -231,9 +240,8 @@ TEST(WebRequestConditionTest, CreateConditionSet) { // Test insertion std::string error; - scoped_ptr<WebRequestConditionSet> result = - WebRequestConditionSet::Create(matcher.condition_factory(), - conditions, &error); + scoped_ptr<WebRequestConditionSet> result = WebRequestConditionSet::Create( + NULL, matcher.condition_factory(), conditions, &error); EXPECT_EQ("", error); ASSERT_TRUE(result.get()); EXPECT_EQ(2u, result->conditions().size()); @@ -289,9 +297,8 @@ TEST(WebRequestConditionTest, TestPortFilter) { // Test insertion std::string error; - scoped_ptr<WebRequestConditionSet> result = - WebRequestConditionSet::Create(matcher.condition_factory(), - conditions, &error); + scoped_ptr<WebRequestConditionSet> result = WebRequestConditionSet::Create( + NULL, matcher.condition_factory(), conditions, &error); EXPECT_EQ("", error); ASSERT_TRUE(result.get()); EXPECT_EQ(1u, result->conditions().size()); @@ -340,15 +347,16 @@ TEST(WebRequestConditionTest, ConditionsWithConflictingStages) { // Test error on incompatible application stages for involved attributes. error.clear(); result = WebRequestCondition::Create( + NULL, matcher.condition_factory(), *base::test::ParseJson( - "{ \n" - " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n" - // Pass a JS array with one empty object to each of the header - // filters. - " \"requestHeaders\": [{}], \n" - " \"responseHeaders\": [{}], \n" - "}"), + "{ \n" + " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n" + // Pass a JS array with one empty object to each of the header + // filters. + " \"requestHeaders\": [{}], \n" + " \"responseHeaders\": [{}], \n" + "}"), &error); EXPECT_FALSE(error.empty()); EXPECT_FALSE(result.get()); diff --git a/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry.cc b/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry.cc index a72edc5d6c..7dc19f7ade 100644 --- a/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry.cc +++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry.cc @@ -178,7 +178,7 @@ std::string WebRequestRulesRegistry::AddRulesImpl( scoped_ptr<WebRequestRule> webrequest_rule(WebRequestRule::Create( url_matcher_.condition_factory(), - extension_id, extension_installation_time, *rule, + extension, extension_installation_time, *rule, base::Bind(&Checker, base::Unretained(extension)), &error)); if (!error.empty()) { diff --git a/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc b/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc index 2b345147fd..29f7791d5c 100644 --- a/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc +++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc @@ -690,13 +690,13 @@ TEST(WebRequestRulesRegistrySimpleTest, StageChecker) { URLMatcher matcher; scoped_ptr<WebRequestConditionSet> conditions = WebRequestConditionSet::Create( - matcher.condition_factory(), rule.conditions, &error); + NULL, matcher.condition_factory(), rule.conditions, &error); ASSERT_TRUE(error.empty()) << error; ASSERT_TRUE(conditions); bool bad_message = false; scoped_ptr<WebRequestActionSet> actions = - WebRequestActionSet::Create(rule.actions, &error, &bad_message); + WebRequestActionSet::Create(NULL, rule.actions, &error, &bad_message); ASSERT_TRUE(error.empty()) << error; ASSERT_FALSE(bad_message); ASSERT_TRUE(actions); @@ -723,7 +723,7 @@ TEST(WebRequestRulesRegistrySimpleTest, HostPermissionsChecker) { std::string error; bool bad_message = false; scoped_ptr<WebRequestActionSet> action_set( - WebRequestActionSet::Create(actions, &error, &bad_message)); + WebRequestActionSet::Create(NULL, actions, &error, &bad_message)); ASSERT_TRUE(error.empty()) << error; ASSERT_FALSE(bad_message); ASSERT_TRUE(action_set); diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.cc b/chrome/browser/extensions/api/developer_private/developer_private_api.cc index bd92c260a2..eb81a66e71 100644 --- a/chrome/browser/extensions/api/developer_private/developer_private_api.cc +++ b/chrome/browser/extensions/api/developer_private/developer_private_api.cc @@ -27,6 +27,7 @@ #include "chrome/browser/extensions/extension_error_reporter.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/management_policy.h" #include "chrome/browser/extensions/unpacked_installer.h" #include "chrome/browser/extensions/updater/extension_updater.h" @@ -341,9 +342,10 @@ scoped_ptr<developer::ItemInfo> } } - info->incognito_enabled = service->IsIncognitoEnabled(item.id()); + info->incognito_enabled = + extension_util::IsIncognitoEnabled(item.id(),service); info->wants_file_access = item.wants_file_access(); - info->allow_file_access = service->AllowFileAccess(&item); + info->allow_file_access = extension_util::AllowFileAccess(&item, service); info->allow_reload = Manifest::IsUnpackedLocation(item.location()); info->is_unpacked = Manifest::IsUnpackedLocation(item.location()); info->terminated = service->terminated_extensions()->Contains(item.id()); @@ -608,7 +610,7 @@ bool DeveloperPrivateAllowFileAccessFunction::RunImpl() { << extension->id(); result = false; } else { - service->SetAllowFileAccess(extension, params->allow); + extension_util::SetAllowFileAccess(extension, service, params->allow); result = true; } @@ -630,7 +632,8 @@ bool DeveloperPrivateAllowIncognitoFunction::RunImpl() { if (!extension) result = false; else - service->SetIsIncognitoEnabled(extension->id(), params->allow); + extension_util::SetIsIncognitoEnabled( + extension->id(),service, params->allow); return result; } diff --git a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc index 6c819b0583..a26c662a26 100644 --- a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc +++ b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc @@ -473,6 +473,7 @@ class DownloadExtensionTest : public ExtensionApiTest { GURL slow_download_url(URLRequestSlowDownloadJob::kUnknownSizeUrl); DownloadManager* manager = GetCurrentManager(); + EXPECT_EQ(0, manager->NonMaliciousInProgressCount()); EXPECT_EQ(0, manager->InProgressCount()); if (manager->InProgressCount() != 0) return NULL; @@ -3345,6 +3346,7 @@ IN_PROC_BROWSER_TEST_F( scoped_ptr<content::DownloadTestObserver> observer( new JustInProgressDownloadObserver(manager, 1)); ASSERT_EQ(0, manager->InProgressCount()); + ASSERT_EQ(0, manager->NonMaliciousInProgressCount()); // Tabs created just for a download are automatically closed, invalidating // the download's WebContents. Downloads without WebContents cannot be // resumed. http://crbug.com/225901 diff --git a/chrome/browser/extensions/api/enterprise_platform_keys_private/OWNERS b/chrome/browser/extensions/api/enterprise_platform_keys_private/OWNERS index 14072cbf8d..a48744dcca 100644 --- a/chrome/browser/extensions/api/enterprise_platform_keys_private/OWNERS +++ b/chrome/browser/extensions/api/enterprise_platform_keys_private/OWNERS @@ -1,2 +1,3 @@ mnissler@chromium.org - +pastarmovj@chromium.org +bartfab@chromium.org diff --git a/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.cc b/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.cc index a5a70a7393..b1078c6b94 100644 --- a/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.cc +++ b/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.cc @@ -146,18 +146,20 @@ std::string EPKPChallengeKeyBase::GetDeviceId() const { void EPKPChallengeKeyBase::PrepareKey( chromeos::attestation::AttestationKeyType key_type, + const std::string& user_id, const std::string& key_name, chromeos::attestation::AttestationCertificateProfile certificate_profile, bool require_user_consent, const base::Callback<void(PrepareKeyResult)>& callback) { cryptohome_client_->TpmAttestationDoesKeyExist( - key_type, key_name, base::Bind( + key_type, user_id, key_name, base::Bind( &EPKPChallengeKeyBase::DoesKeyExistCallback, this, - certificate_profile, require_user_consent, callback)); + certificate_profile, user_id, require_user_consent, callback)); } void EPKPChallengeKeyBase::DoesKeyExistCallback( chromeos::attestation::AttestationCertificateProfile certificate_profile, + const std::string& user_id, bool require_user_consent, const base::Callback<void(PrepareKeyResult)>& callback, chromeos::DBusMethodCallStatus status, @@ -177,10 +179,11 @@ void EPKPChallengeKeyBase::DoesKeyExistCallback( // information to PCA. AskForUserConsent( base::Bind(&EPKPChallengeKeyBase::AskForUserConsentCallback, this, - certificate_profile, callback)); + certificate_profile, user_id, callback)); } else { // User consent is not required. Skip to the next step. - AskForUserConsentCallback(certificate_profile, callback, true); + AskForUserConsentCallback(certificate_profile, user_id, callback, + true); } } } @@ -194,6 +197,7 @@ void EPKPChallengeKeyBase::AskForUserConsent( void EPKPChallengeKeyBase::AskForUserConsentCallback( chromeos::attestation::AttestationCertificateProfile certificate_profile, + const std::string& user_id, const base::Callback<void(PrepareKeyResult)>& callback, bool result) { if (!result) { @@ -205,7 +209,7 @@ void EPKPChallengeKeyBase::AskForUserConsentCallback( // Generate a new key and have it signed by PCA. attestation_flow_->GetCertificate( certificate_profile, - std::string(), // Not used. + user_id, std::string(), // Not used. true, // Force a new key to be generated. base::Bind(&EPKPChallengeKeyBase::GetCertificateCallback, this, @@ -296,6 +300,7 @@ void EPKPChallengeMachineKey::GetDeviceAttestationEnabledCallback( } PrepareKey(chromeos::attestation::KEY_DEVICE, + std::string(), // Not used. kKeyName, chromeos::attestation::PROFILE_ENTERPRISE_MACHINE_CERTIFICATE, false, // user consent is not required. @@ -314,6 +319,7 @@ void EPKPChallengeMachineKey::PrepareKeyCallback( // Everything is checked. Sign the challenge. async_caller_->TpmAttestationSignEnterpriseChallenge( chromeos::attestation::KEY_DEVICE, + std::string(), // Not used. kKeyName, GetEnterpriseDomain(), GetDeviceId(), @@ -441,6 +447,7 @@ void EPKPChallengeUserKey::GetDeviceAttestationEnabledCallback( } PrepareKey(chromeos::attestation::KEY_USER, + GetUserEmail(), kKeyName, chromeos::attestation::PROFILE_ENTERPRISE_USER_CERTIFICATE, require_user_consent, @@ -460,6 +467,7 @@ void EPKPChallengeUserKey::PrepareKeyCallback(const std::string& challenge, // Everything is checked. Sign the challenge. async_caller_->TpmAttestationSignEnterpriseChallenge( chromeos::attestation::KEY_USER, + GetUserEmail(), kKeyName, GetUserEmail(), GetDeviceId(), @@ -483,6 +491,7 @@ void EPKPChallengeUserKey::SignChallengeCallback(bool register_key, if (register_key) { async_caller_->TpmAttestationRegisterKey( chromeos::attestation::KEY_USER, + GetUserEmail(), kKeyName, base::Bind(&EPKPChallengeUserKey::RegisterKeyCallback, this, response)); } else { diff --git a/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.h b/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.h index 2619f6035a..80e6b9fb6b 100644 --- a/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.h +++ b/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.h @@ -92,6 +92,7 @@ class EPKPChallengeKeyBase : public AsyncExtensionFunction { // user consent before calling GetCertificate(). void PrepareKey( chromeos::attestation::AttestationKeyType key_type, + const std::string& user_id, const std::string& key_name, chromeos::attestation::AttestationCertificateProfile certificate_profile, bool require_user_consent, @@ -105,6 +106,7 @@ class EPKPChallengeKeyBase : public AsyncExtensionFunction { private: void DoesKeyExistCallback( chromeos::attestation::AttestationCertificateProfile certificate_profile, + const std::string& user_id, bool require_user_consent, const base::Callback<void(PrepareKeyResult)>& callback, chromeos::DBusMethodCallStatus status, @@ -112,6 +114,7 @@ class EPKPChallengeKeyBase : public AsyncExtensionFunction { void AskForUserConsent(const base::Callback<void(bool)>& callback) const; void AskForUserConsentCallback( chromeos::attestation::AttestationCertificateProfile certificate_profile, + const std::string& user_id, const base::Callback<void(PrepareKeyResult)>& callback, bool result); void GetCertificateCallback( diff --git a/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc b/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc index f592549db3..c4698745e4 100644 --- a/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc +++ b/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc @@ -38,6 +38,7 @@ namespace { void DoesKeyExistCallbackTrue( chromeos::attestation::AttestationKeyType key_type, + const std::string& user_id, const std::string& key_name, const chromeos::BoolDBusMethodCallback& callback) { callback.Run(chromeos::DBUS_METHOD_CALL_SUCCESS, true); @@ -45,6 +46,7 @@ void DoesKeyExistCallbackTrue( void DoesKeyExistCallbackFalse( chromeos::attestation::AttestationKeyType key_type, + const std::string& user_id, const std::string& key_name, const chromeos::BoolDBusMethodCallback& callback) { callback.Run(chromeos::DBUS_METHOD_CALL_SUCCESS, false); @@ -52,6 +54,7 @@ void DoesKeyExistCallbackFalse( void DoesKeyExistCallbackFailed( chromeos::attestation::AttestationKeyType key_type, + const std::string& user_id, const std::string& key_name, const chromeos::BoolDBusMethodCallback& callback) { callback.Run(chromeos::DBUS_METHOD_CALL_FAILURE, false); @@ -59,6 +62,7 @@ void DoesKeyExistCallbackFailed( void RegisterKeyCallbackTrue( chromeos::attestation::AttestationKeyType key_type, + const std::string& user_id, const std::string& key_name, const cryptohome::AsyncMethodCaller::Callback& callback) { callback.Run(true, cryptohome::MOUNT_ERROR_NONE); @@ -66,6 +70,7 @@ void RegisterKeyCallbackTrue( void RegisterKeyCallbackFalse( chromeos::attestation::AttestationKeyType key_type, + const std::string& user_id, const std::string& key_name, const cryptohome::AsyncMethodCaller::Callback& callback) { callback.Run(false, cryptohome::MOUNT_ERROR_NONE); @@ -73,6 +78,7 @@ void RegisterKeyCallbackFalse( void SignChallengeCallbackTrue( chromeos::attestation::AttestationKeyType key_type, + const std::string& user_id, const std::string& key_name, const std::string& domain, const std::string& device_id, @@ -84,6 +90,7 @@ void SignChallengeCallbackTrue( void SignChallengeCallbackFalse( chromeos::attestation::AttestationKeyType key_type, + const std::string& user_id, const std::string& key_name, const std::string& domain, const std::string& device_id, @@ -95,7 +102,7 @@ void SignChallengeCallbackFalse( void GetCertificateCallbackTrue( chromeos::attestation::AttestationCertificateProfile certificate_profile, - const std::string& user_email, + const std::string& user_id, const std::string& request_origin, bool force_new_key, const chromeos::attestation::AttestationFlow::CertificateCallback& @@ -105,7 +112,7 @@ void GetCertificateCallbackTrue( void GetCertificateCallbackFalse( chromeos::attestation::AttestationCertificateProfile certificate_profile, - const std::string& user_email, + const std::string& user_id, const std::string& request_origin, bool force_new_key, const chromeos::attestation::AttestationFlow::CertificateCallback& @@ -118,12 +125,12 @@ class EPKPChallengeKeyTestBase : public BrowserWithTestWindowTest { EPKPChallengeKeyTestBase() : extension_(utils::CreateEmptyExtension("")) { // Set up the default behavior of mocks. - ON_CALL(mock_cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _)) + ON_CALL(mock_cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _, _)) .WillByDefault(Invoke(DoesKeyExistCallbackFalse)); - ON_CALL(mock_async_method_caller_, TpmAttestationRegisterKey(_, _, _)) + ON_CALL(mock_async_method_caller_, TpmAttestationRegisterKey(_, _, _, _)) .WillByDefault(Invoke(RegisterKeyCallbackTrue)); ON_CALL(mock_async_method_caller_, - TpmAttestationSignEnterpriseChallenge(_, _, _, _, _, _, _)) + TpmAttestationSignEnterpriseChallenge(_, _, _, _, _, _, _, _)) .WillByDefault(Invoke(SignChallengeCallbackTrue)); ON_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, _)) .WillByDefault(Invoke(GetCertificateCallbackTrue)); @@ -231,7 +238,7 @@ TEST_F(EPKPChallengeMachineKeyTest, DevicePolicyDisabled) { } TEST_F(EPKPChallengeMachineKeyTest, DoesKeyExistDbusFailed) { - EXPECT_CALL(mock_cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _)) + EXPECT_CALL(mock_cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _, _)) .WillRepeatedly(Invoke(DoesKeyExistCallbackFailed)); EXPECT_EQ(base::StringPrintf( @@ -250,7 +257,7 @@ TEST_F(EPKPChallengeMachineKeyTest, GetCertificateFailed) { TEST_F(EPKPChallengeMachineKeyTest, SignChallengeFailed) { EXPECT_CALL(mock_async_method_caller_, - TpmAttestationSignEnterpriseChallenge(_, _, _, _, _, _, _)) + TpmAttestationSignEnterpriseChallenge(_, _, _, _, _, _, _, _)) .WillRepeatedly(Invoke(SignChallengeCallbackFalse)); EXPECT_EQ(EPKPChallengeKeyBase::kSignChallengeFailedError, @@ -258,7 +265,7 @@ TEST_F(EPKPChallengeMachineKeyTest, SignChallengeFailed) { } TEST_F(EPKPChallengeMachineKeyTest, KeyExists) { - EXPECT_CALL(mock_cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _)) + EXPECT_CALL(mock_cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _, _)) .WillRepeatedly(Invoke(DoesKeyExistCallbackTrue)); // GetCertificate must not be called if the key exists. EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, _)) @@ -277,7 +284,7 @@ TEST_F(EPKPChallengeMachineKeyTest, Success) { // SignEnterpriseChallenge must be called exactly once. EXPECT_CALL(mock_async_method_caller_, TpmAttestationSignEnterpriseChallenge( - chromeos::attestation::KEY_DEVICE, "attest-ent-machine", + chromeos::attestation::KEY_DEVICE, "", "attest-ent-machine", "google.com", "device_id", _, "challenge", _)) .Times(1); @@ -351,7 +358,7 @@ TEST_F(EPKPChallengeUserKeyTest, DevicePolicyDisabled) { } TEST_F(EPKPChallengeUserKeyTest, DoesKeyExistDbusFailed) { - EXPECT_CALL(mock_cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _)) + EXPECT_CALL(mock_cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _, _)) .WillRepeatedly(Invoke(DoesKeyExistCallbackFailed)); EXPECT_EQ(base::StringPrintf( @@ -370,7 +377,7 @@ TEST_F(EPKPChallengeUserKeyTest, GetCertificateFailed) { TEST_F(EPKPChallengeUserKeyTest, SignChallengeFailed) { EXPECT_CALL(mock_async_method_caller_, - TpmAttestationSignEnterpriseChallenge(_, _, _, _, _, _, _)) + TpmAttestationSignEnterpriseChallenge(_, _, _, _, _, _, _, _)) .WillRepeatedly(Invoke(SignChallengeCallbackFalse)); EXPECT_EQ(EPKPChallengeKeyBase::kSignChallengeFailedError, @@ -378,7 +385,7 @@ TEST_F(EPKPChallengeUserKeyTest, SignChallengeFailed) { } TEST_F(EPKPChallengeUserKeyTest, KeyRegistrationFailed) { - EXPECT_CALL(mock_async_method_caller_, TpmAttestationRegisterKey(_, _, _)) + EXPECT_CALL(mock_async_method_caller_, TpmAttestationRegisterKey(_, _, _, _)) .WillRepeatedly(Invoke(RegisterKeyCallbackFalse)); EXPECT_EQ(EPKPChallengeUserKey::kKeyRegistrationFailedError, @@ -386,7 +393,7 @@ TEST_F(EPKPChallengeUserKeyTest, KeyRegistrationFailed) { } TEST_F(EPKPChallengeUserKeyTest, KeyExists) { - EXPECT_CALL(mock_cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _)) + EXPECT_CALL(mock_cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _, _)) .WillRepeatedly(Invoke(DoesKeyExistCallbackTrue)); // GetCertificate must not be called if the key exists. EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, _)) @@ -396,7 +403,7 @@ TEST_F(EPKPChallengeUserKeyTest, KeyExists) { } TEST_F(EPKPChallengeUserKeyTest, KeyNotRegistered) { - EXPECT_CALL(mock_async_method_caller_, TpmAttestationRegisterKey(_, _, _)) + EXPECT_CALL(mock_async_method_caller_, TpmAttestationRegisterKey(_, _, _, _)) .Times(0); EXPECT_TRUE(utils::RunFunction( @@ -421,12 +428,14 @@ TEST_F(EPKPChallengeUserKeyTest, Success) { // SignEnterpriseChallenge must be called exactly once. EXPECT_CALL(mock_async_method_caller_, TpmAttestationSignEnterpriseChallenge( - chromeos::attestation::KEY_USER, "attest-ent-user", - "test@google.com", "device_id", _, "challenge", _)) + chromeos::attestation::KEY_USER, "test@google.com", + "attest-ent-user", "test@google.com", "device_id", _, + "challenge", _)) .Times(1); // RegisterKey must be called exactly once. EXPECT_CALL(mock_async_method_caller_, TpmAttestationRegisterKey(chromeos::attestation::KEY_USER, + "test@google.com", "attest-ent-user", _)) .Times(1); diff --git a/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc b/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc index 06017801b9..dc680e3017 100644 --- a/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc +++ b/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc @@ -120,7 +120,8 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, Basic) { ExtensionService* service = extensions::ExtensionSystem::Get( browser()->profile())->extension_service(); - service->toolbar_model()->ExecuteBrowserAction(extension, browser(), NULL); + service->toolbar_model()->ExecuteBrowserAction( + extension, browser(), NULL, true); ASSERT_TRUE(catcher.GetNextResult()) << catcher.message(); } @@ -590,7 +591,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, DISABLED_CloseBackgroundPage) { // Click the browser action. extensions::ExtensionSystem::Get(browser()->profile())->extension_service()-> - toolbar_model()->ExecuteBrowserAction(extension, browser(), NULL); + toolbar_model()->ExecuteBrowserAction(extension, browser(), NULL, true); // It can take a moment for the background page to actually get destroyed // so we wait for the notification before checking that it's really gone diff --git a/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc b/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc new file mode 100644 index 0000000000..0236ca69d7 --- /dev/null +++ b/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc @@ -0,0 +1,193 @@ +// Copyright 2013 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/extensions/browser_action_test_util.h" +#include "chrome/browser/extensions/extension_action.h" +#include "chrome/browser/extensions/extension_action_manager.h" +#include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_tab_util.h" +#include "chrome/browser/extensions/extension_test_message_listener.h" +#include "chrome/browser/ui/browser_finder.h" +#include "chrome/browser/ui/browser_list.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/common/extensions/permissions/permissions_data.h" +#include "chrome/test/base/ui_test_utils.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/web_contents.h" + +namespace extensions { +namespace { + +// chrome.browserAction API tests that interact with the UI in such a way that +// they cannot be run concurrently (i.e. openPopup API tests that require the +// window be focused/active). +class BrowserActionInteractiveTest : public ExtensionApiTest { + public: + BrowserActionInteractiveTest() {} + virtual ~BrowserActionInteractiveTest() {} + + protected: + // Function to control whether to run popup tests for the current platform. + // These tests require RunExtensionSubtest to work as expected and the browser + // window to able to be made active automatically. Returns false for platforms + // where these conditions are not met. + bool ShouldRunPopupTest() { + // TODO(justinlin): http://crbug.com/177163 +#if defined(OS_WIN) && !defined(NDEBUG) + return false; +#elif defined(OS_MACOSX) + // TODO(justinlin): Browser window do not become active on Mac even when + // Activate() is called on them. Enable when/if it's possible to fix. + return false; +#else + return true; +#endif + } +}; + +// Tests opening a popup using the chrome.browserAction.openPopup API. This test +// opens a popup in the starting window, closes the popup, creates a new window +// and opens a popup in the new window. Both popups should succeed in opening. +IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest, TestOpenPopup) { + if (!ShouldRunPopupTest()) + return; + + BrowserActionTestUtil browserActionBar = BrowserActionTestUtil(browser()); + // Setup extension message listener to wait for javascript to finish running. + ExtensionTestMessageListener listener("ready", true); + { + // Setup the notification observer to wait for the popup to finish loading. + content::WindowedNotificationObserver frame_observer( + content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME, + content::NotificationService::AllSources()); + // Show first popup in first window and expect it to have loaded. + ASSERT_TRUE(RunExtensionSubtest("browser_action/open_popup", + "open_popup_succeeds.html")) << message_; + frame_observer.Wait(); + EXPECT_TRUE(browserActionBar.HasPopup()); + browserActionBar.HidePopup(); + } + + EXPECT_TRUE(listener.WaitUntilSatisfied()); + Browser* new_browser = NULL; + { + content::WindowedNotificationObserver frame_observer( + content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME, + content::NotificationService::AllSources()); + // Open a new window. + new_browser = chrome::FindBrowserWithWebContents( + browser()->OpenURL(content::OpenURLParams( + GURL("about:"), content::Referrer(), NEW_WINDOW, + content::PAGE_TRANSITION_TYPED, false))); +#if defined(OS_WIN) + // Hide all the buttons to test that it opens even when browser action is + // in the overflow bucket. + // TODO(justinlin): Implement for other platforms. + browserActionBar.SetIconVisibilityCount(0); +#endif + frame_observer.Wait(); + } + + ResultCatcher catcher; + { + content::WindowedNotificationObserver frame_observer( + content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME, + content::NotificationService::AllSources()); + // Show second popup in new window. + listener.Reply(""); + frame_observer.Wait(); + EXPECT_TRUE(BrowserActionTestUtil(new_browser).HasPopup()); + } + ASSERT_TRUE(catcher.GetNextResult()) << message_; +} + +// Tests opening a popup in an incognito window. +IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest, TestOpenPopupIncognito) { + if (!ShouldRunPopupTest()) + return; + + content::WindowedNotificationObserver frame_observer( + content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME, + content::NotificationService::AllSources()); + ASSERT_TRUE(RunExtensionSubtest("browser_action/open_popup", + "open_popup_succeeds.html", + kFlagEnableIncognito | kFlagUseIncognito)) + << message_; + frame_observer.Wait(); + // Non-Aura Linux uses a singleton for the popup, so it looks like all windows + // have popups if there is any popup open. +#if !(defined(OS_LINUX) && !defined(USE_AURA)) + // Starting window does not have a popup. + EXPECT_FALSE(BrowserActionTestUtil(browser()).HasPopup()); +#endif + // Incognito window should have a popup. + EXPECT_TRUE(BrowserActionTestUtil(BrowserList::GetInstance( + chrome::GetActiveDesktop())->GetLastActive()).HasPopup()); +} + +#if defined(OS_LINUX) +#define MAYBE_TestOpenPopupDoesNotCloseOtherPopups DISABLED_TestOpenPopupDoesNotCloseOtherPopups +#else +#define MAYBE_TestOpenPopupDoesNotCloseOtherPopups TestOpenPopupDoesNotCloseOtherPopups +#endif +// Tests if there is already a popup open (by a user click or otherwise), that +// the openPopup API does not override it. +IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest, + MAYBE_TestOpenPopupDoesNotCloseOtherPopups) { + if (!ShouldRunPopupTest()) + return; + + // Load a first extension that can open a popup. + ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII( + "browser_action/popup"))); + const Extension* extension = GetSingleLoadedExtension(); + ASSERT_TRUE(extension) << message_; + + ExtensionTestMessageListener listener("ready", true); + // Load the test extension which will do nothing except notifyPass() to + // return control here. + ASSERT_TRUE(RunExtensionSubtest("browser_action/open_popup", + "open_popup_fails.html")) << message_; + EXPECT_TRUE(listener.WaitUntilSatisfied()); + + content::WindowedNotificationObserver frame_observer( + content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME, + content::NotificationService::AllSources()); + // Open popup in the first extension. + BrowserActionTestUtil(browser()).Press(0); + frame_observer.Wait(); + EXPECT_TRUE(BrowserActionTestUtil(browser()).HasPopup()); + + ResultCatcher catcher; + // Return control to javascript to validate that opening a popup fails now. + listener.Reply(""); + ASSERT_TRUE(catcher.GetNextResult()) << message_; +} + +// Test that openPopup does not grant tab permissions like for browser action +// clicks if the activeTab permission is set. +IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest, + TestOpenPopupDoesNotGrantTabPermissions) { + if (!ShouldRunPopupTest()) + return; + + content::WindowedNotificationObserver frame_observer( + content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME, + content::NotificationService::AllSources()); + ASSERT_TRUE(RunExtensionSubtest("browser_action/open_popup", + "open_popup_succeeds.html")) << message_; + frame_observer.Wait(); + + ExtensionService* service = extensions::ExtensionSystem::Get( + browser()->profile())->extension_service(); + ASSERT_FALSE(PermissionsData::HasAPIPermissionForTab( + service->GetExtensionById(last_loaded_extension_id(), false), + SessionID::IdForTab(browser()->tab_strip_model()->GetActiveWebContents()), + APIPermission::kTab)); +} + +} // namespace +} // namespace extensions diff --git a/chrome/browser/extensions/api/extension_action/extension_action_api.cc b/chrome/browser/extensions/api/extension_action/extension_action_api.cc index 98e73ee428..1024b83571 100644 --- a/chrome/browser/extensions/api/extension_action/extension_action_api.cc +++ b/chrome/browser/extensions/api/extension_action/extension_action_api.cc @@ -4,8 +4,6 @@ #include "chrome/browser/extensions/api/extension_action/extension_action_api.h" -#include <string> - #include "base/base64.h" #include "base/lazy_instance.h" #include "base/strings/string_number_conversions.h" @@ -17,6 +15,7 @@ #include "chrome/browser/extensions/extension_action.h" #include "chrome/browser/extensions/extension_action_manager.h" #include "chrome/browser/extensions/extension_function_registry.h" +#include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/extensions/extension_tab_util.h" @@ -59,6 +58,10 @@ const char kNoTabError[] = "No tab with id: *."; const char kNoPageActionError[] = "This extension has no page action specified."; const char kUrlNotActiveError[] = "This url is no longer active: *."; +const char kOpenPopupError[] = + "Failed to show popup either because there is an existing popup or another " + "error occurred."; +const char kInternalError[] = "Internal error."; struct IconRepresentationInfo { // Size as a string that will be used to retrieve representation value from @@ -209,6 +212,7 @@ ExtensionActionAPI::ExtensionActionAPI(Profile* profile) { registry->RegisterFunction<BrowserActionGetPopupFunction>(); registry->RegisterFunction<BrowserActionEnableFunction>(); registry->RegisterFunction<BrowserActionDisableFunction>(); + registry->RegisterFunction<BrowserActionOpenPopupFunction>(); // Page Actions registry->RegisterFunction<EnablePageActionsFunction>(); @@ -805,6 +809,65 @@ bool ExtensionActionGetBadgeBackgroundColorFunction::RunExtensionAction() { return true; } +BrowserActionOpenPopupFunction::BrowserActionOpenPopupFunction() + : response_sent_(false) { +} + +bool BrowserActionOpenPopupFunction::RunImpl() { + ExtensionToolbarModel* model = extensions::ExtensionSystem::Get(profile_)-> + extension_service()->toolbar_model(); + if (!model) { + error_ = kInternalError; + return false; + } + + if (!model->ShowBrowserActionPopup(extension_)) { + error_ = kOpenPopupError; + return false; + } + + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING, + content::Source<Profile>(profile_)); + + // Set a timeout for waiting for the notification that the popup is loaded. + // Waiting is required so that the popup view can be retrieved by the custom + // bindings for the response callback. It's also needed to keep this function + // instance around until a notification is observed. + base::MessageLoopForUI::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&BrowserActionOpenPopupFunction::OpenPopupTimedOut, this), + base::TimeDelta::FromSeconds(10)); + return true; +} + +void BrowserActionOpenPopupFunction::OpenPopupTimedOut() { + if (response_sent_) + return; + + DVLOG(1) << "chrome.browserAction.openPopup did not show a popup."; + error_ = kOpenPopupError; + SendResponse(false); + response_sent_ = true; +} + +void BrowserActionOpenPopupFunction::Observe( + int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + DCHECK_EQ(chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING, type); + if (response_sent_) + return; + + ExtensionHost* host = content::Details<ExtensionHost>(details).ptr(); + if (host->extension_host_type() != VIEW_TYPE_EXTENSION_POPUP || + host->extension()->id() != extension_->id()) + return; + + SendResponse(true); + response_sent_ = true; + registrar_.RemoveAll(); +} + // // ScriptBadgeGetAttentionFunction // diff --git a/chrome/browser/extensions/api/extension_action/extension_action_api.h b/chrome/browser/extensions/api/extension_action/extension_action_api.h index 692576c088..8471ae3332 100644 --- a/chrome/browser/extensions/api/extension_action/extension_action_api.h +++ b/chrome/browser/extensions/api/extension_action/extension_action_api.h @@ -5,6 +5,8 @@ #ifndef CHROME_BROWSER_EXTENSIONS_API_EXTENSION_ACTION_EXTENSION_ACTION_API_H_ #define CHROME_BROWSER_EXTENSIONS_API_EXTENSION_ACTION_EXTENSION_ACTION_API_H_ +#include <string> + #include "base/memory/weak_ptr.h" #include "chrome/browser/extensions/api/profile_keyed_api_factory.h" #include "chrome/browser/extensions/extension_action.h" @@ -341,6 +343,30 @@ class BrowserActionDisableFunction : public ExtensionActionHideFunction { virtual ~BrowserActionDisableFunction() {} }; +class BrowserActionOpenPopupFunction : public UIThreadExtensionFunction, + public content::NotificationObserver { + public: + DECLARE_EXTENSION_FUNCTION("browserAction.openPopup", + BROWSERACTION_OPEN_POPUP) + BrowserActionOpenPopupFunction(); + + private: + virtual ~BrowserActionOpenPopupFunction() {} + + // ExtensionFunction: + virtual bool RunImpl() OVERRIDE; + + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE; + void OpenPopupTimedOut(); + + content::NotificationRegistrar registrar_; + bool response_sent_; + + DISALLOW_COPY_AND_ASSIGN(BrowserActionOpenPopupFunction); +}; + // // scriptBadge.* aliases for supported scriptBadge APIs. // diff --git a/chrome/browser/extensions/api/extension_action/page_as_browser_action_apitest.cc b/chrome/browser/extensions/api/extension_action/page_as_browser_action_apitest.cc index 196ecc1d7c..34eb519d7e 100644 --- a/chrome/browser/extensions/api/extension_action/page_as_browser_action_apitest.cc +++ b/chrome/browser/extensions/api/extension_action/page_as_browser_action_apitest.cc @@ -91,7 +91,8 @@ IN_PROC_BROWSER_TEST_F(PageAsBrowserActionApiTest, Basic) { ResultCatcher catcher; ExtensionService* service = extensions::ExtensionSystem::Get( browser()->profile())->extension_service(); - service->toolbar_model()->ExecuteBrowserAction(extension, browser(), NULL); + service->toolbar_model()->ExecuteBrowserAction( + extension, browser(), NULL, true); EXPECT_TRUE(catcher.GetNextResult()); } @@ -135,7 +136,8 @@ IN_PROC_BROWSER_TEST_F(PageAsBrowserActionApiTest, AddPopup) { ResultCatcher catcher; ExtensionService* service = extensions::ExtensionSystem::Get( browser()->profile())->extension_service(); - service->toolbar_model()->ExecuteBrowserAction(extension, browser(), NULL); + service->toolbar_model()->ExecuteBrowserAction( + extension, browser(), NULL, true); ASSERT_TRUE(catcher.GetNextResult()); } @@ -202,5 +204,5 @@ IN_PROC_BROWSER_TEST_F(PageAsBrowserActionApiTest, Getters) { ASSERT_TRUE(catcher.GetNextResult()); } -} +} // namespace } // namespace extensions diff --git a/chrome/browser/extensions/api/feedback_private/feedback_browsertest.cc b/chrome/browser/extensions/api/feedback_private/feedback_browsertest.cc new file mode 100644 index 0000000000..82c2137774 --- /dev/null +++ b/chrome/browser/extensions/api/feedback_private/feedback_browsertest.cc @@ -0,0 +1,92 @@ +// Copyright 2013 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 "apps/shell_window.h" +#include "apps/shell_window_registry.h" +#include "base/bind.h" +#include "chrome/browser/apps/app_browsertest_util.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/extensions/api/feedback_private/feedback_private_api.h" +#include "chrome/browser/extensions/component_loader.h" +#include "chrome/browser/extensions/event_router.h" +#include "chrome/browser/extensions/extension_browsertest.h" +#include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/common/extensions/api/feedback_private.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "content/public/common/content_switches.h" + +using apps::ShellWindow; +using apps::ShellWindowRegistry; +using extensions::Extension; + +namespace { + +void StopMessageLoopCallback() { + base::MessageLoopForUI::current()->Quit(); +} + +} // namespace + +namespace extensions { + +class FeedbackTest : public ExtensionBrowserTest { + public: + virtual void SetUp() OVERRIDE { + extensions::ComponentLoader::EnableBackgroundExtensionsForTesting(); + InProcessBrowserTest::SetUp(); + } + + virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { + command_line->AppendSwitch(::switches::kEnableUserMediaScreenCapturing); + InProcessBrowserTest::SetUpCommandLine(command_line); + } + + protected: + bool IsFeedbackAppAvailable() { + return extensions::ExtensionSystem::Get( + browser()->profile())->event_router()->ExtensionHasEventListener( + kFeedbackExtensionId, + extensions::api::feedback_private::OnFeedbackRequested::kEventName); + } + + void StartFeedbackUI() { + base::Closure callback = base::Bind(&StopMessageLoopCallback); + extensions::FeedbackPrivateGetStringsFunction::set_test_callback(&callback); + InvokeFeedbackUI(); + content::RunMessageLoop(); + extensions::FeedbackPrivateGetStringsFunction::set_test_callback(NULL); + } + + void VerifyFeedbackAppLaunch() { + ShellWindow* window = + PlatformAppBrowserTest::GetFirstShellWindowForBrowser(browser()); + ASSERT_TRUE(window); + const Extension* feedback_app = window->extension(); + ASSERT_TRUE(feedback_app); + EXPECT_EQ(feedback_app->id(), std::string(kFeedbackExtensionId)); + } + + private: + void InvokeFeedbackUI() { + extensions::FeedbackPrivateAPI* api = + extensions::FeedbackPrivateAPI::GetFactoryInstance()->GetForProfile( + browser()->profile()); + api->RequestFeedback("Test description", + "Test tag", + GURL("http://www.test.com")); + } +}; + +IN_PROC_BROWSER_TEST_F(FeedbackTest, ShowFeedback) { + WaitForExtensionViewsToLoad(); + + ASSERT_TRUE(IsFeedbackAppAvailable()); + StartFeedbackUI(); + VerifyFeedbackAppLaunch(); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/feedback_private/feedback_private_api.cc b/chrome/browser/extensions/api/feedback_private/feedback_private_api.cc index b4c67e73a1..8e7ecbab3a 100644 --- a/chrome/browser/extensions/api/feedback_private/feedback_private_api.cc +++ b/chrome/browser/extensions/api/feedback_private/feedback_private_api.cc @@ -18,12 +18,6 @@ #include "ui/base/webui/web_ui_util.h" #include "url/url_util.h" -namespace { - -char kFeedbackExtensionId[] = "gfdkimpbcpahaombhbimeihdjnejgicl"; - -} - namespace extensions { namespace feedback_private = api::feedback_private; @@ -31,6 +25,8 @@ namespace feedback_private = api::feedback_private; using feedback_private::SystemInformation; using feedback_private::FeedbackInfo; +char kFeedbackExtensionId[] = "gfdkimpbcpahaombhbimeihdjnejgicl"; + static base::LazyInstance<ProfileKeyedAPIFactory<FeedbackPrivateAPI> > g_factory = LAZY_INSTANCE_INITIALIZER; @@ -84,6 +80,9 @@ void FeedbackPrivateAPI::RequestFeedback( } } +// static +base::Closure* FeedbackPrivateGetStringsFunction::test_callback_ = NULL; + bool FeedbackPrivateGetStringsFunction::RunImpl() { DictionaryValue* dict = new DictionaryValue(); SetResult(dict); @@ -108,6 +107,10 @@ bool FeedbackPrivateGetStringsFunction::RunImpl() { #undef SET_STRING webui::SetFontAndTextDirection(dict); + + if (test_callback_ && !test_callback_->is_null()) + test_callback_->Run(); + return true; } diff --git a/chrome/browser/extensions/api/feedback_private/feedback_private_api.h b/chrome/browser/extensions/api/feedback_private/feedback_private_api.h index bfdc000bcc..a4ac0459b8 100644 --- a/chrome/browser/extensions/api/feedback_private/feedback_private_api.h +++ b/chrome/browser/extensions/api/feedback_private/feedback_private_api.h @@ -12,6 +12,8 @@ namespace extensions { +extern char kFeedbackExtensionId[]; + class FeedbackService; using extensions::api::feedback_private::SystemInformation; @@ -49,11 +51,19 @@ class FeedbackPrivateGetStringsFunction : public SyncExtensionFunction { DECLARE_EXTENSION_FUNCTION("feedbackPrivate.getStrings", FEEDBACKPRIVATE_GETSTRINGS) + // Invoke this callback when this function is called - used for testing. + static void set_test_callback(base::Closure* const callback) { + test_callback_ = callback; + } + protected: virtual ~FeedbackPrivateGetStringsFunction() {} // SyncExtensionFunction overrides. virtual bool RunImpl() OVERRIDE; + + private: + static base::Closure* test_callback_; }; class FeedbackPrivateGetUserEmailFunction : public SyncExtensionFunction { diff --git a/chrome/browser/extensions/api/file_handlers/app_file_handler_util.cc b/chrome/browser/extensions/api/file_handlers/app_file_handler_util.cc index 81648f0adb..ca64e69053 100644 --- a/chrome/browser/extensions/api/file_handlers/app_file_handler_util.cc +++ b/chrome/browser/extensions/api/file_handlers/app_file_handler_util.cc @@ -313,6 +313,7 @@ GrantedFileEntry CreateFileEntry( policy->GrantReadFileSystem(renderer_id, result.filesystem_id); if (HasFileSystemWritePermission(extension)) { policy->GrantWriteFileSystem(renderer_id, result.filesystem_id); + policy->GrantDeleteFromFileSystem(renderer_id, result.filesystem_id); if (is_directory) policy->GrantCreateFileForFileSystem(renderer_id, result.filesystem_id); } diff --git a/chrome/browser/extensions/api/identity/account_tracker.cc b/chrome/browser/extensions/api/identity/account_tracker.cc new file mode 100644 index 0000000000..03db40a97d --- /dev/null +++ b/chrome/browser/extensions/api/identity/account_tracker.cc @@ -0,0 +1,244 @@ +// Copyright 2013 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/extensions/api/identity/account_tracker.h" + +#include "base/logging.h" +#include "base/stl_util.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/signin/profile_oauth2_token_service.h" +#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" +#include "chrome/browser/signin/signin_manager_base.h" +#include "content/public/browser/notification_details.h" + +namespace extensions { + +AccountTracker::AccountTracker(Profile* profile) : profile_(profile) { + registrar_.Add(this, + chrome::NOTIFICATION_GOOGLE_SIGNED_OUT, + content::Source<Profile>(profile_)); + + ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->AddObserver(this); + SigninGlobalError::GetForProfile(profile_)->AddProvider(this); +} + +AccountTracker::~AccountTracker() {} + +void AccountTracker::ReportAuthError(const std::string& account_id, + const GoogleServiceAuthError& error) { + account_errors_.insert(make_pair(account_id, error)); + SigninGlobalError::GetForProfile(profile_)->AuthStatusChanged(); + UpdateSignInState(account_id, false); +} + +void AccountTracker::Shutdown() { + STLDeleteValues(&user_info_requests_); + SigninGlobalError::GetForProfile(profile_)->RemoveProvider(this); + ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)-> + RemoveObserver(this); +} + +void AccountTracker::AddObserver(Observer* observer) { + observer_list_.AddObserver(observer); +} + +void AccountTracker::RemoveObserver(Observer* observer) { + observer_list_.RemoveObserver(observer); +} + +void AccountTracker::OnRefreshTokenAvailable(const std::string& account_id) { + DVLOG(1) << "AVAILABLE " << account_id; + account_errors_.erase(account_id); + UpdateSignInState(account_id, true); +} + +void AccountTracker::OnRefreshTokenRevoked(const std::string& account_id) { + DVLOG(1) << "REVOKED " << account_id; + UpdateSignInState(account_id, false); +} + +void AccountTracker::Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + switch (type) { + case chrome::NOTIFICATION_GOOGLE_SIGNED_OUT: + StopTrackingAccount(content::Details<GoogleServiceSignoutDetails>( + details)->username); + break; + default: + NOTREACHED(); + } +} + +void AccountTracker::NotifyAccountAdded(const AccountState& account) { + DCHECK(!account.ids.gaia.empty()); + FOR_EACH_OBSERVER( + Observer, observer_list_, OnAccountAdded(account.ids)); +} + +void AccountTracker::NotifyAccountRemoved(const AccountState& account) { + DCHECK(!account.ids.gaia.empty()); + FOR_EACH_OBSERVER( + Observer, observer_list_, OnAccountRemoved(account.ids)); +} + +void AccountTracker::NotifySignInChanged(const AccountState& account) { + DCHECK(!account.ids.gaia.empty()); + FOR_EACH_OBSERVER(Observer, + observer_list_, + OnAccountSignInChanged(account.ids, account.is_signed_in)); +} + +void AccountTracker::UpdateSignInState(const std::string& account_key, + bool is_signed_in) { + StartTrackingAccount(account_key); + AccountState& account = accounts_[account_key]; + bool needs_gaia_id = account.ids.gaia.empty(); + bool was_signed_in = account.is_signed_in; + account.is_signed_in = is_signed_in; + + if (needs_gaia_id && is_signed_in) + StartFetchingUserInfo(account_key); + + if (!needs_gaia_id && (was_signed_in != is_signed_in)) + NotifySignInChanged(account); +} + +void AccountTracker::StartTrackingAccount(const std::string& account_key) { + if (!ContainsKey(accounts_, account_key)) { + DVLOG(1) << "StartTracking " << account_key; + AccountState account_state; + account_state.ids.account_key = account_key; + account_state.ids.email = account_key; + account_state.is_signed_in = false; + accounts_.insert(make_pair(account_key, account_state)); + } +} + +void AccountTracker::StopTrackingAccount(const std::string& account_key) { + if (ContainsKey(accounts_, account_key)) { + AccountState& account = accounts_[account_key]; + if (!account.ids.gaia.empty()) { + UpdateSignInState(account_key, false); + NotifyAccountRemoved(account); + } + accounts_.erase(account_key); + } + + account_errors_.erase(account_key); + + if (ContainsKey(user_info_requests_, account_key)) + DeleteFetcher(user_info_requests_[account_key]); +} + +void AccountTracker::StartFetchingUserInfo(const std::string& account_key) { + if (ContainsKey(user_info_requests_, account_key)) + DeleteFetcher(user_info_requests_[account_key]); + + DVLOG(1) << "StartFetching " << account_key; + AccountIdFetcher* fetcher = + new AccountIdFetcher(profile_, this, account_key); + user_info_requests_[account_key] = fetcher; + fetcher->Start(); +} + +void AccountTracker::OnUserInfoFetchSuccess(AccountIdFetcher* fetcher, + const std::string& gaia_id) { + const std::string& account_key = fetcher->account_key(); + DCHECK(ContainsKey(accounts_, account_key)); + AccountState& account = accounts_[account_key]; + + account.ids.gaia = gaia_id; + NotifyAccountAdded(account); + + if (account.is_signed_in) + NotifySignInChanged(account); + + DeleteFetcher(fetcher); +} + +void AccountTracker::OnUserInfoFetchFailure(AccountIdFetcher* fetcher) { + LOG(WARNING) << "Failed to get UserInfo for " << fetcher->account_key(); + std::string key = fetcher->account_key(); + DeleteFetcher(fetcher); + StopTrackingAccount(key); +} + +std::string AccountTracker::GetAccountId() const { + if (account_errors_.size() == 0) + return std::string(); + else + return account_errors_.begin()->first; +} + +GoogleServiceAuthError AccountTracker::GetAuthStatus() const { + if (account_errors_.size() == 0) + return GoogleServiceAuthError::AuthErrorNone(); + else + return account_errors_.begin()->second; +} + +void AccountTracker::DeleteFetcher(AccountIdFetcher* fetcher) { + const std::string& account_key = fetcher->account_key(); + DCHECK(ContainsKey(user_info_requests_, account_key)); + DCHECK_EQ(fetcher, user_info_requests_[account_key]); + user_info_requests_.erase(account_key); + delete fetcher; +} + +AccountIdFetcher::AccountIdFetcher(Profile* profile, + AccountTracker* tracker, + const std::string& account_key) + : profile_(profile), + tracker_(tracker), + account_key_(account_key) {} + +AccountIdFetcher::~AccountIdFetcher() {} + +void AccountIdFetcher::Start() { + ProfileOAuth2TokenService* service = + ProfileOAuth2TokenServiceFactory::GetForProfile(profile_); + login_token_request_ = service->StartRequest( + account_key_, OAuth2TokenService::ScopeSet(), this); +} + +void AccountIdFetcher::OnGetTokenSuccess( + const OAuth2TokenService::Request* request, + const std::string& access_token, + const base::Time& expiration_time) { + DCHECK_EQ(request, login_token_request_.get()); + + gaia_oauth_client_.reset(new gaia::GaiaOAuthClient( + g_browser_process->system_request_context())); + + const int kMaxGetUserIdRetries = 3; + gaia_oauth_client_->GetUserId(access_token, kMaxGetUserIdRetries, this); +} + +void AccountIdFetcher::OnGetTokenFailure( + const OAuth2TokenService::Request* request, + const GoogleServiceAuthError& error) { + LOG(ERROR) << "OnGetTokenFailure: " << error.error_message(); + DCHECK_EQ(request, login_token_request_.get()); + tracker_->OnUserInfoFetchFailure(this); +} + +void AccountIdFetcher::OnGetUserIdResponse(const std::string& gaia_id) { + tracker_->OnUserInfoFetchSuccess(this, gaia_id); +} + +void AccountIdFetcher::OnOAuthError() { + LOG(ERROR) << "OnOAuthError"; + tracker_->OnUserInfoFetchFailure(this); +} + +void AccountIdFetcher::OnNetworkError(int response_code) { + LOG(ERROR) << "OnNetworkError " << response_code; + tracker_->OnUserInfoFetchFailure(this); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/identity/account_tracker.h b/chrome/browser/extensions/api/identity/account_tracker.h new file mode 100644 index 0000000000..de9b6d8851 --- /dev/null +++ b/chrome/browser/extensions/api/identity/account_tracker.h @@ -0,0 +1,140 @@ +// Copyright 2013 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_EXTENSIONS_API_IDENTITY_ACCOUNT_TRACKER_H_ +#define CHROME_BROWSER_EXTENSIONS_API_IDENTITY_ACCOUNT_TRACKER_H_ + +#include <map> +#include <string> + +#include "base/observer_list.h" +#include "chrome/browser/signin/signin_global_error.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" +#include "content/public/browser/notification_source.h" +#include "google_apis/gaia/gaia_oauth_client.h" +#include "google_apis/gaia/oauth2_token_service.h" + +class GoogleServiceAuthError; +class Profile; + +namespace extensions { + +struct AccountIds { + std::string account_key; // The account ID used by OAuth2TokenService. + std::string gaia; + std::string email; +}; + +class AccountIdFetcher; + +// The AccountTracker keeps track of what accounts exist on the +// profile and the state of their credentials. The tracker fetches the +// gaia ID of each account it knows about. +// +// The AccountTracker maintains these invariants: +// 1. Events are only fired after the gaia ID has been fetched. +// 2. Add/Remove and SignIn/SignOut pairs are always generated in order. +// 3. SignIn follows Add, and there will be a SignOut between SignIn & Remove. +class AccountTracker : public OAuth2TokenService::Observer, + public content::NotificationObserver, + public SigninGlobalError::AuthStatusProvider { + public: + explicit AccountTracker(Profile* profile); + virtual ~AccountTracker(); + + class Observer { + public: + virtual void OnAccountAdded(const AccountIds& ids) = 0; + virtual void OnAccountRemoved(const AccountIds& ids) = 0; + virtual void OnAccountSignInChanged(const AccountIds& ids, + bool is_signed_in) = 0; + }; + + void Shutdown(); + + void ReportAuthError(const std::string& account_key, + const GoogleServiceAuthError& error); + + void AddObserver(Observer* observer); + void RemoveObserver(Observer* observer); + + // OAuth2TokenService::Observer implementation. + virtual void OnRefreshTokenAvailable(const std::string& account_key) OVERRIDE; + virtual void OnRefreshTokenRevoked(const std::string& account_key) OVERRIDE; + + // content::NotificationObserver implementation. + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE; + + void OnUserInfoFetchSuccess(AccountIdFetcher* fetcher, + const std::string& gaia_id); + void OnUserInfoFetchFailure(AccountIdFetcher* fetcher); + + // AuthStatusProvider implementation. + virtual std::string GetAccountId() const OVERRIDE; + virtual GoogleServiceAuthError GetAuthStatus() const OVERRIDE; + + private: + struct AccountState { + AccountIds ids; + bool is_signed_in; + }; + + void NotifyAccountAdded(const AccountState& account); + void NotifyAccountRemoved(const AccountState& account); + void NotifySignInChanged(const AccountState& account); + + void UpdateSignInState(const std::string& account_key, bool is_signed_in); + + void StartTrackingAccount(const std::string& account_key); + void StopTrackingAccount(const std::string& account_key); + void StartFetchingUserInfo(const std::string& account_key); + void DeleteFetcher(AccountIdFetcher* fetcher); + + Profile* profile_; + std::map<std::string, AccountIdFetcher*> user_info_requests_; + std::map<std::string, AccountState> accounts_; + std::map<std::string, GoogleServiceAuthError> account_errors_; + ObserverList<Observer> observer_list_; + content::NotificationRegistrar registrar_; +}; + +class AccountIdFetcher : public OAuth2TokenService::Consumer, + public gaia::GaiaOAuthClient::Delegate { + public: + AccountIdFetcher(Profile* profile, + AccountTracker* tracker, + const std::string& account_key); + virtual ~AccountIdFetcher(); + + const std::string& account_key() { return account_key_; } + + void Start(); + + // OAuth2TokenService::Consumer implementation. + virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request, + const std::string& access_token, + const base::Time& expiration_time) OVERRIDE; + virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request, + const GoogleServiceAuthError& error) OVERRIDE; + + // gaia::GaiaOAuthClient::Delegate implementation. + virtual void OnGetUserIdResponse(const std::string& gaia_id) OVERRIDE; + virtual void OnOAuthError() OVERRIDE; + virtual void OnNetworkError(int response_code) OVERRIDE; + + private: + Profile* profile_; + AccountTracker* tracker_; + const std::string account_key_; + + scoped_ptr<OAuth2TokenService::Request> login_token_request_; + scoped_ptr<gaia::GaiaOAuthClient> gaia_oauth_client_; +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_IDENTITY_ACCOUNT_TRACKER_H_ diff --git a/chrome/browser/extensions/api/identity/account_tracker_unittest.cc b/chrome/browser/extensions/api/identity/account_tracker_unittest.cc new file mode 100644 index 0000000000..2ac55aded6 --- /dev/null +++ b/chrome/browser/extensions/api/identity/account_tracker_unittest.cc @@ -0,0 +1,504 @@ +// Copyright 2013 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/extensions/api/identity/account_tracker.h" + +#include <vector> + +#include "base/strings/stringprintf.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/signin/fake_profile_oauth2_token_service.h" +#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" +#include "chrome/browser/signin/signin_manager_base.h" +#include "chrome/test/base/testing_profile.h" +#include "content/public/browser/notification_service.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "google_apis/gaia/gaia_oauth_client.h" +#include "net/http/http_status_code.h" +#include "net/url_request/test_url_fetcher_factory.h" +#include "net/url_request/url_fetcher_delegate.h" +#include "net/url_request/url_request_test_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const char kFakeGaiaId[] = "8675309"; + +enum TrackingEventType { + ADDED, + REMOVED, + SIGN_IN, + SIGN_OUT +}; + +class TrackingEvent { + public: + TrackingEvent(TrackingEventType type, + const std::string& account_key, + const std::string& gaia_id) + : type_(type), + account_key_(account_key), + gaia_id_(gaia_id) {} + + TrackingEvent(TrackingEventType type, + const std::string& account_key) + : type_(type), + account_key_(account_key), + gaia_id_(kFakeGaiaId) {} + + bool operator==(const TrackingEvent& event) const { + return type_ == event.type_ && account_key_ == event.account_key_ && + gaia_id_ == event.gaia_id_; + } + + std::string ToString() const { + const char * typestr = "INVALID"; + switch (type_) { + case ADDED: + typestr = "ADD"; + break; + case REMOVED: + typestr = "REM"; + break; + case SIGN_IN: + typestr = " IN"; + break; + case SIGN_OUT: + typestr = "OUT"; + break; + } + return base::StringPrintf("{ type: %s, email: %s, gaia: %s }", + typestr, + account_key_.c_str(), + gaia_id_.c_str()); + } + + private: + TrackingEventType type_; + std::string account_key_; + std::string gaia_id_; +}; + +std::string Str(const std::vector<TrackingEvent>& events) { + std::string str = "["; + bool needs_comma = false; + for (std::vector<TrackingEvent>::const_iterator it = + events.begin(); it != events.end(); ++it) { + if (needs_comma) + str += ",\n "; + needs_comma = true; + str += it->ToString(); + } + str += "]"; + return str; +} + +} // namespace + +namespace extensions { + +class AccountTrackerObserver : public AccountTracker::Observer { + public: + AccountTrackerObserver() {} + virtual ~AccountTrackerObserver() {} + + testing::AssertionResult CheckEvents(); + testing::AssertionResult CheckEvents(const TrackingEvent& e1); + testing::AssertionResult CheckEvents(const TrackingEvent& e1, + const TrackingEvent& e2); + testing::AssertionResult CheckEvents(const TrackingEvent& e1, + const TrackingEvent& e2, + const TrackingEvent& e3); + testing::AssertionResult CheckEvents(const TrackingEvent& e1, + const TrackingEvent& e2, + const TrackingEvent& e3, + const TrackingEvent& e4); + + // AccountTracker::Observer implementation + virtual void OnAccountAdded(const AccountIds& ids) OVERRIDE; + virtual void OnAccountRemoved(const AccountIds& ids) OVERRIDE; + virtual void OnAccountSignInChanged(const AccountIds& ids, bool is_signed_in) + OVERRIDE; + + private: + testing::AssertionResult CheckEvents( + const std::vector<TrackingEvent>& events); + + std::vector<TrackingEvent> events_; +}; + +void AccountTrackerObserver::OnAccountAdded(const AccountIds& ids) { + events_.push_back(TrackingEvent(ADDED, ids.email, ids.gaia)); +} + +void AccountTrackerObserver::OnAccountRemoved(const AccountIds& ids) { + events_.push_back(TrackingEvent(REMOVED, ids.email, ids.gaia)); +} + +void AccountTrackerObserver::OnAccountSignInChanged(const AccountIds& ids, + bool is_signed_in) { + events_.push_back( + TrackingEvent(is_signed_in ? SIGN_IN : SIGN_OUT, ids.email, ids.gaia)); +} + +testing::AssertionResult AccountTrackerObserver::CheckEvents() { + std::vector<TrackingEvent> events; + return CheckEvents(events); +} + +testing::AssertionResult AccountTrackerObserver::CheckEvents( + const TrackingEvent& e1) { + std::vector<TrackingEvent> events; + events.push_back(e1); + return CheckEvents(events); +} + +testing::AssertionResult AccountTrackerObserver::CheckEvents( + const TrackingEvent& e1, + const TrackingEvent& e2) { + std::vector<TrackingEvent> events; + events.push_back(e1); + events.push_back(e2); + return CheckEvents(events); +} + +testing::AssertionResult AccountTrackerObserver::CheckEvents( + const TrackingEvent& e1, + const TrackingEvent& e2, + const TrackingEvent& e3) { + std::vector<TrackingEvent> events; + events.push_back(e1); + events.push_back(e2); + events.push_back(e3); + return CheckEvents(events); +} + +testing::AssertionResult AccountTrackerObserver::CheckEvents( + const TrackingEvent& e1, + const TrackingEvent& e2, + const TrackingEvent& e3, + const TrackingEvent& e4) { + std::vector<TrackingEvent> events; + events.push_back(e1); + events.push_back(e2); + events.push_back(e3); + events.push_back(e4); + return CheckEvents(events); +} + +testing::AssertionResult AccountTrackerObserver::CheckEvents( + const std::vector<TrackingEvent>& events) { + std::string maybe_newline = (events.size() + events_.size()) > 2 ? "\n" : ""; + testing::AssertionResult result( + (events_ == events) + ? testing::AssertionSuccess() + : (testing::AssertionFailure() + << "Expected " << maybe_newline << Str(events) << ", " + << maybe_newline << "Got " << maybe_newline << Str(events_))); + events_.clear(); + return result; +} + +class IdentityAccountTrackerTest : public testing::Test { + public: + IdentityAccountTrackerTest() {} + + virtual ~IdentityAccountTrackerTest() {} + + virtual void SetUp() OVERRIDE { + TestingProfile::Builder builder; + builder.AddTestingFactory(ProfileOAuth2TokenServiceFactory::GetInstance(), + FakeProfileOAuth2TokenService::Build); + + test_profile_ = builder.Build(); + + fake_oauth2_token_service_ = static_cast<FakeProfileOAuth2TokenService*>( + ProfileOAuth2TokenServiceFactory::GetForProfile(test_profile_.get())); + + account_tracker_.reset(new AccountTracker(test_profile_.get())); + account_tracker_->AddObserver(&observer_); + } + + virtual void TearDown() OVERRIDE { + account_tracker_->RemoveObserver(&observer_); + account_tracker_->Shutdown(); + } + + Profile* profile() { + return test_profile_.get(); + } + + AccountTrackerObserver* observer() { + return &observer_; + } + + AccountTracker* account_tracker() { + return account_tracker_.get(); + } + + // Helpers to pass fake events to the tracker. + + void NotifyRemoveAccount(const std::string& username) { + GoogleServiceSigninSuccessDetails details(username, std::string()); + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_GOOGLE_SIGNED_OUT, + content::Source<Profile>(profile()), + content::Details<const GoogleServiceSigninSuccessDetails>(&details)); + } + + void NotifyTokenAvailable(const std::string& username) { + fake_oauth2_token_service_->IssueRefreshTokenForUser(username, + "refresh_token"); + } + + void NotifyTokenRevoked(const std::string& username) { + fake_oauth2_token_service_->IssueRefreshTokenForUser(username, + std::string()); + } + + // Helpers to fake access token and user info fetching + void IssueAccessToken() { + fake_oauth2_token_service_->IssueTokenForAllPendingRequests( + "access_token", base::Time::Max()); + } + + std::string GetValidTokenInfoResponse(const std::string email) { + return std::string("{ \"id\": \"") + kFakeGaiaId + "\" }"; + } + + void ReturnOAuthUrlFetchResults(int fetcher_id, + net::HttpStatusCode response_code, + const std::string& response_string); + + void ReturnOAuthUrlFetchSuccess(const std::string& account_key); + void ReturnOAuthUrlFetchFailure(const std::string& account_key); + + private: + scoped_ptr<TestingProfile> test_profile_; + net::TestURLFetcherFactory test_fetcher_factory_; + FakeProfileOAuth2TokenService* fake_oauth2_token_service_; + content::TestBrowserThreadBundle thread_bundle_; + + scoped_ptr<AccountTracker> account_tracker_; + AccountTrackerObserver observer_; +}; + +void IdentityAccountTrackerTest::ReturnOAuthUrlFetchResults( + int fetcher_id, + net::HttpStatusCode response_code, + const std::string& response_string) { + + net::TestURLFetcher* fetcher = + test_fetcher_factory_.GetFetcherByID(fetcher_id); + ASSERT_TRUE(fetcher); + fetcher->set_response_code(response_code); + fetcher->SetResponseString(response_string); + fetcher->delegate()->OnURLFetchComplete(fetcher); +} + +void IdentityAccountTrackerTest::ReturnOAuthUrlFetchSuccess( + const std::string& account_key) { + IssueAccessToken(); + ReturnOAuthUrlFetchResults(gaia::GaiaOAuthClient::kUrlFetcherId, + net::HTTP_OK, + GetValidTokenInfoResponse(account_key)); +} + +void IdentityAccountTrackerTest::ReturnOAuthUrlFetchFailure( + const std::string& account_key) { + IssueAccessToken(); + ReturnOAuthUrlFetchResults( + gaia::GaiaOAuthClient::kUrlFetcherId, net::HTTP_BAD_REQUEST, ""); +} + +TEST_F(IdentityAccountTrackerTest, Available) { + NotifyTokenAvailable("user@example.com"); + EXPECT_TRUE(observer()->CheckEvents()); + + ReturnOAuthUrlFetchSuccess("user@example.com"); + EXPECT_TRUE(observer()->CheckEvents( + TrackingEvent(ADDED, "user@example.com", kFakeGaiaId), + TrackingEvent(SIGN_IN, "user@example.com", kFakeGaiaId))); +} + +TEST_F(IdentityAccountTrackerTest, Revoke) { + account_tracker()->OnRefreshTokenRevoked("user@example.com"); + EXPECT_TRUE(observer()->CheckEvents()); +} + +TEST_F(IdentityAccountTrackerTest, Remove) { + NotifyRemoveAccount("user@example.com"); + EXPECT_TRUE(observer()->CheckEvents()); +} + +TEST_F(IdentityAccountTrackerTest, AvailableRemoveFetchCancelAvailable) { + NotifyTokenAvailable("user@example.com"); + NotifyRemoveAccount("user@example.com"); + EXPECT_TRUE(observer()->CheckEvents()); + + NotifyTokenAvailable("user@example.com"); + ReturnOAuthUrlFetchSuccess("user@example.com"); + EXPECT_TRUE(observer()->CheckEvents( + TrackingEvent(ADDED, "user@example.com", kFakeGaiaId), + TrackingEvent(SIGN_IN, "user@example.com", kFakeGaiaId))); +} + +TEST_F(IdentityAccountTrackerTest, AvailableRemoveAvailable) { + NotifyTokenAvailable("user@example.com"); + ReturnOAuthUrlFetchSuccess("user@example.com"); + NotifyRemoveAccount("user@example.com"); + EXPECT_TRUE(observer()->CheckEvents( + TrackingEvent(ADDED, "user@example.com", kFakeGaiaId), + TrackingEvent(SIGN_IN, "user@example.com", kFakeGaiaId), + TrackingEvent(SIGN_OUT, "user@example.com", kFakeGaiaId), + TrackingEvent(REMOVED, "user@example.com", kFakeGaiaId))); + + NotifyTokenAvailable("user@example.com"); + ReturnOAuthUrlFetchSuccess("user@example.com"); + EXPECT_TRUE(observer()->CheckEvents( + TrackingEvent(ADDED, "user@example.com", kFakeGaiaId), + TrackingEvent(SIGN_IN, "user@example.com", kFakeGaiaId))); +} + +TEST_F(IdentityAccountTrackerTest, AvailableRevokeAvailable) { + NotifyTokenAvailable("user@example.com"); + ReturnOAuthUrlFetchSuccess("user@example.com"); + NotifyTokenRevoked("user@example.com"); + EXPECT_TRUE(observer()->CheckEvents( + TrackingEvent(ADDED, "user@example.com", kFakeGaiaId), + TrackingEvent(SIGN_IN, "user@example.com", kFakeGaiaId), + TrackingEvent(SIGN_OUT, "user@example.com", kFakeGaiaId))); + + NotifyTokenAvailable("user@example.com"); + EXPECT_TRUE(observer()->CheckEvents( + TrackingEvent(SIGN_IN, "user@example.com", kFakeGaiaId))); +} + +TEST_F(IdentityAccountTrackerTest, AvailableRevokeAvailableWithPendingFetch) { + NotifyTokenAvailable("user@example.com"); + NotifyTokenRevoked("user@example.com"); + EXPECT_TRUE(observer()->CheckEvents()); + + NotifyTokenAvailable("user@example.com"); + ReturnOAuthUrlFetchSuccess("user@example.com"); + EXPECT_TRUE(observer()->CheckEvents( + TrackingEvent(ADDED, "user@example.com", kFakeGaiaId), + TrackingEvent(SIGN_IN, "user@example.com", kFakeGaiaId))); +} + +TEST_F(IdentityAccountTrackerTest, AvailableRevokeRemove) { + NotifyTokenAvailable("user@example.com"); + ReturnOAuthUrlFetchSuccess("user@example.com"); + NotifyTokenRevoked("user@example.com"); + EXPECT_TRUE(observer()->CheckEvents( + TrackingEvent(ADDED, "user@example.com", kFakeGaiaId), + TrackingEvent(SIGN_IN, "user@example.com", kFakeGaiaId), + TrackingEvent(SIGN_OUT, "user@example.com", kFakeGaiaId))); + + NotifyRemoveAccount("user@example.com"); + EXPECT_TRUE(observer()->CheckEvents( + TrackingEvent(REMOVED, "user@example.com", kFakeGaiaId))); +} + +TEST_F(IdentityAccountTrackerTest, AvailableRevokeRevoke) { + NotifyTokenAvailable("user@example.com"); + ReturnOAuthUrlFetchSuccess("user@example.com"); + NotifyTokenRevoked("user@example.com"); + EXPECT_TRUE(observer()->CheckEvents( + TrackingEvent(ADDED, "user@example.com", kFakeGaiaId), + TrackingEvent(SIGN_IN, "user@example.com", kFakeGaiaId), + TrackingEvent(SIGN_OUT, "user@example.com", kFakeGaiaId))); + + NotifyTokenRevoked("user@example.com"); + EXPECT_TRUE(observer()->CheckEvents()); +} + +TEST_F(IdentityAccountTrackerTest, AvailableAvailable) { + NotifyTokenAvailable("user@example.com"); + ReturnOAuthUrlFetchSuccess("user@example.com"); + EXPECT_TRUE(observer()->CheckEvents( + TrackingEvent(ADDED, "user@example.com", kFakeGaiaId), + TrackingEvent(SIGN_IN, "user@example.com", kFakeGaiaId))); + + NotifyTokenAvailable("user@example.com"); + EXPECT_TRUE(observer()->CheckEvents()); +} + +TEST_F(IdentityAccountTrackerTest, TwoAccounts) { + NotifyTokenAvailable("alpha@example.com"); + ReturnOAuthUrlFetchSuccess("alpha@example.com"); + EXPECT_TRUE(observer()->CheckEvents( + TrackingEvent(ADDED, "alpha@example.com", kFakeGaiaId), + TrackingEvent(SIGN_IN, "alpha@example.com", kFakeGaiaId))); + + NotifyTokenAvailable("beta@example.com"); + ReturnOAuthUrlFetchSuccess("beta@example.com"); + EXPECT_TRUE(observer()->CheckEvents( + TrackingEvent(ADDED, "beta@example.com", kFakeGaiaId), + TrackingEvent(SIGN_IN, "beta@example.com", kFakeGaiaId))); + + NotifyRemoveAccount("alpha@example.com"); + EXPECT_TRUE(observer()->CheckEvents( + TrackingEvent(SIGN_OUT, "alpha@example.com", kFakeGaiaId), + TrackingEvent(REMOVED, "alpha@example.com", kFakeGaiaId))); + + NotifyRemoveAccount("beta@example.com"); + EXPECT_TRUE(observer()->CheckEvents( + TrackingEvent(SIGN_OUT, "beta@example.com", kFakeGaiaId), + TrackingEvent(REMOVED, "beta@example.com", kFakeGaiaId))); +} + +TEST_F(IdentityAccountTrackerTest, GlobalErrors) { + NotifyTokenAvailable("alpha@example.com"); + ReturnOAuthUrlFetchSuccess("alpha@example.com"); + EXPECT_TRUE(observer()->CheckEvents( + TrackingEvent(ADDED, "alpha@example.com", kFakeGaiaId), + TrackingEvent(SIGN_IN, "alpha@example.com", kFakeGaiaId))); + NotifyTokenAvailable("beta@example.com"); + ReturnOAuthUrlFetchSuccess("beta@example.com"); + EXPECT_TRUE(observer()->CheckEvents( + TrackingEvent(ADDED, "beta@example.com", kFakeGaiaId), + TrackingEvent(SIGN_IN, "beta@example.com", kFakeGaiaId))); + + EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(), + account_tracker()->GetAuthStatus()); + + account_tracker()->ReportAuthError( + "beta@example.com", + GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED)); + EXPECT_TRUE(observer()->CheckEvents( + TrackingEvent(SIGN_OUT, "beta@example.com", kFakeGaiaId))); + EXPECT_EQ(GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED), + account_tracker()->GetAuthStatus()); + + account_tracker()->ReportAuthError( + "alpha@example.com", + GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED)); + EXPECT_TRUE(observer()->CheckEvents( + TrackingEvent(SIGN_OUT, "alpha@example.com", kFakeGaiaId))); + EXPECT_EQ(GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED), + account_tracker()->GetAuthStatus()); + + NotifyRemoveAccount("alpha@example.com"); + EXPECT_EQ(GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED), + account_tracker()->GetAuthStatus()); + + NotifyTokenAvailable("beta@example.com"); + EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(), + account_tracker()->GetAuthStatus()); +} + +TEST_F(IdentityAccountTrackerTest, AvailableTokenFetchFailAvailable) { + NotifyTokenAvailable("alpha@example.com"); + ReturnOAuthUrlFetchFailure("alpha@example.com"); + EXPECT_TRUE(observer()->CheckEvents()); + + NotifyTokenAvailable("user@example.com"); + ReturnOAuthUrlFetchSuccess("user@example.com"); + EXPECT_TRUE(observer()->CheckEvents( + TrackingEvent(ADDED, "user@example.com", kFakeGaiaId), + TrackingEvent(SIGN_IN, "user@example.com", kFakeGaiaId))); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/identity/identity_api.cc b/chrome/browser/extensions/api/identity/identity_api.cc index 890f99443d..485711e8be 100644 --- a/chrome/browser/extensions/api/identity/identity_api.cc +++ b/chrome/browser/extensions/api/identity/identity_api.cc @@ -36,6 +36,7 @@ #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/settings/device_oauth2_token_service.h" #include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h" +#include "google_apis/gaia/gaia_constants.h" #endif namespace extensions { @@ -215,18 +216,11 @@ void IdentityGetAuthTokenFunction::StartMintToken( #if defined(OS_CHROMEOS) // Always force minting token for ChromeOS kiosk app. if (chromeos::UserManager::Get()->IsLoggedInAsKioskApp()) { + gaia_mint_token_mode_ = OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE; if (g_browser_process->browser_policy_connector()-> IsEnterpriseManaged()) { - OAuth2TokenService::ScopeSet scope_set(oauth2_info.scopes.begin(), - oauth2_info.scopes.end()); - chromeos::DeviceOAuth2TokenService* token_service = - chromeos::DeviceOAuth2TokenServiceFactory::Get(); - device_token_request_ = - token_service->StartRequest(token_service->GetRobotAccountId(), - scope_set, - this); + StartDeviceLoginAccessTokenRequest(); } else { - gaia_mint_token_mode_ = OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE; StartLoginAccessTokenRequest(); } return; @@ -386,37 +380,32 @@ void IdentityGetAuthTokenFunction::OnGetTokenSuccess( const OAuth2TokenService::Request* request, const std::string& access_token, const base::Time& expiration_time) { - if (login_token_request_.get() == request) { - login_token_request_.reset(); - StartGaiaRequest(access_token); - } else { - DCHECK_EQ(device_token_request_.get(), request); - device_token_request_.reset(); - - const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); - IdentityTokenCacheValue token(access_token, - expiration_time - base::Time::Now()); - IdentityAPI::GetFactoryInstance()->GetForProfile(profile())->SetCachedToken( - GetExtension()->id(), oauth2_info.scopes, token); - - CompleteMintTokenFlow(); - CompleteFunctionWithResult(access_token); - } + login_token_request_.reset(); + StartGaiaRequest(access_token); } void IdentityGetAuthTokenFunction::OnGetTokenFailure( const OAuth2TokenService::Request* request, const GoogleServiceAuthError& error) { - if (login_token_request_.get() == request) { - login_token_request_.reset(); - } else { - DCHECK_EQ(device_token_request_.get(), request); - device_token_request_.reset(); - } - + login_token_request_.reset(); OnGaiaFlowFailure(GaiaWebAuthFlow::SERVICE_AUTH_ERROR, error, std::string()); } +#if defined(OS_CHROMEOS) +void IdentityGetAuthTokenFunction::StartDeviceLoginAccessTokenRequest() { + chromeos::DeviceOAuth2TokenService* service = + chromeos::DeviceOAuth2TokenServiceFactory::Get(); + // Since robot account refresh tokens are scoped down to [any-api] only, + // request access token for [any-api] instead of login. + OAuth2TokenService::ScopeSet scopes; + scopes.insert(GaiaConstants::kAnyApiOAuth2Scope); + login_token_request_ = + service->StartRequest(service->GetRobotAccountId(), + scopes, + this); +} +#endif + void IdentityGetAuthTokenFunction::StartLoginAccessTokenRequest() { ProfileOAuth2TokenService* service = ProfileOAuth2TokenServiceFactory::GetForProfile(profile()); @@ -666,13 +655,12 @@ const base::Time& IdentityTokenCacheValue::expiration_time() const { IdentityAPI::IdentityAPI(Profile* profile) : profile_(profile), - error_(GoogleServiceAuthError::NONE) { - SigninGlobalError::GetForProfile(profile_)->AddProvider(this); - ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->AddObserver(this); + account_tracker_(profile), + identity_event_router_(profile) { + account_tracker_.AddObserver(this); } -IdentityAPI::~IdentityAPI() { -} +IdentityAPI::~IdentityAPI() {} IdentityMintRequestQueue* IdentityAPI::mint_queue() { return &mint_queue_; @@ -720,14 +708,14 @@ const IdentityAPI::CachedTokens& IdentityAPI::GetAllCachedTokens() { } void IdentityAPI::ReportAuthError(const GoogleServiceAuthError& error) { - error_ = error; - SigninGlobalError::GetForProfile(profile_)->AuthStatusChanged(); + ProfileOAuth2TokenService* token_service = + ProfileOAuth2TokenServiceFactory::GetForProfile(profile_); + account_tracker_.ReportAuthError(token_service->GetPrimaryAccountId(), error); } void IdentityAPI::Shutdown() { - SigninGlobalError::GetForProfile(profile_)->RemoveProvider(this); - ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)-> - RemoveObserver(this); + account_tracker_.RemoveObserver(this); + account_tracker_.Shutdown(); } static base::LazyInstance<ProfileKeyedAPIFactory<IdentityAPI> > @@ -738,24 +726,18 @@ ProfileKeyedAPIFactory<IdentityAPI>* IdentityAPI::GetFactoryInstance() { return &g_factory.Get(); } -std::string IdentityAPI::GetAccountId() const { - return ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)-> - GetPrimaryAccountId(); -} +void IdentityAPI::OnAccountAdded(const AccountIds& ids) {} -GoogleServiceAuthError IdentityAPI::GetAuthStatus() const { - return error_; -} +void IdentityAPI::OnAccountRemoved(const AccountIds& ids) {} -void IdentityAPI::OnRefreshTokenAvailable(const std::string& account_id) { - error_ = GoogleServiceAuthError::AuthErrorNone(); +void IdentityAPI::OnAccountSignInChanged(const AccountIds& ids, + bool is_signed_in) { + identity_event_router_.DispatchSignInEvent(ids.gaia, ids.email, is_signed_in); } template <> void ProfileKeyedAPIFactory<IdentityAPI>::DeclareFactoryDependencies() { DependsOn(ExtensionSystemFactory::GetInstance()); - // Need dependency on ProfileOAuth2TokenServiceFactory because it owns - // the SigninGlobalError instance. DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance()); } diff --git a/chrome/browser/extensions/api/identity/identity_api.h b/chrome/browser/extensions/api/identity/identity_api.h index 6918a786bd..a9a77468e1 100644 --- a/chrome/browser/extensions/api/identity/identity_api.h +++ b/chrome/browser/extensions/api/identity/identity_api.h @@ -13,7 +13,9 @@ #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" +#include "chrome/browser/extensions/api/identity/account_tracker.h" #include "chrome/browser/extensions/api/identity/gaia_web_auth_flow.h" +#include "chrome/browser/extensions/api/identity/identity_event_router.h" #include "chrome/browser/extensions/api/identity/identity_mint_queue.h" #include "chrome/browser/extensions/api/identity/identity_signin_flow.h" #include "chrome/browser/extensions/api/identity/web_auth_flow.h" @@ -128,6 +130,12 @@ class IdentityGetAuthTokenFunction : public AsyncExtensionFunction, // Starts a login access token request. virtual void StartLoginAccessTokenRequest(); +#if defined(OS_CHROMEOS) + // Starts a login access token request for device robot account. This method + // will be called only in enterprise kiosk mode in ChromeOS. + virtual void StartDeviceLoginAccessTokenRequest(); +#endif + // Starts a mint token request to GAIA. void StartGaiaRequest(const std::string& login_access_token); @@ -159,7 +167,6 @@ class IdentityGetAuthTokenFunction : public AsyncExtensionFunction, IssueAdviceInfo issue_advice_; scoped_ptr<GaiaWebAuthFlow> gaia_web_auth_flow_; scoped_ptr<IdentitySigninFlow> signin_flow_; - scoped_ptr<OAuth2TokenService::Request> device_token_request_; scoped_ptr<OAuth2TokenService::Request> login_token_request_; }; @@ -234,8 +241,7 @@ class IdentityTokenCacheValue { }; class IdentityAPI : public ProfileKeyedAPI, - public SigninGlobalError::AuthStatusProvider, - public OAuth2TokenService::Observer { + public AccountTracker::Observer { public: struct TokenCacheKey { TokenCacheKey(const std::string& extension_id, @@ -272,12 +278,11 @@ class IdentityAPI : public ProfileKeyedAPI, virtual void Shutdown() OVERRIDE; static ProfileKeyedAPIFactory<IdentityAPI>* GetFactoryInstance(); - // AuthStatusProvider implementation. - virtual std::string GetAccountId() const OVERRIDE; - virtual GoogleServiceAuthError GetAuthStatus() const OVERRIDE; - - // OAuth2TokenService::Observer implementation: - virtual void OnRefreshTokenAvailable(const std::string& account_id) OVERRIDE; + // AccountTracker::Observer implementation: + virtual void OnAccountAdded(const AccountIds& ids) OVERRIDE; + virtual void OnAccountRemoved(const AccountIds& ids) OVERRIDE; + virtual void OnAccountSignInChanged(const AccountIds& ids, bool is_signed_in) + OVERRIDE; private: friend class ProfileKeyedAPIFactory<IdentityAPI>; @@ -289,9 +294,10 @@ class IdentityAPI : public ProfileKeyedAPI, static const bool kServiceIsNULLWhileTesting = true; Profile* profile_; - GoogleServiceAuthError error_; IdentityMintRequestQueue mint_queue_; CachedTokens token_cache_; + AccountTracker account_tracker_; + IdentityEventRouter identity_event_router_; }; template <> diff --git a/chrome/browser/extensions/api/identity/identity_event_router.cc b/chrome/browser/extensions/api/identity/identity_event_router.cc new file mode 100644 index 0000000000..06a9ac8d53 --- /dev/null +++ b/chrome/browser/extensions/api/identity/identity_event_router.cc @@ -0,0 +1,73 @@ +// Copyright 2013 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/extensions/api/identity/identity_event_router.h" + +#include <set> + +#include "base/stl_util.h" +#include "base/values.h" +#include "chrome/browser/extensions/event_router.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_system.h" +#include "chrome/common/extensions/api/identity.h" + +namespace extensions { + +IdentityEventRouter::IdentityEventRouter(Profile* profile) + : profile_(profile) {} + +IdentityEventRouter::~IdentityEventRouter() {} + +void IdentityEventRouter::DispatchSignInEvent(const std::string& id, + const std::string& email, + bool is_signed_in) { + const EventListenerMap::ListenerList& listeners = + extensions::ExtensionSystem::Get(profile_)->event_router()->listeners() + .GetEventListenersByName(api::identity::OnSignInChanged::kEventName); + + ExtensionService* service = + ExtensionSystem::Get(profile_)->extension_service(); + EventRouter* event_router = ExtensionSystem::Get(profile_)->event_router(); + + api::identity::AccountInfo account_info; + account_info.id = id; + + api::identity::AccountInfo account_info_email; + account_info_email.id = id; + account_info_email.email = scoped_ptr<std::string>(new std::string(email)); + + std::set<std::string> already_dispatched; + + for (EventListenerMap::ListenerList::const_iterator it = listeners.begin(); + it != listeners.end(); + ++it) { + + const std::string extension_id = (*it)->extension_id; + const Extension* extension = service->extensions()->GetByID(extension_id); + + if (ContainsKey(already_dispatched, extension_id)) + continue; + + already_dispatched.insert(extension_id); + + // Add the email address to AccountInfo only for extensions that + // have APIPermission::kIdentityEmail. + scoped_ptr<base::ListValue> args; + if (extension->HasAPIPermission(APIPermission::kIdentityEmail)) { + args = api::identity::OnSignInChanged::Create(account_info_email, + is_signed_in); + } else { + args = api::identity::OnSignInChanged::Create(account_info, + is_signed_in); + } + + scoped_ptr<Event> event( + new Event(api::identity::OnSignInChanged::kEventName, args.Pass())); + event->restrict_to_profile = profile_; + event_router->DispatchEventToExtension(extension_id, event.Pass()); + } +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/identity/identity_event_router.h b/chrome/browser/extensions/api/identity/identity_event_router.h new file mode 100644 index 0000000000..21bf2eb734 --- /dev/null +++ b/chrome/browser/extensions/api/identity/identity_event_router.h @@ -0,0 +1,35 @@ +// Copyright 2013 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_EXTENSIONS_API_IDENTITY_IDENTITY_EVENT_ROUTER_H_ +#define CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_EVENT_ROUTER_H_ + +#include <string> + +#include "base/basictypes.h" + +class Profile; + +namespace extensions { + +class IdentityEventRouter { + public: + explicit IdentityEventRouter(Profile* profile); + ~IdentityEventRouter(); + + // Dispatch identity.onSignInChanged event, including email address + // for extensions with the identity.email permission. + void DispatchSignInEvent(const std::string& id, + const std::string& email, + bool is_signed_in); + + private: + Profile* profile_; + + DISALLOW_COPY_AND_ASSIGN(IdentityEventRouter); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_EVENT_ROUTER_H_ diff --git a/chrome/browser/extensions/api/identity/identity_event_router_unittest.cc b/chrome/browser/extensions/api/identity/identity_event_router_unittest.cc new file mode 100644 index 0000000000..8b65c5cec8 --- /dev/null +++ b/chrome/browser/extensions/api/identity/identity_event_router_unittest.cc @@ -0,0 +1,291 @@ +// Copyright 2013 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/extensions/api/identity/identity_event_router.h" + +#include <map> +#include <string> +#include <vector> + +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/path_service.h" +#include "base/stl_util.h" +#include "base/strings/stringprintf.h" +#include "base/values.h" +#include "chrome/browser/extensions/event_router.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_system_factory.h" +#include "chrome/browser/extensions/test_extension_service.h" +#include "chrome/browser/extensions/test_extension_system.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/extensions/api/identity.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_builder.h" +#include "chrome/common/extensions/extension_set.h" +#include "chrome/common/extensions/permissions/permissions_data.h" +#include "chrome/common/extensions/value_builder.h" +#include "chrome/test/base/testing_profile.h" +#include "content/public/test/mock_render_process_host.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "extensions/common/permissions/api_permission.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +struct EventInfo { + std::string user_id; + std::string email; + bool is_signed_in; +}; + +class FakeEventRouter : public extensions::EventRouter { + public: + explicit FakeEventRouter(Profile* profile) : EventRouter(profile, NULL) {} + + virtual void DispatchEventToExtension( + const std::string& extension_id, + scoped_ptr<extensions::Event> event) OVERRIDE { + EventInfo event_info; + base::DictionaryValue* event_object = NULL; + EXPECT_TRUE(event->event_args->GetDictionary(0, &event_object)); + EXPECT_TRUE(event_object->GetString("id", &event_info.user_id)); + event_object->GetString("email", &event_info.email); + + EXPECT_TRUE(event->event_args->GetBoolean(1, &event_info.is_signed_in)); + + EXPECT_FALSE(ContainsKey(extension_id_to_event_, extension_id)); + extension_id_to_event_[extension_id] = event_info; + } + + size_t GetEventCount() { + return extension_id_to_event_.size(); + } + + bool ContainsExtensionId(const std::string extension_id) { + return ContainsKey(extension_id_to_event_, extension_id); + } + + const EventInfo& GetEventInfo(const std::string extension_id) { + return extension_id_to_event_[extension_id]; + } + + private: + std::map<std::string, EventInfo> extension_id_to_event_; + + DISALLOW_COPY_AND_ASSIGN(FakeEventRouter); +}; + +class FakeExtensionService : public TestExtensionService { + public: + FakeExtensionService() {} + virtual ~FakeExtensionService() {} + + virtual const ExtensionSet* extensions() const OVERRIDE { + return &extensions_; + } + + virtual void AddExtension(const extensions::Extension* extension) OVERRIDE { + extensions_.Insert(extension); + } + + private: + ExtensionSet extensions_; +}; + +class FakeExtensionSystem : public extensions::TestExtensionSystem { + public: + explicit FakeExtensionSystem(Profile* profile) + : extensions::TestExtensionSystem(profile) {} + + virtual extensions::EventRouter* event_router() OVERRIDE { + return fake_event_router(); + } + + virtual ExtensionService* extension_service() OVERRIDE { + ExtensionServiceInterface* as_interface = + static_cast<ExtensionServiceInterface*>(&fake_extension_service_); + return static_cast<ExtensionService*>(as_interface); + } + + FakeEventRouter* fake_event_router() { + if (!fake_event_router_) + fake_event_router_.reset(new FakeEventRouter(profile_)); + return fake_event_router_.get(); + } + + private: + FakeExtensionService fake_extension_service_; + scoped_ptr<FakeEventRouter> fake_event_router_; + + DISALLOW_COPY_AND_ASSIGN(FakeExtensionSystem); +}; + +BrowserContextKeyedService* BuildFakeExtensionSystem( + content::BrowserContext* profile) { + return new FakeExtensionSystem(static_cast<Profile*>(profile)); +} + +} // namespace + +namespace extensions { + +class IdentityEventRouterTest : public testing::Test { + public: + IdentityEventRouterTest() + : test_profile_(new TestingProfile()), + identity_event_router_(test_profile_.get()), + extension_counter_(0) {} + + virtual void SetUp() OVERRIDE { + fake_extension_system_ = static_cast<FakeExtensionSystem*>( + ExtensionSystemFactory::GetInstance()->SetTestingFactoryAndUse( + test_profile_.get(), &BuildFakeExtensionSystem)); + } + + FakeEventRouter* fake_event_router() { + return fake_extension_system_->fake_event_router(); + } + + Profile* profile() { + return test_profile_.get(); + } + + protected: + scoped_refptr<const Extension> CreateExtension(bool has_email_permission) { + ListBuilder permissions; + if (has_email_permission) + permissions.Append("identity.email"); + + std::string id = base::StringPrintf("id.%d", extension_counter_++); + scoped_refptr<const Extension> extension = ExtensionBuilder() + .SetID(id) + .SetManifest(DictionaryBuilder() + .Set("name", "Extension with ID " + id) + .Set("version", "1.0") + .Set("manifest_version", 2) + .Set("permissions", permissions)) + .Build(); + fake_extension_system_->extension_service()->AddExtension(extension.get()); + fake_event_router()->AddEventListener( + api::identity::OnSignInChanged::kEventName, NULL, extension->id()); + return extension; + } + + scoped_ptr<TestingProfile> test_profile_; + IdentityEventRouter identity_event_router_; + FakeExtensionSystem* fake_extension_system_; + content::TestBrowserThreadBundle thread_bundle_; + int extension_counter_; +}; + +TEST_F(IdentityEventRouterTest, SignInNoListeners) { + identity_event_router_.DispatchSignInEvent( + "test_user_id", "test_email", true); + EXPECT_EQ(0ul, fake_event_router()->GetEventCount()); +} + +TEST_F(IdentityEventRouterTest, SignInNoEmailListener) { + scoped_refptr<const Extension> ext = CreateExtension(false); + identity_event_router_.DispatchSignInEvent( + "test_user_id", "test_email", true); + EXPECT_EQ(1ul, fake_event_router()->GetEventCount()); + EXPECT_TRUE(fake_event_router()->ContainsExtensionId(ext->id())); + EXPECT_EQ("test_user_id", + fake_event_router()->GetEventInfo(ext->id()).user_id); + EXPECT_TRUE(fake_event_router()->GetEventInfo(ext->id()).email.empty()); + EXPECT_TRUE(fake_event_router()->GetEventInfo(ext->id()).is_signed_in); +} + +TEST_F(IdentityEventRouterTest, SignInWithEmailListener) { + scoped_refptr<const Extension> ext = CreateExtension(true); + identity_event_router_.DispatchSignInEvent( + "test_user_id", "test_email", true); + EXPECT_EQ(1ul, fake_event_router()->GetEventCount()); + EXPECT_TRUE(fake_event_router()->ContainsExtensionId(ext->id())); + EXPECT_EQ("test_user_id", + fake_event_router()->GetEventInfo(ext->id()).user_id); + EXPECT_EQ("test_email", fake_event_router()->GetEventInfo(ext->id()).email); + EXPECT_TRUE(fake_event_router()->GetEventInfo(ext->id()).is_signed_in); +} + +TEST_F(IdentityEventRouterTest, SignInMultipleListeners) { + typedef std::vector<scoped_refptr<const Extension> > ExtensionVector; + ExtensionVector with_email; + ExtensionVector no_email; + + for (int i = 0; i < 3; i++) + with_email.push_back(CreateExtension(true)); + + for (int i = 0; i < 2; i++) + no_email.push_back(CreateExtension(false)); + + identity_event_router_.DispatchSignInEvent( + "test_user_id", "test_email", true); + + EXPECT_EQ(with_email.size() + no_email.size(), + fake_event_router()->GetEventCount()); + + for (ExtensionVector::const_iterator it = with_email.begin(); + it != with_email.end(); + ++it) { + EXPECT_TRUE(fake_event_router()->ContainsExtensionId((*it)->id())); + EXPECT_EQ("test_user_id", + fake_event_router()->GetEventInfo((*it)->id()).user_id); + EXPECT_EQ("test_email", + fake_event_router()->GetEventInfo((*it)->id()).email); + EXPECT_TRUE(fake_event_router()->GetEventInfo((*it)->id()).is_signed_in); + } + + for (ExtensionVector::const_iterator it = no_email.begin(); + it != no_email.end(); + ++it) { + EXPECT_TRUE(fake_event_router()->ContainsExtensionId((*it)->id())); + EXPECT_EQ("test_user_id", + fake_event_router()->GetEventInfo((*it)->id()).user_id); + EXPECT_TRUE(fake_event_router()->GetEventInfo((*it)->id()).email.empty()); + EXPECT_TRUE(fake_event_router()->GetEventInfo((*it)->id()).is_signed_in); + } +} + +TEST_F(IdentityEventRouterTest, SignInWithTwoListenersOnOneExtension) { + scoped_refptr<const Extension> ext = CreateExtension(true); + + scoped_ptr<content::MockRenderProcessHost> fake_render_process( + new content::MockRenderProcessHost(profile())); + fake_event_router()->AddEventListener( + api::identity::OnSignInChanged::kEventName, + fake_render_process.get(), + ext->id()); + + identity_event_router_.DispatchSignInEvent( + "test_user_id", "test_email", true); + EXPECT_EQ(1ul, fake_event_router()->GetEventCount()); + EXPECT_TRUE(fake_event_router()->ContainsExtensionId(ext->id())); + EXPECT_EQ("test_user_id", + fake_event_router()->GetEventInfo(ext->id()).user_id); + EXPECT_EQ("test_email", fake_event_router()->GetEventInfo(ext->id()).email); + EXPECT_TRUE(fake_event_router()->GetEventInfo(ext->id()).is_signed_in); + + fake_event_router()->RemoveEventListener( + api::identity::OnSignInChanged::kEventName, + fake_render_process.get(), + ext->id()); +} + +TEST_F(IdentityEventRouterTest, SignOut) { + scoped_refptr<const Extension> ext = CreateExtension(false); + identity_event_router_.DispatchSignInEvent( + "test_user_id", "test_email", false); + EXPECT_EQ(1ul, fake_event_router()->GetEventCount()); + EXPECT_TRUE(fake_event_router()->ContainsExtensionId(ext->id())); + EXPECT_EQ("test_user_id", + fake_event_router()->GetEventInfo(ext->id()).user_id); + EXPECT_TRUE(fake_event_router()->GetEventInfo(ext->id()).email.empty()); + EXPECT_FALSE(fake_event_router()->GetEventInfo(ext->id()).is_signed_in); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/identity/web_auth_flow.cc b/chrome/browser/extensions/api/identity/web_auth_flow.cc index 2c50724531..f59a6b7938 100644 --- a/chrome/browser/extensions/api/identity/web_auth_flow.cc +++ b/chrome/browser/extensions/api/identity/web_auth_flow.cc @@ -223,6 +223,7 @@ void WebAuthFlow::DidStartProvisionalLoadForFrame( } void WebAuthFlow::DidFailProvisionalLoad(int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& validated_url, int error_code, diff --git a/chrome/browser/extensions/api/identity/web_auth_flow.h b/chrome/browser/extensions/api/identity/web_auth_flow.h index e1a98523fb..d9a4336a3d 100644 --- a/chrome/browser/extensions/api/identity/web_auth_flow.h +++ b/chrome/browser/extensions/api/identity/web_auth_flow.h @@ -118,6 +118,7 @@ class WebAuthFlow : public content::NotificationObserver, bool is_iframe_srcdoc, content::RenderViewHost* render_view_host) OVERRIDE; virtual void DidFailProvisionalLoad(int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& validated_url, int error_code, diff --git a/chrome/browser/extensions/api/input_ime/input_ime_api.cc b/chrome/browser/extensions/api/input_ime/input_ime_api.cc index 54ca0d40e2..bd8048e327 100644 --- a/chrome/browser/extensions/api/input_ime/input_ime_api.cc +++ b/chrome/browser/extensions/api/input_ime/input_ime_api.cc @@ -34,7 +34,6 @@ namespace SetComposition = extensions::api::input_ime::SetComposition; namespace { const char kErrorEngineNotAvailable[] = "Engine is not available"; -const char kErrorBadCandidateList[] = "Invalid candidate list provided"; const char kErrorSetMenuItemsFail[] = "Could not create menu Items"; const char kErrorUpdateMenuItemsFail[] = "Could not update menu Items"; diff --git a/chrome/browser/extensions/api/management/management_apitest.cc b/chrome/browser/extensions/api/management/management_apitest.cc index 0dc25451a3..3a0fccbff9 100644 --- a/chrome/browser/extensions/api/management/management_apitest.cc +++ b/chrome/browser/extensions/api/management/management_apitest.cc @@ -70,7 +70,7 @@ class ExtensionManagementApiTest : public ExtensionApiTest { ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII(app_path))); if (out_app_id) - *out_app_id = last_loaded_extension_id_; + *out_app_id = last_loaded_extension_id(); ASSERT_TRUE(launched_app.WaitUntilSatisfied()); } diff --git a/chrome/browser/extensions/api/mdns/dns_sd_delegate.h b/chrome/browser/extensions/api/mdns/dns_sd_delegate.h index cca02c62e1..d1c2df48c1 100644 --- a/chrome/browser/extensions/api/mdns/dns_sd_delegate.h +++ b/chrome/browser/extensions/api/mdns/dns_sd_delegate.h @@ -36,6 +36,7 @@ class DnsSdDelegate { const DnsSdService& service) = 0; virtual void ServiceRemoved(const std::string& service_type, const std::string& service_name) = 0; + virtual void ServicesFlushed(const std::string& service_type) = 0; }; } // namespace extensions diff --git a/chrome/browser/extensions/api/mdns/dns_sd_device_lister.cc b/chrome/browser/extensions/api/mdns/dns_sd_device_lister.cc index fdb7fe4231..2deea48325 100644 --- a/chrome/browser/extensions/api/mdns/dns_sd_device_lister.cc +++ b/chrome/browser/extensions/api/mdns/dns_sd_device_lister.cc @@ -4,17 +4,9 @@ #include "chrome/browser/extensions/api/mdns/dns_sd_device_lister.h" -#include <utility> - -#include "base/bind.h" -#include "chrome/browser/local_discovery/service_discovery_shared_client.h" #include "chrome/common/extensions/api/mdns.h" -#include "net/base/net_util.h" using local_discovery::ServiceDescription; -using local_discovery::ServiceDiscoverySharedClient; -using local_discovery::ServiceResolver; -using local_discovery::ServiceWatcher; namespace extensions { @@ -38,74 +30,40 @@ void FillServiceInfo(const ServiceDescription& service_description, } // namespace DnsSdDeviceLister::DnsSdDeviceLister( + local_discovery::ServiceDiscoveryClient* service_discovery_client, DnsSdDelegate* delegate, - const std::string& service_type, - ServiceDiscoverySharedClient* service_discovery_client) + const std::string& service_type) : delegate_(delegate), - service_type_(service_type), - service_discovery_client_(service_discovery_client) { + device_lister_(this, service_discovery_client, service_type), + started_(false) { } -DnsSdDeviceLister::~DnsSdDeviceLister() {} - -void DnsSdDeviceLister::Discover(bool force_update) { - if (!service_discovery_client_) - return; - - if (!service_watcher_.get()) { - service_watcher_ = service_discovery_client_->CreateServiceWatcher( - service_type_, - base::Bind(&DnsSdDeviceLister::OnServiceUpdated, - base::Unretained(this))); - service_watcher_->Start(); - } - service_watcher_->DiscoverNewServices(force_update); +DnsSdDeviceLister::~DnsSdDeviceLister() { } -void DnsSdDeviceLister::OnServiceUpdated( - ServiceWatcher::UpdateType update, - const std::string& service_name) { - // TODO(justinlin): Consolidate with PrivetDeviceListerImpl. - if (update != ServiceWatcher::UPDATE_REMOVED) { - bool added = (update == ServiceWatcher::UPDATE_ADDED); - std::pair<ServiceResolverMap::iterator, bool> insert_result = - resolvers_.insert(make_pair(service_name, - linked_ptr<ServiceResolver>(NULL))); - - // If there is already a resolver working on this service, don't add one. - if (insert_result.second) { - scoped_ptr<ServiceResolver> resolver = - service_discovery_client_->CreateServiceResolver( - service_name, base::Bind( - &DnsSdDeviceLister::OnResolveComplete, - base::Unretained(this), - added)); - - insert_result.first->second.reset(resolver.release()); - insert_result.first->second->StartResolving(); - } - } else { - delegate_->ServiceRemoved(service_type_, service_name); +void DnsSdDeviceLister::Discover(bool force_update) { + if (!started_) { + device_lister_.Start(); + started_ = true; } + device_lister_.DiscoverNewDevices(force_update); } -void DnsSdDeviceLister::OnResolveComplete( +void DnsSdDeviceLister::OnDeviceChanged( bool added, - ServiceResolver::RequestStatus status, const ServiceDescription& service_description) { - // TODO(justinlin): Consolidate with PrivetDeviceListerImpl. - if (status != ServiceResolver::STATUS_SUCCESS) { - resolvers_.erase(service_description.service_name); - - // TODO(noamsml): Add retry logic. - return; - } - DnsSdService service; FillServiceInfo(service_description, &service); + delegate_->ServiceChanged(device_lister_.service_type(), added, service); +} + +void DnsSdDeviceLister::OnDeviceRemoved(const std::string& service_name) { + delegate_->ServiceRemoved(device_lister_.service_type(), service_name); +} - resolvers_.erase(service_description.service_name); - delegate_->ServiceChanged(service_type_, added, service); +void DnsSdDeviceLister::OnDeviceCacheFlushed() { + delegate_->ServicesFlushed(device_lister_.service_type()); + device_lister_.DiscoverNewDevices(false); } } // namespace extensions diff --git a/chrome/browser/extensions/api/mdns/dns_sd_device_lister.h b/chrome/browser/extensions/api/mdns/dns_sd_device_lister.h index 4c6f49c2ce..060af2fafc 100644 --- a/chrome/browser/extensions/api/mdns/dns_sd_device_lister.h +++ b/chrome/browser/extensions/api/mdns/dns_sd_device_lister.h @@ -5,65 +5,42 @@ #ifndef CHROME_BROWSER_EXTENSIONS_API_MDNS_DNS_SD_DEVICE_LISTER_H_ #define CHROME_BROWSER_EXTENSIONS_API_MDNS_DNS_SD_DEVICE_LISTER_H_ -#include <map> #include <string> -#include "base/basictypes.h" -#include "base/memory/linked_ptr.h" -#include "base/memory/scoped_ptr.h" #include "chrome/browser/extensions/api/mdns/dns_sd_delegate.h" -#include "chrome/common/local_discovery/service_discovery_client.h" +#include "chrome/browser/local_discovery/service_discovery_device_lister.h" namespace local_discovery { -class ServiceDiscoverySharedClient; +class ServiceDiscoveryClient; } // local_discovery namespace extensions { // Manages a watcher for a specific MDNS/DNS-SD service type and notifies // a delegate of changes to watched services. -class DnsSdDeviceLister { +class DnsSdDeviceLister + : public local_discovery::ServiceDiscoveryDeviceLister::Delegate { public: DnsSdDeviceLister( + local_discovery::ServiceDiscoveryClient* service_discovery_client, DnsSdDelegate* delegate, - const std::string& service_type, - local_discovery::ServiceDiscoverySharedClient* service_discovery_client); + const std::string& service_type); virtual ~DnsSdDeviceLister(); - // Requests that the service watcher issue an immediate query for services. - // force_update will first clear the service cache. This must be called - // to instantiate the service watcher and start discovery. virtual void Discover(bool force_update); protected: - // Invoked when the watcher notifies us of a service change. - void OnServiceUpdated( - local_discovery::ServiceWatcher::UpdateType update, - const std::string& service_name); - - void OnResolveComplete( - bool added, - local_discovery::ServiceResolver::RequestStatus status, - const local_discovery::ServiceDescription& description); + virtual void OnDeviceChanged( + bool added, + const local_discovery::ServiceDescription& service_description) OVERRIDE; + virtual void OnDeviceRemoved(const std::string& service_name) OVERRIDE; + virtual void OnDeviceCacheFlushed() OVERRIDE; private: - typedef std::map<std::string, linked_ptr<local_discovery::ServiceResolver> > - ServiceResolverMap; - // The delegate to notify of changes to services. DnsSdDelegate* const delegate_; - - // The service type for this watcher. - const std::string service_type_; - - // The instance of the service discovery client. - scoped_refptr<local_discovery::ServiceDiscoverySharedClient> - service_discovery_client_; - - // The instance of the service watcher. - scoped_ptr<local_discovery::ServiceWatcher> service_watcher_; - - ServiceResolverMap resolvers_; + local_discovery::ServiceDiscoveryDeviceLister device_lister_; + bool started_; DISALLOW_COPY_AND_ASSIGN(DnsSdDeviceLister); }; diff --git a/chrome/browser/extensions/api/mdns/dns_sd_registry.cc b/chrome/browser/extensions/api/mdns/dns_sd_registry.cc index 6a72fc52ff..e4e92dd4c5 100644 --- a/chrome/browser/extensions/api/mdns/dns_sd_registry.cc +++ b/chrome/browser/extensions/api/mdns/dns_sd_registry.cc @@ -73,6 +73,14 @@ bool DnsSdRegistry::ServiceTypeData::RemoveService( return false; }; +bool DnsSdRegistry::ServiceTypeData::ClearServices() { + if (service_list_.empty()) + return false; + + service_list_.clear(); + return true; +} + const DnsSdRegistry::DnsSdServiceList& DnsSdRegistry::ServiceTypeData::GetServiceList() { return service_list_; @@ -102,14 +110,14 @@ DnsSdDeviceLister* DnsSdRegistry::CreateDnsSdDeviceLister( DnsSdDelegate* delegate, const std::string& service_type, local_discovery::ServiceDiscoverySharedClient* discovery_client) { - return new DnsSdDeviceLister(delegate, service_type, discovery_client); + return new DnsSdDeviceLister(discovery_client, delegate, service_type); } void DnsSdRegistry::RegisterDnsSdListener(std::string service_type) { if (service_type.empty()) return; - if (service_data_map_.find(service_type) != service_data_map_.end()) { + if (IsRegistered(service_type)) { service_data_map_[service_type]->ListenerAdded(); DispatchApiEvent(service_type); return; @@ -137,7 +145,7 @@ void DnsSdRegistry::UnregisterDnsSdListener(std::string service_type) { void DnsSdRegistry::ServiceChanged(const std::string& service_type, bool added, const DnsSdService& service) { - if (service_data_map_.find(service_type) == service_data_map_.end()) + if (!IsRegistered(service_type)) return; if (service_data_map_[service_type]->UpdateService(added, service)) { @@ -150,7 +158,7 @@ void DnsSdRegistry::ServiceChanged(const std::string& service_type, void DnsSdRegistry::ServiceRemoved(const std::string& service_type, const std::string& service_name) { - if (service_data_map_.find(service_type) == service_data_map_.end()) + if (!IsRegistered(service_type)) return; if (service_data_map_[service_type]->RemoveService(service_name)) { @@ -160,9 +168,23 @@ void DnsSdRegistry::ServiceRemoved(const std::string& service_type, } } +void DnsSdRegistry::ServicesFlushed(const std::string& service_type) { + if (!IsRegistered(service_type)) + return; + + if (service_data_map_[service_type]->ClearServices()) + DispatchApiEvent(service_type); +} + void DnsSdRegistry::DispatchApiEvent(const std::string& service_type) { + // TODO(justinlin): Make this MaybeDispatchApiEvent instead and dispatch if a + // dirty bit is set. FOR_EACH_OBSERVER(DnsSdObserver, observers_, OnDnsSdEvent( service_type, service_data_map_[service_type]->GetServiceList())); } +bool DnsSdRegistry::IsRegistered(const std::string& service_type) { + return service_data_map_.find(service_type) != service_data_map_.end(); +} + } // namespace extensions diff --git a/chrome/browser/extensions/api/mdns/dns_sd_registry.h b/chrome/browser/extensions/api/mdns/dns_sd_registry.h index 79d898809f..e3d59d8461 100644 --- a/chrome/browser/extensions/api/mdns/dns_sd_registry.h +++ b/chrome/browser/extensions/api/mdns/dns_sd_registry.h @@ -68,6 +68,7 @@ class DnsSdRegistry : public DnsSdDelegate { // Methods for adding, updating or removing services for this service type. bool UpdateService(bool added, const DnsSdService& service); bool RemoveService(const std::string& service_name); + bool ClearServices(); const DnsSdRegistry::DnsSdServiceList& GetServiceList(); @@ -93,11 +94,13 @@ class DnsSdRegistry : public DnsSdDelegate { const DnsSdService& service) OVERRIDE; virtual void ServiceRemoved(const std::string& service_type, const std::string& service_name) OVERRIDE; + virtual void ServicesFlushed(const std::string& service_type) OVERRIDE; DnsSdServiceTypeDataMap service_data_map_; private: void DispatchApiEvent(const std::string& service_type); + bool IsRegistered(const std::string& service_type); scoped_refptr<local_discovery::ServiceDiscoverySharedClient> service_discovery_client_; diff --git a/chrome/browser/extensions/api/mdns/dns_sd_registry_unittest.cc b/chrome/browser/extensions/api/mdns/dns_sd_registry_unittest.cc index 451dc6ad3b..ee9bbe58a8 100644 --- a/chrome/browser/extensions/api/mdns/dns_sd_registry_unittest.cc +++ b/chrome/browser/extensions/api/mdns/dns_sd_registry_unittest.cc @@ -13,10 +13,10 @@ namespace extensions { class MockDnsSdDeviceLister : public DnsSdDeviceLister { public: - MockDnsSdDeviceLister() : DnsSdDeviceLister(NULL, "", NULL) {} + MockDnsSdDeviceLister() : DnsSdDeviceLister(NULL, NULL, "") {} virtual ~MockDnsSdDeviceLister() {} - virtual void Discover(bool forced) OVERRIDE {} + virtual void Discover(bool force_update) OVERRIDE {} }; class TestDnsSdRegistry : public DnsSdRegistry { @@ -43,8 +43,8 @@ class TestDnsSdRegistry : public DnsSdRegistry { virtual DnsSdDeviceLister* CreateDnsSdDeviceLister( DnsSdDelegate* delegate, const std::string& service_type, - local_discovery::ServiceDiscoverySharedClient* - discovery_client) OVERRIDE { + local_discovery::ServiceDiscoverySharedClient* discovery_client) + OVERRIDE { delegate_ = delegate; MockDnsSdDeviceLister* lister = new MockDnsSdDeviceLister(); listers_[service_type] = lister; @@ -173,4 +173,33 @@ TEST_F(DnsSdRegistryTest, AddMultipleServices) { registry_->GetDelegate()->ServiceChanged(service_type, true, service2); } +// Tests adding multiple services and handling a flush event. +TEST_F(DnsSdRegistryTest, FlushCache) { + testing::InSequence s; + const std::string service_type = "_testing._tcp.local"; + + DnsSdService service; + service.service_name = "_myDevice." + service_type; + service.ip_address = "192.168.0.100"; + + DnsSdService service2; + service.service_name = "_myDevice2." + service_type; + service.ip_address = "192.168.0.101"; + + DnsSdRegistry::DnsSdServiceList service_list; + EXPECT_CALL(observer_, OnDnsSdEvent(service_type, service_list)); + service_list.push_back(service); + EXPECT_CALL(observer_, OnDnsSdEvent(service_type, service_list)); + service_list.push_back(service2); + EXPECT_CALL(observer_, OnDnsSdEvent(service_type, service_list)); + service_list.clear(); + EXPECT_CALL(observer_, OnDnsSdEvent(service_type, service_list)); + + registry_->RegisterDnsSdListener(service_type); + registry_->GetDelegate()->ServiceChanged(service_type, true, service); + registry_->GetDelegate()->ServiceChanged(service_type, true, service2); + registry_->GetDelegate()->ServicesFlushed(service_type); +} + + } // namespace extensions diff --git a/chrome/browser/extensions/api/media_galleries/media_galleries_api.cc b/chrome/browser/extensions/api/media_galleries/media_galleries_api.cc index c07e229b65..0358fa31fd 100644 --- a/chrome/browser/extensions/api/media_galleries/media_galleries_api.cc +++ b/chrome/browser/extensions/api/media_galleries/media_galleries_api.cc @@ -196,11 +196,10 @@ void MediaGalleriesGetMediaFileSystemsFunction::ReturnGalleries( if (filesystems[i].path.empty()) continue; - if (has_read_permission || has_copy_to_permission) { + if (has_read_permission) { content::ChildProcessSecurityPolicy* policy = ChildProcessSecurityPolicy::GetInstance(); - if (has_read_permission) - policy->GrantReadFileSystem(child_id, filesystems[i].fsid); + policy->GrantReadFileSystem(child_id, filesystems[i].fsid); if (has_copy_to_permission) policy->GrantCopyIntoFileSystem(child_id, filesystems[i].fsid); } diff --git a/chrome/browser/extensions/api/media_galleries_private/media_galleries_private_api.cc b/chrome/browser/extensions/api/media_galleries_private/media_galleries_private_api.cc index 34801a4461..2fb85d7de3 100644 --- a/chrome/browser/extensions/api/media_galleries_private/media_galleries_private_api.cc +++ b/chrome/browser/extensions/api/media_galleries_private/media_galleries_private_api.cc @@ -19,6 +19,7 @@ #include "chrome/browser/extensions/extension_function.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/media_galleries/media_file_system_registry.h" #include "chrome/browser/media_galleries/media_galleries_preferences.h" #include "chrome/browser/profiles/profile.h" @@ -381,7 +382,7 @@ bool MediaGalleriesPrivateGetHandlersFunction::RunImpl() { ++iter) { const Extension* extension = iter->get(); if (profile_->IsOffTheRecord() && - !service->IsIncognitoEnabled(extension->id())) + !extension_util::IsIncognitoEnabled(extension->id(), service)) continue; MediaGalleriesHandler::List* handler_list = diff --git a/chrome/browser/extensions/api/media_galleries_private/media_galleries_watch_apitest.cc b/chrome/browser/extensions/api/media_galleries_private/media_galleries_watch_apitest.cc index bc98bb11a5..1447203859 100644 --- a/chrome/browser/extensions/api/media_galleries_private/media_galleries_watch_apitest.cc +++ b/chrome/browser/extensions/api/media_galleries_private/media_galleries_watch_apitest.cc @@ -26,6 +26,7 @@ namespace { const char kTestExtensionId[] = "gceegfkgibmgpfopknlcgleimclbknie"; const char kTestExtensionPath[] = "media_galleries_private/gallerywatch"; +#if !defined(OS_CHROMEOS) // JS commands. const char kGetAllWatchedGalleryIdsCmd[] = "getAllWatchedGalleryIds()"; const char kGetMediaFileSystemsCmd[] = "getMediaFileSystems()"; @@ -62,6 +63,7 @@ const char kGalleryChangedEventReceived[] = "gallery_changed_event_received"; const char kGetAllGalleryWatchResultB[] = "watchers_for_galleries_{1, 2, 3}_found"; #endif // defined(OS_WIN) +#endif // !defined(OS_CHROMEOS) } // namespace diff --git a/chrome/browser/extensions/api/messaging/incognito_connectability.cc b/chrome/browser/extensions/api/messaging/incognito_connectability.cc new file mode 100644 index 0000000000..eca7bd22a9 --- /dev/null +++ b/chrome/browser/extensions/api/messaging/incognito_connectability.cc @@ -0,0 +1,124 @@ +// Copyright 2013 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/extensions/api/messaging/incognito_connectability.h" + +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/ui/simple_message_box.h" +#include "chrome/common/extensions/extension.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_view.h" +#include "grit/generated_resources.h" +#include "ui/base/l10n/l10n_util.h" + +namespace extensions { + +namespace { +IncognitoConnectability::ScopedAlertTracker::Mode g_alert_mode = + IncognitoConnectability::ScopedAlertTracker::INTERACTIVE; +int g_alert_count = 0; +} + +IncognitoConnectability::ScopedAlertTracker::ScopedAlertTracker(Mode mode) + : last_checked_invocation_count_(g_alert_count) { + DCHECK_EQ(INTERACTIVE, g_alert_mode); + DCHECK_NE(INTERACTIVE, mode); + g_alert_mode = mode; +} + +IncognitoConnectability::ScopedAlertTracker::~ScopedAlertTracker() { + DCHECK_NE(INTERACTIVE, g_alert_mode); + g_alert_mode = INTERACTIVE; +} + +int IncognitoConnectability::ScopedAlertTracker::GetAndResetAlertCount() { + int result = g_alert_count - last_checked_invocation_count_; + last_checked_invocation_count_ = g_alert_count; + return result; +} + +IncognitoConnectability::IncognitoConnectability(Profile* profile) { + CHECK(profile->IsOffTheRecord()); +} + +IncognitoConnectability::~IncognitoConnectability() { +} + +// static +IncognitoConnectability* IncognitoConnectability::Get(Profile* profile) { + return ProfileKeyedAPIFactory<IncognitoConnectability>::GetForProfile( + profile); +} + +bool IncognitoConnectability::Query(const Extension* extension, + content::WebContents* web_contents, + const GURL& url) { + GURL origin = url.GetOrigin(); + if (origin.is_empty()) + return false; + + if (IsInMap(extension, origin, allowed_origins_)) + return true; + if (IsInMap(extension, origin, disallowed_origins_)) + return false; + + // We need to ask the user. + ++g_alert_count; + chrome::MessageBoxResult result = chrome::MESSAGE_BOX_RESULT_NO; + + switch (g_alert_mode) { + // Production code should always be using INTERACTIVE. + case ScopedAlertTracker::INTERACTIVE: { + int template_id = extension->is_app() ? + IDS_EXTENSION_PROMPT_APP_CONNECT_FROM_INCOGNITO : + IDS_EXTENSION_PROMPT_EXTENSION_CONNECT_FROM_INCOGNITO; + result = chrome::ShowMessageBox( + web_contents ? web_contents->GetView()->GetTopLevelNativeWindow() + : NULL, + string16(), // no title + l10n_util::GetStringFUTF16(template_id, + UTF8ToUTF16(origin.spec()), + UTF8ToUTF16(extension->name())), + chrome::MESSAGE_BOX_TYPE_QUESTION); + break; + } + + // Testing code can override to always allow or deny. + case ScopedAlertTracker::ALWAYS_ALLOW: + result = chrome::MESSAGE_BOX_RESULT_YES; + break; + case ScopedAlertTracker::ALWAYS_DENY: + result = chrome::MESSAGE_BOX_RESULT_NO; + break; + } + + if (result == chrome::MESSAGE_BOX_RESULT_NO) { + disallowed_origins_[extension->id()].insert(origin); + return false; + } + allowed_origins_[extension->id()].insert(origin); + return true; +} + +bool IncognitoConnectability::IsInMap(const Extension* extension, + const GURL& origin, + const ExtensionToOriginsMap& map) { + DCHECK_EQ(origin, origin.GetOrigin()); + ExtensionToOriginsMap::const_iterator it = map.find(extension->id()); + return it != map.end() && it->second.count(origin) > 0; +} + +static base::LazyInstance<ProfileKeyedAPIFactory<IncognitoConnectability> > + g_factory = LAZY_INSTANCE_INITIALIZER; + +// static +ProfileKeyedAPIFactory<IncognitoConnectability>* +IncognitoConnectability::GetFactoryInstance() { + return &g_factory.Get(); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/messaging/incognito_connectability.h b/chrome/browser/extensions/api/messaging/incognito_connectability.h new file mode 100644 index 0000000000..6b680ab115 --- /dev/null +++ b/chrome/browser/extensions/api/messaging/incognito_connectability.h @@ -0,0 +1,93 @@ +// Copyright 2013 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_EXTENSIONS_API_MESSAGING_INCOGNITO_CONNECTABILITY_H_ +#define CHROME_BROWSER_EXTENSIONS_API_MESSAGING_INCOGNITO_CONNECTABILITY_H_ + +#include <set> + +#include "chrome/browser/extensions/api/profile_keyed_api_factory.h" +#include "url/gurl.h" + +class Profile; +namespace content { +class WebContents; +} + +namespace extensions { +class Extension; + +// Tracks the web connectability of domains to extensions in incognito mode. +// +// The most important functionality is prompting the user to allow or disallow +// connections from incognito tabs to extensions or apps. Even if an extension +// hasn't been enabled in incognito mode, it's still useful for web sites to be +// able to send messages to them, with user constent. For apps, it's essential +// we have this functionality because there is no way for them to be enabled in +// incognito. +class IncognitoConnectability : public ProfileKeyedAPI { + public: + // While in scope, immediately either accepts or denies the alerts that show + // up, and counts the number of times it was invoked. + class ScopedAlertTracker { + public: + enum Mode { + INTERACTIVE, + ALWAYS_ALLOW, + ALWAYS_DENY, + }; + + explicit ScopedAlertTracker(Mode mode); + + ~ScopedAlertTracker(); + + // Returns the number of times the alert has been shown since + // GetAndResetAlertCount was last called. + int GetAndResetAlertCount(); + + private: + int last_checked_invocation_count_; + }; + + // Returns the IncognitoConnectability object for |profile|. |profile| must + // be off-the-record. + static IncognitoConnectability* Get(Profile* profile); + + // Returns true if |url| is allowed to connect from this profile, false + // otherwise. If unknown, this call will block and prompt the user. + bool Query(const Extension* extension, + content::WebContents* web_contents, + const GURL& url); + + private: + friend class ProfileKeyedAPIFactory<IncognitoConnectability>; + + explicit IncognitoConnectability(Profile* profile); + virtual ~IncognitoConnectability(); + + typedef std::map<std::string, std::set<GURL> > ExtensionToOriginsMap; + + // Returns true if the (|extension|, |origin|) pair appears in the map. + bool IsInMap(const Extension* extension, + const GURL& origin, + const ExtensionToOriginsMap& map); + + // ProfileKeyedAPI implementation. + static ProfileKeyedAPIFactory<IncognitoConnectability>* GetFactoryInstance(); + static const char* service_name() { + return "Messaging.IncognitoConnectability"; + } + static const bool kServiceHasOwnInstanceInIncognito = true; + static const bool kServiceIsCreatedWithBrowserContext = false; + + // The origins that have been prompted for and either allowed or disallowed. + // These are deliberately stored in-memory so that they're reset when the + // profile is destroyed (i.e. when the last incognito window is closed). + ExtensionToOriginsMap allowed_origins_; + ExtensionToOriginsMap disallowed_origins_; +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_MESSAGING_INCOGNITO_CONNECTABILITY_H_ diff --git a/chrome/browser/extensions/api/messaging/message_service.cc b/chrome/browser/extensions/api/messaging/message_service.cc index db57a4e2aa..754bc101b4 100644 --- a/chrome/browser/extensions/api/messaging/message_service.cc +++ b/chrome/browser/extensions/api/messaging/message_service.cc @@ -14,12 +14,14 @@ #include "base/values.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/extensions/api/messaging/extension_message_port.h" +#include "chrome/browser/extensions/api/messaging/incognito_connectability.h" #include "chrome/browser/extensions/api/messaging/native_message_port.h" #include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/extension_process_manager.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/extensions/extension_tab_util.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/lazy_background_task_queue.h" #include "chrome/browser/extensions/process_map.h" #include "chrome/browser/profiles/profile.h" @@ -27,7 +29,6 @@ #include "chrome/common/extensions/background_info.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_messages.h" -#include "chrome/common/extensions/features/simple_feature.h" #include "chrome/common/extensions/incognito_handler.h" #include "chrome/common/extensions/manifest_handlers/externally_connectable.h" #include "content/public/browser/notification_service.h" @@ -60,8 +61,10 @@ namespace extensions { const char kReceivingEndDoesntExistError[] = "Could not establish connection. Receiving end does not exist."; +#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) const char kMissingPermissionError[] = "Access to native messaging requires nativeMessaging permission."; +#endif struct MessageService::MessageChannel { scoped_ptr<MessagePort> opener; @@ -207,23 +210,7 @@ void MessageService::OpenChannelToExtension( return; } - ExtensionService* extension_service = - ExtensionSystem::Get(profile)->extension_service(); - - if (profile->IsOffTheRecord() && - !extension_service->IsIncognitoEnabled(target_extension_id)) { - // Allow the security token apps (normal, dev) to be connectable from - // incognito profiles. See http://crbug.com/295845. - std::set<std::string> incognito_whitelist; - incognito_whitelist.insert("E4FCC42F7C7776C0985996DAED74F630C4F0A785"); - incognito_whitelist.insert("D3D12919F7F00FE553E8A573AAA7147C51DD65C9"); - if (!extensions::SimpleFeature::IsIdInWhitelist(target_extension_id, - incognito_whitelist)) { - DispatchOnDisconnect( - source, receiver_port_id, kReceivingEndDoesntExistError); - return; - } - } + bool is_web_connection = false; if (source_extension_id != target_extension_id) { // It's an external connection. Check the externally_connectable manifest @@ -239,6 +226,7 @@ void MessageService::OpenChannelToExtension( if (source_extension_id.empty()) { // No source extension ID so the source was a web page. Check that the // URL matches. + is_web_connection = true; is_externally_connectable = externally_connectable->matches.MatchesURL(source_url); // Only include the TLS channel ID for externally connected web pages. @@ -266,14 +254,35 @@ void MessageService::OpenChannelToExtension( } } + ExtensionService* extension_service = + ExtensionSystem::Get(profile)->extension_service(); + WebContents* source_contents = tab_util::GetWebContentsByID( + source_process_id, source_routing_id); + + if (profile->IsOffTheRecord() && + !extension_util::IsIncognitoEnabled(target_extension_id, + extension_service)) { + // Give the user a chance to accept an incognito connection if they haven't + // already - but only for spanning-mode incognito. We don't want the + // complication of spinning up an additional process here which might need + // to do some setup that we're not expecting. + if (!is_web_connection || + IncognitoInfo::IsSplitMode(target_extension) || + !IncognitoConnectability::Get(profile)->Query(target_extension, + source_contents, + source_url)) { + DispatchOnDisconnect( + source, receiver_port_id, kReceivingEndDoesntExistError); + return; + } + } + // Note: we use the source's profile here. If the source is an incognito // process, we will use the incognito EPM to find the right extension process, // which depends on whether the extension uses spanning or split mode. MessagePort* receiver = new ExtensionMessagePort( GetExtensionProcess(profile, target_extension_id), MSG_ROUTING_CONTROL, target_extension_id); - WebContents* source_contents = tab_util::GetWebContentsByID( - source_process_id, source_routing_id); // Include info about the opener's tab (if it was a tab). scoped_ptr<base::DictionaryValue> source_tab; diff --git a/chrome/browser/extensions/api/module/module.cc b/chrome/browser/extensions/api/module/module.cc index b1295d1650..31e849c1d5 100644 --- a/chrome/browser/extensions/api/module/module.cc +++ b/chrome/browser/extensions/api/module/module.cc @@ -10,6 +10,7 @@ #include "chrome/browser/extensions/extension_prefs.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/profiles/profile.h" namespace extensions { @@ -51,7 +52,7 @@ bool ExtensionIsAllowedIncognitoAccessFunction::RunImpl() { const Extension* extension = GetExtension(); SetResult(new base::FundamentalValue( - ext_service->IsIncognitoEnabled(extension->id()))); + extension_util::IsIncognitoEnabled(extension->id(), ext_service))); return true; } @@ -61,7 +62,7 @@ bool ExtensionIsAllowedFileSchemeAccessFunction::RunImpl() { const Extension* extension = GetExtension(); SetResult(new base::FundamentalValue( - ext_service->AllowFileAccess(extension))); + extension_util::AllowFileAccess(extension, ext_service))); return true; } diff --git a/chrome/browser/extensions/api/music_manager_private/device_id_chromeos.cc b/chrome/browser/extensions/api/music_manager_private/device_id_chromeos.cc index 2e79c62925..5d392b4c3c 100644 --- a/chrome/browser/extensions/api/music_manager_private/device_id_chromeos.cc +++ b/chrome/browser/extensions/api/music_manager_private/device_id_chromeos.cc @@ -5,7 +5,7 @@ #include "chrome/browser/extensions/api/music_manager_private/device_id.h" #include "base/message_loop/message_loop.h" -#include "chromeos/cryptohome/cryptohome_library.h" +#include "chromeos/cryptohome/system_salt_getter.h" namespace extensions { namespace api { @@ -13,7 +13,7 @@ namespace api { // ChromeOS: Use the System Salt. /* static */ void DeviceId::GetMachineId(const IdCallback& callback) { - chromeos::CryptohomeLibrary* c_home = chromeos::CryptohomeLibrary::Get(); + chromeos::SystemSaltGetter* c_home = chromeos::SystemSaltGetter::Get(); std::string result = c_home->GetSystemSaltSync(); if (result.empty()) { // cryptohome must not be running; re-request after a delay. diff --git a/chrome/browser/extensions/api/networking_private/networking_private_api.h b/chrome/browser/extensions/api/networking_private/networking_private_api.h index 754219a592..08b2708936 100644 --- a/chrome/browser/extensions/api/networking_private/networking_private_api.h +++ b/chrome/browser/extensions/api/networking_private/networking_private_api.h @@ -136,6 +136,60 @@ class NetworkingPrivateGetVisibleNetworksFunction DISALLOW_COPY_AND_ASSIGN(NetworkingPrivateGetVisibleNetworksFunction); }; +// Implements the chrome.networkingPrivate.getEnabledNetworkTypes method. +class NetworkingPrivateGetEnabledNetworkTypesFunction + : public SyncExtensionFunction { + public: + NetworkingPrivateGetEnabledNetworkTypesFunction() {} + DECLARE_EXTENSION_FUNCTION("networkingPrivate.getEnabledNetworkTypes", + NETWORKINGPRIVATE_GETENABLEDNETWORKTYPES); + + protected: + virtual ~NetworkingPrivateGetEnabledNetworkTypesFunction(); + + // SyncExtensionFunction overrides. + virtual bool RunImpl() OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(NetworkingPrivateGetEnabledNetworkTypesFunction); +}; + +// Implements the chrome.networkingPrivate.enableNetworkType method. +class NetworkingPrivateEnableNetworkTypeFunction + : public SyncExtensionFunction { + public: + NetworkingPrivateEnableNetworkTypeFunction() {} + DECLARE_EXTENSION_FUNCTION("networkingPrivate.enableNetworkType", + NETWORKINGPRIVATE_ENABLENETWORKTYPE); + + protected: + virtual ~NetworkingPrivateEnableNetworkTypeFunction(); + + // SyncExtensionFunction overrides. + virtual bool RunImpl() OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(NetworkingPrivateEnableNetworkTypeFunction); +}; + +// Implements the chrome.networkingPrivate.disableNetworkType method. +class NetworkingPrivateDisableNetworkTypeFunction + : public SyncExtensionFunction { + public: + NetworkingPrivateDisableNetworkTypeFunction() {} + DECLARE_EXTENSION_FUNCTION("networkingPrivate.disableNetworkType", + NETWORKINGPRIVATE_DISABLENETWORKTYPE); + + protected: + virtual ~NetworkingPrivateDisableNetworkTypeFunction(); + + // SyncExtensionFunction overrides. + virtual bool RunImpl() OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(NetworkingPrivateDisableNetworkTypeFunction); +}; + // Implements the chrome.networkingPrivate.requestNetworkScan method. class NetworkingPrivateRequestNetworkScanFunction : public SyncExtensionFunction { diff --git a/chrome/browser/extensions/api/networking_private/networking_private_api_chromeos.cc b/chrome/browser/extensions/api/networking_private/networking_private_api_chromeos.cc index 96aa57a11e..a2f6e797fe 100644 --- a/chrome/browser/extensions/api/networking_private/networking_private_api_chromeos.cc +++ b/chrome/browser/extensions/api/networking_private/networking_private_api_chromeos.cc @@ -22,6 +22,7 @@ #include "chromeos/network/network_state_handler.h" #include "chromeos/network/onc/onc_signature.h" #include "chromeos/network/onc/onc_translator.h" +#include "chromeos/network/shill_property_util.h" #include "components/onc/onc_constants.h" namespace api = extensions::api::networking_private; @@ -31,6 +32,7 @@ using chromeos::ManagedNetworkConfigurationHandler; using chromeos::NetworkHandler; using chromeos::NetworkState; using chromeos::NetworkStateHandler; +using chromeos::NetworkTypePattern; using chromeos::ShillManagerClient; namespace { @@ -305,6 +307,108 @@ bool NetworkingPrivateGetVisibleNetworksFunction::RunImpl() { } //////////////////////////////////////////////////////////////////////////////// +// NetworkingPrivateGetEnabledNetworkTypesFunction + +NetworkingPrivateGetEnabledNetworkTypesFunction:: +~NetworkingPrivateGetEnabledNetworkTypesFunction() { +} + +bool NetworkingPrivateGetEnabledNetworkTypesFunction::RunImpl() { + NetworkStateHandler* state_handler = + NetworkHandler::Get()->network_state_handler(); + + base::ListValue* network_list = new base::ListValue; + + if (state_handler->IsTechnologyEnabled(NetworkTypePattern::Ethernet())) + network_list->AppendString("Ethernet"); + if (state_handler->IsTechnologyEnabled(NetworkTypePattern::WiFi())) + network_list->AppendString("WiFi"); + if (state_handler->IsTechnologyEnabled(NetworkTypePattern::Cellular())) + network_list->AppendString("Cellular"); + + SetResult(network_list); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// NetworkingPrivateEnableNetworkTypeFunction + +NetworkingPrivateEnableNetworkTypeFunction:: +~NetworkingPrivateEnableNetworkTypeFunction() { +} + +bool NetworkingPrivateEnableNetworkTypeFunction::RunImpl() { + scoped_ptr<api::EnableNetworkType::Params> params = + api::EnableNetworkType::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params); + NetworkStateHandler* state_handler = + NetworkHandler::Get()->network_state_handler(); + + switch (params->network_type) { + case api::NETWORK_TYPE_ETHERNET: + state_handler->SetTechnologyEnabled( + NetworkTypePattern::Ethernet(), true, + chromeos::network_handler::ErrorCallback()); + break; + + case api::NETWORK_TYPE_WIFI: + state_handler->SetTechnologyEnabled( + NetworkTypePattern::WiFi(), true, + chromeos::network_handler::ErrorCallback()); + break; + + case api::NETWORK_TYPE_CELLULAR: + state_handler->SetTechnologyEnabled( + NetworkTypePattern::Cellular(), true, + chromeos::network_handler::ErrorCallback()); + break; + + default: + break; + } + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// NetworkingPrivateDisableNetworkTypeFunction + +NetworkingPrivateDisableNetworkTypeFunction:: +~NetworkingPrivateDisableNetworkTypeFunction() { +} + +bool NetworkingPrivateDisableNetworkTypeFunction::RunImpl() { + scoped_ptr<api::DisableNetworkType::Params> params = + api::DisableNetworkType::Params::Create(*args_); + NetworkStateHandler* state_handler = + NetworkHandler::Get()->network_state_handler(); + + switch (params->network_type) { + case api::NETWORK_TYPE_ETHERNET: + state_handler->SetTechnologyEnabled( + NetworkTypePattern::Ethernet(), false, + chromeos::network_handler::ErrorCallback()); + break; + + case api::NETWORK_TYPE_WIFI: + state_handler->SetTechnologyEnabled( + NetworkTypePattern::WiFi(), false, + chromeos::network_handler::ErrorCallback()); + break; + + case api::NETWORK_TYPE_CELLULAR: + state_handler->SetTechnologyEnabled( + NetworkTypePattern::Cellular(), false, + chromeos::network_handler::ErrorCallback()); + break; + + default: + break; + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// // NetworkingPrivateRequestNetworkScanFunction NetworkingPrivateRequestNetworkScanFunction:: diff --git a/chrome/browser/extensions/api/networking_private/networking_private_api_nonchromeos.cc b/chrome/browser/extensions/api/networking_private/networking_private_api_nonchromeos.cc index 167d853cbf..467b9b746b 100644 --- a/chrome/browser/extensions/api/networking_private/networking_private_api_nonchromeos.cc +++ b/chrome/browser/extensions/api/networking_private/networking_private_api_nonchromeos.cc @@ -290,6 +290,52 @@ bool NetworkingPrivateGetVisibleNetworksFunction::RunImpl() { } //////////////////////////////////////////////////////////////////////////////// +// NetworkingPrivateGetEnabledNetworkTypesFunction + +NetworkingPrivateGetEnabledNetworkTypesFunction:: +~NetworkingPrivateGetEnabledNetworkTypesFunction() { +} + +bool NetworkingPrivateGetEnabledNetworkTypesFunction::RunImpl() { + base::ListValue* network_list = new base::ListValue; + + network_list->Append(new base::StringValue("Ethernet")); + network_list->Append(new base::StringValue("WiFi")); + network_list->Append(new base::StringValue("Cellular")); + + SetResult(network_list); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// NetworkingPrivateEnableNetworkTypeFunction + +NetworkingPrivateEnableNetworkTypeFunction:: +~NetworkingPrivateEnableNetworkTypeFunction() { +} + +bool NetworkingPrivateEnableNetworkTypeFunction::RunImpl() { + scoped_ptr<api::EnableNetworkType::Params> params = + api::EnableNetworkType::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// NetworkingPrivateDisableNetworkTypeFunction + +NetworkingPrivateDisableNetworkTypeFunction:: +~NetworkingPrivateDisableNetworkTypeFunction() { +} + +bool NetworkingPrivateDisableNetworkTypeFunction::RunImpl() { + scoped_ptr<api::DisableNetworkType::Params> params = + api::DisableNetworkType::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// // NetworkingPrivateRequestNetworkScanFunction NetworkingPrivateRequestNetworkScanFunction:: diff --git a/chrome/browser/extensions/api/notifications/notifications_api.cc b/chrome/browser/extensions/api/notifications/notifications_api.cc index 3fa8c76927..16254322b2 100644 --- a/chrome/browser/extensions/api/notifications/notifications_api.cc +++ b/chrome/browser/extensions/api/notifications/notifications_api.cc @@ -323,6 +323,9 @@ bool NotificationsApiFunction::CreateNotification( } } + if (options->is_clickable.get()) + optional_fields.clickable = *options->is_clickable; + NotificationsApiDelegate* api_delegate(new NotificationsApiDelegate( this, profile(), @@ -435,6 +438,10 @@ bool NotificationsApiFunction::UpdateNotification( } } + // Then override if it's already set. + if (options->is_clickable.get()) + notification->set_clickable(*options->is_clickable); + g_browser_process->notification_ui_manager()->Update( *notification, profile()); return true; @@ -531,11 +538,13 @@ bool NotificationsUpdateFunction::RunNotificationsApi() { return true; } + // Copy the existing notification to get a writable version of it. + Notification notification = *matched_notification; + // If we have trouble updating the notification (could be improper use of API // or some other reason), mark the function as failed, calling the callback // with false. // TODO(dewittj): Add more human-readable error strings if this fails. - Notification notification = *matched_notification; bool could_update_notification = UpdateNotification( params_->notification_id, ¶ms_->options, ¬ification); SetResult(new base::FundamentalValue(could_update_notification)); diff --git a/chrome/browser/extensions/api/permissions/permissions_api.cc b/chrome/browser/extensions/api/permissions/permissions_api.cc index 9fc653685c..cf23aa049b 100644 --- a/chrome/browser/extensions/api/permissions/permissions_api.cc +++ b/chrome/browser/extensions/api/permissions/permissions_api.cc @@ -14,6 +14,7 @@ #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/permissions/permissions_data.h" #include "extensions/common/error_utils.h" +#include "extensions/common/permissions/permission_message_provider.h" #include "extensions/common/permissions/permissions_info.h" #include "extensions/common/url_pattern_set.h" #include "url/gurl.h" @@ -205,8 +206,9 @@ bool PermissionsRequestFunction::RunImpl() { // We don't need to show the prompt if there are no new warnings, or if // we're skipping the confirmation UI. All extension types but INTERNAL // are allowed to silently increase their permission level. - bool has_no_warnings = requested_permissions_->GetWarningMessages( - GetExtension()->GetType()).empty(); + bool has_no_warnings = + PermissionMessageProvider::Get()->GetWarningMessages( + requested_permissions_, GetExtension()->GetType()).empty(); if (auto_confirm_for_tests == PROCEED || has_no_warnings || extension_->location() == Manifest::COMPONENT) { InstallUIProceed(); diff --git a/chrome/browser/extensions/api/preference/preference_apitest.cc b/chrome/browser/extensions/api/preference/preference_apitest.cc index bbb4cab5d8..6e0bcb3880 100644 --- a/chrome/browser/extensions/api/preference/preference_apitest.cc +++ b/chrome/browser/extensions/api/preference/preference_apitest.cc @@ -111,7 +111,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionPreferenceApiTest, MAYBE_Standard) { CheckPreferencesSet(); // The settings should not be reset when the extension is reloaded. - ReloadExtension(last_loaded_extension_id_); + ReloadExtension(last_loaded_extension_id()); CheckPreferencesSet(); // Uninstalling and installing the extension (without running the test that @@ -119,7 +119,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionPreferenceApiTest, MAYBE_Standard) { content::WindowedNotificationObserver observer( chrome::NOTIFICATION_EXTENSION_UNINSTALLED, content::NotificationService::AllSources()); - UninstallExtension(last_loaded_extension_id_); + UninstallExtension(last_loaded_extension_id()); observer.Wait(); CheckPreferencesCleared(); diff --git a/chrome/browser/extensions/api/preference/preference_helpers.cc b/chrome/browser/extensions/api/preference/preference_helpers.cc index 8649f8eb04..162e2ae862 100644 --- a/chrome/browser/extensions/api/preference/preference_helpers.cc +++ b/chrome/browser/extensions/api/preference/preference_helpers.cc @@ -12,6 +12,7 @@ #include "chrome/browser/extensions/extension_prefs.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/extensions/incognito_handler.h" @@ -102,7 +103,7 @@ void DispatchEventToExtensions( if (router->ExtensionHasEventListener(extension_id, event_name) && (*it)->HasAPIPermission(permission) && (!incognito || IncognitoInfo::IsSplitMode(it->get()) || - extension_service->CanCrossIncognito(it->get()))) { + extension_util::CanCrossIncognito(it->get(), extension_service))) { // Inject level of control key-value. base::DictionaryValue* dict; bool rv = args->GetDictionary(0, &dict); @@ -118,7 +119,9 @@ void DispatchEventToExtensions( Profile* restrict_to_profile = NULL; bool from_incognito = false; if (IncognitoInfo::IsSplitMode(it->get())) { - if (incognito && extension_service->IsIncognitoEnabled(extension_id)) { + if (incognito && + extension_util::IsIncognitoEnabled(extension_id, + extension_service)) { restrict_to_profile = profile->GetOffTheRecordProfile(); } else if (!incognito && PreferenceAPI::Get(profile)->DoesExtensionControlPref( diff --git a/chrome/browser/extensions/api/processes/processes_apitest.cc b/chrome/browser/extensions/api/processes/processes_apitest.cc index f6e1f0a2d7..db840e1346 100644 --- a/chrome/browser/extensions/api/processes/processes_apitest.cc +++ b/chrome/browser/extensions/api/processes/processes_apitest.cc @@ -50,7 +50,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ProcessesVsTaskManager) { EXPECT_EQ(TaskManagerModel::TASK_PENDING, model->update_state_); // Unload the extension and check that listener count decreases - UnloadExtension(last_loaded_extension_id_); + UnloadExtension(last_loaded_extension_id()); EXPECT_EQ(1, model->update_requests_); } diff --git a/chrome/browser/extensions/api/socket/tcp_socket.cc b/chrome/browser/extensions/api/socket/tcp_socket.cc index 8401dc2674..4f9273468a 100644 --- a/chrome/browser/extensions/api/socket/tcp_socket.cc +++ b/chrome/browser/extensions/api/socket/tcp_socket.cc @@ -28,6 +28,17 @@ ApiResourceManager<ResumableTCPSocket>::GetFactoryInstance() { return &g_factory.Get(); } +static base::LazyInstance<ProfileKeyedAPIFactory< + ApiResourceManager<ResumableTCPServerSocket> > > + g_server_factory = LAZY_INSTANCE_INITIALIZER; + +// static +template <> +ProfileKeyedAPIFactory<ApiResourceManager<ResumableTCPServerSocket> >* +ApiResourceManager<ResumableTCPServerSocket>::GetFactoryInstance() { + return &g_server_factory.Get(); +} + TCPSocket::TCPSocket(const std::string& owner_extension_id) : Socket(owner_extension_id), socket_mode_(UNKNOWN) { @@ -109,6 +120,10 @@ void TCPSocket::Disconnect() { if (socket_.get()) socket_->Disconnect(); server_socket_.reset(NULL); + connect_callback_.Reset(); + read_callback_.Reset(); + accept_callback_.Reset(); + accept_socket_.reset(NULL); } int TCPSocket::Bind(const std::string& address, int port) { @@ -127,28 +142,23 @@ void TCPSocket::Read(int count, if (!read_callback_.is_null()) { callback.Run(net::ERR_IO_PENDING, NULL); return; - } else { - read_callback_ = callback; } - int result = net::ERR_FAILED; - scoped_refptr<net::IOBuffer> io_buffer; - do { - if (count < 0) { - result = net::ERR_INVALID_ARGUMENT; - break; - } + if (count < 0) { + callback.Run(net::ERR_INVALID_ARGUMENT, NULL); + return; + } - if (!socket_.get() || !IsConnected()) { - result = net::ERR_SOCKET_NOT_CONNECTED; - break; - } + if (!socket_.get() || !IsConnected()) { + callback.Run(net::ERR_SOCKET_NOT_CONNECTED, NULL); + return; + } - io_buffer = new net::IOBuffer(count); - result = socket_->Read(io_buffer.get(), count, - base::Bind(&TCPSocket::OnReadComplete, base::Unretained(this), - io_buffer)); - } while (false); + read_callback_ = callback; + scoped_refptr<net::IOBuffer> io_buffer = new net::IOBuffer(count); + int result = socket_->Read(io_buffer.get(), count, + base::Bind(&TCPSocket::OnReadComplete, base::Unretained(this), + io_buffer)); if (result != net::ERR_IO_PENDING) OnReadComplete(io_buffer, result); @@ -266,8 +276,7 @@ void TCPSocket::RefreshConnectionStatus() { if (!is_connected_) return; if (server_socket_) return; if (!socket_->IsConnected()) { - is_connected_ = false; - socket_->Disconnect(); + Disconnect(); } } @@ -304,8 +313,28 @@ ResumableTCPSocket::ResumableTCPSocket(const std::string& owner_extension_id) paused_(false) { } -bool ResumableTCPSocket::persistent() const { - return persistent_; +ResumableTCPSocket::ResumableTCPSocket(net::TCPClientSocket* tcp_client_socket, + const std::string& owner_extension_id, + bool is_connected) + : TCPSocket(tcp_client_socket, owner_extension_id, is_connected), + persistent_(false), + buffer_size_(0), + paused_(false) { +} + +bool ResumableTCPSocket::IsPersistent() const { + return persistent(); +} + +ResumableTCPServerSocket::ResumableTCPServerSocket( + const std::string& owner_extension_id) + : TCPSocket(owner_extension_id), + persistent_(false), + paused_(false) { +} + +bool ResumableTCPServerSocket::IsPersistent() const { + return persistent(); } } // namespace extensions diff --git a/chrome/browser/extensions/api/socket/tcp_socket.h b/chrome/browser/extensions/api/socket/tcp_socket.h index c6848243af..6cda66c0ad 100644 --- a/chrome/browser/extensions/api/socket/tcp_socket.h +++ b/chrome/browser/extensions/api/socket/tcp_socket.h @@ -102,11 +102,17 @@ class TCPSocket : public Socket { class ResumableTCPSocket : public TCPSocket { public: explicit ResumableTCPSocket(const std::string& owner_extension_id); + explicit ResumableTCPSocket(net::TCPClientSocket* tcp_client_socket, + const std::string& owner_extension_id, + bool is_connected); + + // Overriden from ApiResource + virtual bool IsPersistent() const OVERRIDE; const std::string& name() const { return name_; } void set_name(const std::string& name) { name_ = name; } - virtual bool persistent() const OVERRIDE; + bool persistent() const { return persistent_; } void set_persistent(bool persistent) { persistent_ = persistent; } int buffer_size() const { return buffer_size_; } @@ -124,7 +130,7 @@ class ResumableTCPSocket : public TCPSocket { // Application-defined string - see sockets_tcp.idl. std::string name_; // Flag indicating whether the socket is left open when the application is - // suspended - see sockets_tcp.idl.. + // suspended - see sockets_tcp.idl. bool persistent_; // The size of the buffer used to receive data - see sockets_tcp.idl. int buffer_size_; @@ -133,6 +139,41 @@ class ResumableTCPSocket : public TCPSocket { bool paused_; }; +// TCP Socket instances from the "sockets.tcpServer" namespace. These are +// regular socket objects with additional properties related to the behavior +// defined in the "sockets.tcpServer" namespace. +class ResumableTCPServerSocket : public TCPSocket { + public: + explicit ResumableTCPServerSocket(const std::string& owner_extension_id); + + // Overriden from ApiResource + virtual bool IsPersistent() const OVERRIDE; + + const std::string& name() const { return name_; } + void set_name(const std::string& name) { name_ = name; } + + bool persistent() const { return persistent_; } + void set_persistent(bool persistent) { persistent_ = persistent; } + + bool paused() const { return paused_; } + void set_paused(bool paused) { paused_ = paused; } + + private: + friend class ApiResourceManager<ResumableTCPServerSocket>; + static const char* service_name() { + return "ResumableTCPServerSocketManager"; + } + + // Application-defined string - see sockets_tcp_server.idl. + std::string name_; + // Flag indicating whether the socket is left open when the application is + // suspended - see sockets_tcp_server.idl. + bool persistent_; + // Flag indicating whether a connected socket blocks its peer from sending + // more data - see sockets_tcp_server.idl. + bool paused_; +}; + } // namespace extensions #endif // CHROME_BROWSER_EXTENSIONS_API_SOCKET_TCP_SOCKET_H_ diff --git a/chrome/browser/extensions/api/socket/udp_socket.cc b/chrome/browser/extensions/api/socket/udp_socket.cc index 7d36ffcece..b6be256a74 100644 --- a/chrome/browser/extensions/api/socket/udp_socket.cc +++ b/chrome/browser/extensions/api/socket/udp_socket.cc @@ -71,6 +71,10 @@ int UDPSocket::Bind(const std::string& address, int port) { void UDPSocket::Disconnect() { is_connected_ = false; socket_.Close(); + read_callback_.Reset(); + recv_from_callback_.Reset(); + send_to_callback_.Reset(); + multicast_groups_.clear(); } void UDPSocket::Read(int count, @@ -295,28 +299,8 @@ ResumableUDPSocket::ResumableUDPSocket(const std::string& owner_extension_id) buffer_size_(0) { } -const std::string& ResumableUDPSocket::name() const { - return name_; -} - -void ResumableUDPSocket::set_name(const std::string& name) { - name_ = name; -} - -bool ResumableUDPSocket::persistent() const { - return persistent_; -} - -void ResumableUDPSocket::set_persistent(bool persistent) { - persistent_ = persistent; -} - -int ResumableUDPSocket::buffer_size() const { - return buffer_size_; -} - -void ResumableUDPSocket::set_buffer_size(int buffer_size) { - buffer_size_ = buffer_size; +bool ResumableUDPSocket::IsPersistent() const { + return persistent(); } } // namespace extensions diff --git a/chrome/browser/extensions/api/socket/udp_socket.h b/chrome/browser/extensions/api/socket/udp_socket.h index 4989e27092..e3dab20a18 100644 --- a/chrome/browser/extensions/api/socket/udp_socket.h +++ b/chrome/browser/extensions/api/socket/udp_socket.h @@ -81,14 +81,17 @@ class ResumableUDPSocket : public UDPSocket { public: explicit ResumableUDPSocket(const std::string& owner_extension_id); - const std::string& name() const; - void set_name(const std::string& name); + // Overriden from ApiResource + virtual bool IsPersistent() const OVERRIDE; - virtual bool persistent() const OVERRIDE; - void set_persistent(bool persistent); + const std::string& name() const { return name_; } + void set_name(const std::string& name) { name_ = name; } - int buffer_size() const; - void set_buffer_size(int buffer_size); + bool persistent() const { return persistent_; } + void set_persistent(bool persistent) { persistent_ = persistent; } + + int buffer_size() const { return buffer_size_; } + void set_buffer_size(int buffer_size) { buffer_size_ = buffer_size; } private: friend class ApiResourceManager<ResumableUDPSocket>; @@ -96,8 +99,12 @@ class ResumableUDPSocket : public UDPSocket { return "ResumableUDPSocketManager"; } + // Application-defined string - see sockets_udp.idl. std::string name_; + // Flag indicating whether the socket is left open when the application is + // suspended - see sockets_udp.idl. bool persistent_; + // The size of the buffer used to receive data - see sockets_udp.idl. int buffer_size_; }; diff --git a/chrome/browser/extensions/api/sockets_tcp/tcp_socket_event_dispatcher.cc b/chrome/browser/extensions/api/sockets_tcp/tcp_socket_event_dispatcher.cc index fe674ae497..40d8c22782 100644 --- a/chrome/browser/extensions/api/sockets_tcp/tcp_socket_event_dispatcher.cc +++ b/chrome/browser/extensions/api/sockets_tcp/tcp_socket_event_dispatcher.cc @@ -116,10 +116,15 @@ void TCPSocketEventDispatcher::ReadCallback( scoped_refptr<net::IOBuffer> io_buffer) { DCHECK(BrowserThread::CurrentlyOn(params.thread_id)); - // Note: if "bytes_read" < 0, there was a network error, and "bytes_read" is - // a value from "net::ERR_". + // If |bytes_read| == 0, the connection has been closed by the peer. + // If |bytes_read| < 0, there was a network error, and |bytes_read| is a value + // from "net::ERR_". - if (bytes_read >= 0) { + if (bytes_read == 0) { + bytes_read = net::ERR_CONNECTION_CLOSED; + } + + if (bytes_read > 0) { // Dispatch "onReceive" event. sockets_tcp::ReceiveInfo receive_info; receive_info.socket_id = params.socket_id; diff --git a/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.cc b/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.cc new file mode 100644 index 0000000000..0827e29bed --- /dev/null +++ b/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.cc @@ -0,0 +1,298 @@ +// Copyright 2013 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/extensions/api/sockets_tcp_server/sockets_tcp_server_api.h" + +#include "chrome/browser/extensions/api/socket/tcp_socket.h" +#include "chrome/browser/extensions/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h" +#include "chrome/common/extensions/api/sockets/sockets_handler.h" +#include "chrome/common/extensions/permissions/permissions_data.h" +#include "chrome/common/extensions/permissions/socket_permission.h" +#include "content/public/common/socket_permission_request.h" +#include "net/base/net_errors.h" + +using content::SocketPermissionRequest; +using extensions::ResumableTCPServerSocket; +using extensions::api::sockets_tcp_server::SocketInfo; +using extensions::api::sockets_tcp_server::SocketProperties; + +namespace { + +const char kSocketNotFoundError[] = "Socket not found"; +const char kPermissionError[] = "Does not have permission"; +const int kDefaultListenBacklog = SOMAXCONN; + +linked_ptr<SocketInfo> CreateSocketInfo(int socket_id, + ResumableTCPServerSocket* socket) { + linked_ptr<SocketInfo> socket_info(new SocketInfo()); + // This represents what we know about the socket, and does not call through + // to the system. + socket_info->socket_id = socket_id; + if (!socket->name().empty()) { + socket_info->name.reset(new std::string(socket->name())); + } + socket_info->persistent = socket->persistent(); + socket_info->paused = socket->paused(); + + // Grab the local address as known by the OS. + net::IPEndPoint localAddress; + if (socket->GetLocalAddress(&localAddress)) { + socket_info->local_address.reset( + new std::string(localAddress.ToStringWithoutPort())); + socket_info->local_port.reset(new int(localAddress.port())); + } + + return socket_info; +} + +void SetSocketProperties(ResumableTCPServerSocket* socket, + SocketProperties* properties) { + if (properties->name.get()) { + socket->set_name(*properties->name.get()); + } + if (properties->persistent.get()) { + socket->set_persistent(*properties->persistent.get()); + } +} + +} // namespace + +namespace extensions { +namespace api { + +TCPServerSocketAsyncApiFunction::~TCPServerSocketAsyncApiFunction() {} + +scoped_ptr<SocketResourceManagerInterface> + TCPServerSocketAsyncApiFunction::CreateSocketResourceManager() { + return scoped_ptr<SocketResourceManagerInterface>( + new SocketResourceManager<ResumableTCPServerSocket>()).Pass(); +} + +ResumableTCPServerSocket* TCPServerSocketAsyncApiFunction::GetTcpSocket( + int socket_id) { + return static_cast<ResumableTCPServerSocket*>(GetSocket(socket_id)); +} + +SocketsTcpServerCreateFunction::SocketsTcpServerCreateFunction() {} + +SocketsTcpServerCreateFunction::~SocketsTcpServerCreateFunction() {} + +bool SocketsTcpServerCreateFunction::Prepare() { + params_ = sockets_tcp_server::Create::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsTcpServerCreateFunction::Work() { + ResumableTCPServerSocket* socket = + new ResumableTCPServerSocket(extension_->id()); + + sockets_tcp_server::SocketProperties* properties = + params_.get()->properties.get(); + if (properties) { + SetSocketProperties(socket, properties); + } + + sockets_tcp_server::CreateInfo create_info; + create_info.socket_id = AddSocket(socket); + results_ = sockets_tcp_server::Create::Results::Create(create_info); +} + +SocketsTcpServerUpdateFunction::SocketsTcpServerUpdateFunction() {} + +SocketsTcpServerUpdateFunction::~SocketsTcpServerUpdateFunction() {} + +bool SocketsTcpServerUpdateFunction::Prepare() { + params_ = sockets_tcp_server::Update::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsTcpServerUpdateFunction::Work() { + ResumableTCPServerSocket* socket = GetTcpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + SetSocketProperties(socket, ¶ms_.get()->properties); + results_ = sockets_tcp_server::Update::Results::Create(); +} + +SocketsTcpServerSetPausedFunction::SocketsTcpServerSetPausedFunction() + : socket_event_dispatcher_(NULL) {} + +SocketsTcpServerSetPausedFunction::~SocketsTcpServerSetPausedFunction() {} + +bool SocketsTcpServerSetPausedFunction::Prepare() { + params_ = api::sockets_tcp_server::SetPaused::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + + socket_event_dispatcher_ = TCPServerSocketEventDispatcher::Get(profile()); + DCHECK(socket_event_dispatcher_) << "There is no socket event dispatcher. " + "If this assertion is failing during a test, then it is likely that " + "TestExtensionSystem is failing to provide an instance of " + "TCPServerSocketEventDispatcher."; + return socket_event_dispatcher_ != NULL; +} + +void SocketsTcpServerSetPausedFunction::Work() { + ResumableTCPServerSocket* socket = GetTcpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + if (socket->paused() != params_->paused) { + socket->set_paused(params_->paused); + if (socket->IsConnected() && !params_->paused) { + socket_event_dispatcher_->OnServerSocketResume(extension_->id(), + params_->socket_id); + } + } + + results_ = sockets_tcp_server::SetPaused::Results::Create(); +} + +SocketsTcpServerListenFunction::SocketsTcpServerListenFunction() + : socket_event_dispatcher_(NULL) {} + +SocketsTcpServerListenFunction::~SocketsTcpServerListenFunction() {} + +bool SocketsTcpServerListenFunction::Prepare() { + params_ = api::sockets_tcp_server::Listen::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + + socket_event_dispatcher_ = TCPServerSocketEventDispatcher::Get(profile()); + DCHECK(socket_event_dispatcher_) << "There is no socket event dispatcher. " + "If this assertion is failing during a test, then it is likely that " + "TestExtensionSystem is failing to provide an instance of " + "TCPServerSocketEventDispatcher."; + return socket_event_dispatcher_ != NULL; +} + +void SocketsTcpServerListenFunction::Work() { + ResumableTCPServerSocket* socket = GetTcpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + SocketPermissionRequest param( + SocketPermissionRequest::TCP_LISTEN, + params_->address, + params_->port); + if (!SocketsManifestData::CheckRequest(GetExtension(), param)) { + error_ = kPermissionError; + return; + } + + int net_result = socket->Listen( + params_->address, + params_->port, + params_->backlog.get() ? *params_->backlog.get() : kDefaultListenBacklog, + &error_); + + if (net_result != net::OK) + error_ = net::ErrorToString(net_result); + + + if (net_result == net::OK) { + socket_event_dispatcher_->OnServerSocketListen(extension_->id(), + params_->socket_id); + } + + results_ = sockets_tcp_server::Listen::Results::Create(net_result); +} + +SocketsTcpServerDisconnectFunction::SocketsTcpServerDisconnectFunction() {} + +SocketsTcpServerDisconnectFunction::~SocketsTcpServerDisconnectFunction() {} + +bool SocketsTcpServerDisconnectFunction::Prepare() { + params_ = sockets_tcp_server::Disconnect::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsTcpServerDisconnectFunction::Work() { + ResumableTCPServerSocket* socket = GetTcpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + socket->Disconnect(); + results_ = sockets_tcp_server::Disconnect::Results::Create(); +} + +SocketsTcpServerCloseFunction::SocketsTcpServerCloseFunction() {} + +SocketsTcpServerCloseFunction::~SocketsTcpServerCloseFunction() {} + +bool SocketsTcpServerCloseFunction::Prepare() { + params_ = sockets_tcp_server::Close::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsTcpServerCloseFunction::Work() { + ResumableTCPServerSocket* socket = GetTcpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + RemoveSocket(params_->socket_id); + results_ = sockets_tcp_server::Close::Results::Create(); +} + +SocketsTcpServerGetInfoFunction::SocketsTcpServerGetInfoFunction() {} + +SocketsTcpServerGetInfoFunction::~SocketsTcpServerGetInfoFunction() {} + +bool SocketsTcpServerGetInfoFunction::Prepare() { + params_ = sockets_tcp_server::GetInfo::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsTcpServerGetInfoFunction::Work() { + ResumableTCPServerSocket* socket = GetTcpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + linked_ptr<sockets_tcp_server::SocketInfo> socket_info = + CreateSocketInfo(params_->socket_id, socket); + results_ = sockets_tcp_server::GetInfo::Results::Create(*socket_info); +} + +SocketsTcpServerGetSocketsFunction::SocketsTcpServerGetSocketsFunction() {} + +SocketsTcpServerGetSocketsFunction::~SocketsTcpServerGetSocketsFunction() {} + +bool SocketsTcpServerGetSocketsFunction::Prepare() { + return true; +} + +void SocketsTcpServerGetSocketsFunction::Work() { + std::vector<linked_ptr<sockets_tcp_server::SocketInfo> > socket_infos; + base::hash_set<int>* resource_ids = GetSocketIds(); + if (resource_ids != NULL) { + for (base::hash_set<int>::iterator it = resource_ids->begin(); + it != resource_ids->end(); ++it) { + int socket_id = *it; + ResumableTCPServerSocket* socket = GetTcpSocket(socket_id); + if (socket) { + socket_infos.push_back(CreateSocketInfo(socket_id, socket)); + } + } + } + results_ = sockets_tcp_server::GetSockets::Results::Create(socket_infos); +} + +} // namespace api +} // namespace extensions diff --git a/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.h b/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.h new file mode 100644 index 0000000000..439b85714c --- /dev/null +++ b/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.h @@ -0,0 +1,179 @@ +// Copyright 2013 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_EXTENSIONS_API_SOCKETS_TCP_SERVER_SOCKETS_TCP_SERVER_API_H_ +#define CHROME_BROWSER_EXTENSIONS_API_SOCKETS_TCP_SERVER_SOCKETS_TCP_SERVER_API_H_ + +#include "chrome/browser/extensions/api/socket/socket_api.h" +#include "chrome/common/extensions/api/sockets_tcp_server.h" + +namespace extensions { +class ResumableTCPServerSocket; +} + +namespace extensions { +namespace api { + +class TCPServerSocketAsyncApiFunction : public SocketAsyncApiFunction { + protected: + virtual ~TCPServerSocketAsyncApiFunction(); + + virtual scoped_ptr<SocketResourceManagerInterface> + CreateSocketResourceManager() OVERRIDE; + + ResumableTCPServerSocket* GetTcpSocket(int socket_id); +}; + +class SocketsTcpServerCreateFunction : public TCPServerSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcpServer.create", + SOCKETS_TCP_SERVER_CREATE) + + SocketsTcpServerCreateFunction(); + + protected: + virtual ~SocketsTcpServerCreateFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + FRIEND_TEST_ALL_PREFIXES(SocketsTcpServerUnitTest, Create); + scoped_ptr<sockets_tcp_server::Create::Params> params_; +}; + +class SocketsTcpServerUpdateFunction : public TCPServerSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcpServer.update", + SOCKETS_TCP_SERVER_UPDATE) + + SocketsTcpServerUpdateFunction(); + + protected: + virtual ~SocketsTcpServerUpdateFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_tcp_server::Update::Params> params_; +}; + +class SocketsTcpServerSetPausedFunction + : public TCPServerSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcpServer.setPaused", + SOCKETS_TCP_SERVER_SETPAUSED) + + SocketsTcpServerSetPausedFunction(); + + protected: + virtual ~SocketsTcpServerSetPausedFunction(); + + // AsyncApiFunction + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_tcp_server::SetPaused::Params> params_; + TCPServerSocketEventDispatcher* socket_event_dispatcher_; +}; + +class SocketsTcpServerListenFunction + : public TCPServerSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcpServer.listen", + SOCKETS_TCP_SERVER_LISTEN) + + SocketsTcpServerListenFunction(); + + protected: + virtual ~SocketsTcpServerListenFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_tcp_server::Listen::Params> params_; + TCPServerSocketEventDispatcher* socket_event_dispatcher_; +}; + +class SocketsTcpServerDisconnectFunction + : public TCPServerSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcpServer.disconnect", + SOCKETS_TCP_SERVER_DISCONNECT) + + SocketsTcpServerDisconnectFunction(); + + protected: + virtual ~SocketsTcpServerDisconnectFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_tcp_server::Disconnect::Params> params_; +}; + +class SocketsTcpServerCloseFunction : public TCPServerSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcpServer.close", + SOCKETS_TCP_SERVER_CLOSE) + + SocketsTcpServerCloseFunction(); + + protected: + virtual ~SocketsTcpServerCloseFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_tcp_server::Close::Params> params_; +}; + +class SocketsTcpServerGetInfoFunction : public TCPServerSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcpServer.getInfo", + SOCKETS_TCP_SERVER_GETINFO) + + SocketsTcpServerGetInfoFunction(); + + protected: + virtual ~SocketsTcpServerGetInfoFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_tcp_server::GetInfo::Params> params_; +}; + +class SocketsTcpServerGetSocketsFunction + : public TCPServerSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcpServer.getSockets", + SOCKETS_TCP_SERVER_GETSOCKETS) + + SocketsTcpServerGetSocketsFunction(); + + protected: + virtual ~SocketsTcpServerGetSocketsFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; +}; + +} // namespace api +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_SOCKETS_TCP_SERVER_SOCKETS_TCP_SERVER_API_H_ diff --git a/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api_unittest.cc b/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api_unittest.cc new file mode 100644 index 0000000000..6ed0f9e08e --- /dev/null +++ b/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api_unittest.cc @@ -0,0 +1,94 @@ +// Copyright 2013 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/values.h" +#include "chrome/browser/browser_process_impl.h" +#include "chrome/browser/extensions/api/api_function.h" +#include "chrome/browser/extensions/api/api_resource_manager.h" +#include "chrome/browser/extensions/api/socket/socket.h" +#include "chrome/browser/extensions/api/socket/tcp_socket.h" +#include "chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.h" +#include "chrome/browser/extensions/extension_function_test_utils.h" +#include "chrome/browser/extensions/test_extension_system.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/test/base/browser_with_test_window_test.h" +#include "chrome/test/base/testing_browser_process.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace utils = extension_function_test_utils; + +namespace extensions { +namespace api { + +static +BrowserContextKeyedService* ApiResourceManagerTestFactory( + content::BrowserContext* profile) { + content::BrowserThread::ID id; + CHECK(content::BrowserThread::GetCurrentThreadIdentifier(&id)); + return ApiResourceManager<ResumableTCPSocket>:: + CreateApiResourceManagerForTest(static_cast<Profile*>(profile), id); +} + +static +BrowserContextKeyedService* ApiResourceManagerTestServerFactory( + content::BrowserContext* profile) { + content::BrowserThread::ID id; + CHECK(content::BrowserThread::GetCurrentThreadIdentifier(&id)); + return ApiResourceManager<ResumableTCPServerSocket>:: + CreateApiResourceManagerForTest(static_cast<Profile*>(profile), id); +} + +class SocketsTcpServerUnitTest : public BrowserWithTestWindowTest { + public: + virtual void SetUp() { + BrowserWithTestWindowTest::SetUp(); + + ApiResourceManager<ResumableTCPSocket>::GetFactoryInstance()-> + SetTestingFactoryAndUse(browser()->profile(), + ApiResourceManagerTestFactory); + + ApiResourceManager<ResumableTCPServerSocket>::GetFactoryInstance()-> + SetTestingFactoryAndUse(browser()->profile(), + ApiResourceManagerTestServerFactory); + + extension_ = utils::CreateEmptyExtensionWithLocation( + extensions::Manifest::UNPACKED); + } + + base::Value* RunFunctionWithExtension( + UIThreadExtensionFunction* function, const std::string& args) { + scoped_refptr<UIThreadExtensionFunction> delete_function(function); + function->set_extension(extension_.get()); + return utils::RunFunctionAndReturnSingleResult(function, args, browser()); + } + + base::DictionaryValue* RunFunctionAndReturnDict( + UIThreadExtensionFunction* function, const std::string& args) { + base::Value* result = RunFunctionWithExtension(function, args); + return result ? utils::ToDictionary(result) : NULL; + } + + protected: + scoped_refptr<extensions::Extension> extension_; +}; + +TEST_F(SocketsTcpServerUnitTest, Create) { + // Get BrowserThread + content::BrowserThread::ID id; + CHECK(content::BrowserThread::GetCurrentThreadIdentifier(&id)); + + // Create SocketCreateFunction and put it on BrowserThread + SocketsTcpServerCreateFunction *function = + new SocketsTcpServerCreateFunction(); + function->set_work_thread_id(id); + + // Run tests + scoped_ptr<base::DictionaryValue> result(RunFunctionAndReturnDict( + function, "[{\"persistent\": true, \"name\": \"foo\"}]")); + ASSERT_TRUE(result.get()); +} + +} // namespace api +} // namespace extensions diff --git a/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_apitest.cc b/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_apitest.cc new file mode 100644 index 0000000000..6e36a8cacf --- /dev/null +++ b/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_apitest.cc @@ -0,0 +1,111 @@ +// Copyright 2013 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/memory/ref_counted.h" +#include "base/path_service.h" +#include "base/strings/stringprintf.h" +#include "chrome/browser/extensions/api/dns/host_resolver_wrapper.h" +#include "chrome/browser/extensions/api/dns/mock_host_resolver_creator.h" +#include "chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.h" +#include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/browser/extensions/extension_function_test_utils.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_test_message_listener.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/extensions/application_launch.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/spawned_test_server/spawned_test_server.h" + +using extensions::Extension; +using extensions::api::SocketsTcpServerCreateFunction; + +namespace utils = extension_function_test_utils; + +namespace { + +// TODO(jschuh): Hanging plugin tests. crbug.com/244653 +#if defined(OS_WIN) && defined(ARCH_CPU_X86_64) +#define MAYBE(x) DISABLED_##x +#else +#define MAYBE(x) x +#endif + +const std::string kHostname = "127.0.0.1"; +const int kPort = 8888; + +class SocketsTcpServerApiTest : public ExtensionApiTest { + public: + SocketsTcpServerApiTest() : resolver_event_(true, false), + resolver_creator_( + new extensions::MockHostResolverCreator()) { + } + + virtual void SetUpOnMainThread() OVERRIDE { + extensions::HostResolverWrapper::GetInstance()->SetHostResolverForTesting( + resolver_creator_->CreateMockHostResolver()); + } + + virtual void CleanUpOnMainThread() OVERRIDE { + extensions::HostResolverWrapper::GetInstance()-> + SetHostResolverForTesting(NULL); + resolver_creator_->DeleteMockHostResolver(); + } + + private: + base::WaitableEvent resolver_event_; + + // The MockHostResolver asserts that it's used on the same thread on which + // it's created, which is actually a stronger rule than its real counterpart. + // But that's fine; it's good practice. + scoped_refptr<extensions::MockHostResolverCreator> resolver_creator_; +}; + +} // namespace + +IN_PROC_BROWSER_TEST_F(SocketsTcpServerApiTest, SocketTCPCreateGood) { + scoped_refptr<SocketsTcpServerCreateFunction> + socket_create_function(new SocketsTcpServerCreateFunction()); + scoped_refptr<Extension> empty_extension(utils::CreateEmptyExtension()); + + socket_create_function->set_extension(empty_extension.get()); + socket_create_function->set_has_callback(true); + + scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult( + socket_create_function.get(), "[]", browser(), utils::NONE)); + ASSERT_EQ(base::Value::TYPE_DICTIONARY, result->GetType()); + base::DictionaryValue *value = + static_cast<base::DictionaryValue*>(result.get()); + int socketId = -1; + EXPECT_TRUE(value->GetInteger("socketId", &socketId)); + ASSERT_TRUE(socketId > 0); +} + +IN_PROC_BROWSER_TEST_F(SocketsTcpServerApiTest, SocketTCPServerExtension) { + base::FilePath path = test_data_dir_.AppendASCII("sockets_tcp_server/api"); + ResultCatcher catcher; + catcher.RestrictToProfile(browser()->profile()); + ExtensionTestMessageListener listener("info_please", true); + ASSERT_TRUE(LoadExtension(path)); + EXPECT_TRUE(listener.WaitUntilSatisfied()); + listener.Reply( + base::StringPrintf("tcp_server:%s:%d", kHostname.c_str(), kPort)); + + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); +} + +IN_PROC_BROWSER_TEST_F(SocketsTcpServerApiTest, SocketTCPServerUnbindOnUnload) { + base::FilePath path = test_data_dir_.AppendASCII("sockets_tcp_server/unload"); + ResultCatcher catcher; + const Extension* extension = LoadExtension(path); + ASSERT_TRUE(extension); + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); + + UnloadExtension(extension->id()); + + ASSERT_TRUE(LoadExtension(path)) << message_; + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); +} diff --git a/chrome/browser/extensions/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.cc b/chrome/browser/extensions/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.cc new file mode 100644 index 0000000000..d849e8c0e2 --- /dev/null +++ b/chrome/browser/extensions/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.cc @@ -0,0 +1,198 @@ +// Copyright 2013 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/extensions/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h" + +#include "chrome/browser/browser_process.h" +#include "chrome/browser/extensions/api/socket/tcp_socket.h" +#include "chrome/browser/extensions/event_router.h" +#include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "net/base/net_errors.h" + +namespace extensions { +namespace api { + +using content::BrowserThread; + +static + base::LazyInstance<ProfileKeyedAPIFactory<TCPServerSocketEventDispatcher> > + g_factory = LAZY_INSTANCE_INITIALIZER; + +// static +ProfileKeyedAPIFactory<TCPServerSocketEventDispatcher>* + TCPServerSocketEventDispatcher::GetFactoryInstance() { + return &g_factory.Get(); +} + +// static +TCPServerSocketEventDispatcher* TCPServerSocketEventDispatcher::Get( + Profile* profile) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + return ProfileKeyedAPIFactory<TCPServerSocketEventDispatcher>::GetForProfile( + profile); +} + +TCPServerSocketEventDispatcher::TCPServerSocketEventDispatcher(Profile* profile) + : thread_id_(Socket::kThreadId), + profile_(profile) { + ApiResourceManager<ResumableTCPServerSocket>* server_manager = + ApiResourceManager<ResumableTCPServerSocket>::Get(profile); + DCHECK(server_manager) << "There is no server socket manager. " + "If this assertion is failing during a test, then it is likely that " + "TestExtensionSystem is failing to provide an instance of " + "ApiResourceManager<ResumableTCPServerSocket>."; + server_sockets_ = server_manager->data_; + + ApiResourceManager<ResumableTCPSocket>* client_manager = + ApiResourceManager<ResumableTCPSocket>::Get(profile); + DCHECK(client_manager) << "There is no client socket manager. " + "If this assertion is failing during a test, then it is likely that " + "TestExtensionSystem is failing to provide an instance of " + "ApiResourceManager<ResumableTCPSocket>."; + client_sockets_ = client_manager->data_; +} + +TCPServerSocketEventDispatcher::~TCPServerSocketEventDispatcher() {} + +TCPServerSocketEventDispatcher::AcceptParams::AcceptParams() {} + +TCPServerSocketEventDispatcher::AcceptParams::~AcceptParams() {} + +void TCPServerSocketEventDispatcher::OnServerSocketListen( + const std::string& extension_id, + int socket_id) { + DCHECK(BrowserThread::CurrentlyOn(thread_id_)); + + StartSocketAccept(extension_id, socket_id); +} + +void TCPServerSocketEventDispatcher::OnServerSocketResume( + const std::string& extension_id, + int socket_id) { + DCHECK(BrowserThread::CurrentlyOn(thread_id_)); + + StartSocketAccept(extension_id, socket_id); +} + +void TCPServerSocketEventDispatcher::StartSocketAccept( + const std::string& extension_id, + int socket_id) { + DCHECK(BrowserThread::CurrentlyOn(thread_id_)); + + AcceptParams params; + params.thread_id = thread_id_; + params.profile_id = profile_; + params.extension_id = extension_id; + params.server_sockets = server_sockets_; + params.client_sockets = client_sockets_; + params.socket_id = socket_id; + + StartAccept(params); +} + +// static +void TCPServerSocketEventDispatcher::StartAccept(const AcceptParams& params) { + DCHECK(BrowserThread::CurrentlyOn(params.thread_id)); + + ResumableTCPServerSocket* socket = + params.server_sockets->Get(params.extension_id, params.socket_id); + if (!socket) { + // This can happen if the socket is closed while our callback is active. + return; + } + DCHECK(params.extension_id == socket->owner_extension_id()) + << "Socket has wrong owner."; + + // Don't start another accept if the socket has been paused. + if (socket->paused()) + return; + + socket->Accept(base::Bind(&TCPServerSocketEventDispatcher::AcceptCallback, + params)); +} + +// static +void TCPServerSocketEventDispatcher::AcceptCallback( + const AcceptParams& params, + int result_code, + net::TCPClientSocket *socket) { + DCHECK(BrowserThread::CurrentlyOn(params.thread_id)); + + if (result_code >= 0) { + ResumableTCPSocket *client_socket = + new ResumableTCPSocket(socket, params.extension_id, true); + client_socket->set_paused(true); + int client_socket_id = params.client_sockets->Add(client_socket); + + // Dispatch "onAccept" event. + sockets_tcp_server::AcceptInfo accept_info; + accept_info.socket_id = params.socket_id; + accept_info.client_socket_id = client_socket_id; + scoped_ptr<base::ListValue> args = + sockets_tcp_server::OnAccept::Create(accept_info); + scoped_ptr<Event> event( + new Event(sockets_tcp_server::OnAccept::kEventName, args.Pass())); + PostEvent(params, event.Pass()); + + // Post a task to delay the "accept" until the socket is available, as + // calling StartAccept at this point would error with ERR_IO_PENDING. + BrowserThread::PostTask( + params.thread_id, FROM_HERE, + base::Bind(&TCPServerSocketEventDispatcher::StartAccept, params)); + } else { + // Dispatch "onAcceptError" event but don't start another accept to avoid + // potential infinite "accepts" if we have a persistent network error. + sockets_tcp_server::AcceptErrorInfo accept_error_info; + accept_error_info.socket_id = params.socket_id; + accept_error_info.result_code = result_code; + scoped_ptr<base::ListValue> args = + sockets_tcp_server::OnAcceptError::Create(accept_error_info); + scoped_ptr<Event> event( + new Event(sockets_tcp_server::OnAcceptError::kEventName, args.Pass())); + PostEvent(params, event.Pass()); + + // Since we got an error, the socket is now "paused" until the application + // "resumes" it. + ResumableTCPServerSocket* socket = + params.server_sockets->Get(params.extension_id, params.socket_id); + if (socket) { + socket->set_paused(true); + } + } +} + +// static +void TCPServerSocketEventDispatcher::PostEvent(const AcceptParams& params, + scoped_ptr<Event> event) { + DCHECK(BrowserThread::CurrentlyOn(params.thread_id)); + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&DispatchEvent, + params.profile_id, + params.extension_id, + base::Passed(event.Pass()))); +} + +// static +void TCPServerSocketEventDispatcher::DispatchEvent( + void* profile_id, + const std::string& extension_id, + scoped_ptr<Event> event) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + Profile* profile = reinterpret_cast<Profile*>(profile_id); + if (!g_browser_process->profile_manager()->IsValidProfile(profile)) + return; + + EventRouter* router = ExtensionSystem::Get(profile)->event_router(); + if (router) + router->DispatchEventToExtension(extension_id, event.Pass()); +} + +} // namespace api +} // namespace extensions diff --git a/chrome/browser/extensions/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h b/chrome/browser/extensions/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h new file mode 100644 index 0000000000..4d15bc351b --- /dev/null +++ b/chrome/browser/extensions/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h @@ -0,0 +1,99 @@ +// Copyright 2013 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_EXTENSIONS_API_SOCKETS_TCP_SERVER_TCP_SERVER_SOCKET_EVENT_DISPATCHER_H_ +#define CHROME_BROWSER_EXTENSIONS_API_SOCKETS_TCP_SERVER_TCP_SERVER_SOCKET_EVENT_DISPATCHER_H_ + +#include "chrome/browser/extensions/api/api_resource_manager.h" +#include "chrome/browser/extensions/api/sockets_tcp/sockets_tcp_api.h" +#include "chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.h" + +namespace extensions { +struct Event; +class ResumableTCPSocket; +} + +namespace extensions { +namespace api { + +// Dispatch events related to "sockets.tcp" sockets from callback on native +// socket instances. There is one instance per profile. +class TCPServerSocketEventDispatcher + : public ProfileKeyedAPI, + public base::SupportsWeakPtr<TCPServerSocketEventDispatcher> { + public: + explicit TCPServerSocketEventDispatcher(Profile* profile); + virtual ~TCPServerSocketEventDispatcher(); + + // Server socket is active, start accepting connections from it. + void OnServerSocketListen(const std::string& extension_id, int socket_id); + + // Server socket is active again, start accepting connections from it. + void OnServerSocketResume(const std::string& extension_id, int socket_id); + + // ProfileKeyedAPI implementation. + static ProfileKeyedAPIFactory<TCPServerSocketEventDispatcher>* + GetFactoryInstance(); + + // Convenience method to get the SocketEventDispatcher for a profile. + static TCPServerSocketEventDispatcher* Get(Profile* profile); + + private: + typedef ApiResourceManager<ResumableTCPServerSocket>::ApiResourceData + ServerSocketData; + typedef ApiResourceManager<ResumableTCPSocket>::ApiResourceData + ClientSocketData; + friend class ProfileKeyedAPIFactory<TCPServerSocketEventDispatcher>; + // ProfileKeyedAPI implementation. + static const char* service_name() { + return "TCPServerSocketEventDispatcher"; + } + static const bool kServiceHasOwnInstanceInIncognito = true; + static const bool kServiceIsNULLWhileTesting = true; + + // base::Bind supports methods with up to 6 parameters. AcceptParams is used + // as a workaround that limitation for invoking StartAccept. + struct AcceptParams { + AcceptParams(); + ~AcceptParams(); + + content::BrowserThread::ID thread_id; + void* profile_id; + std::string extension_id; + scoped_refptr<ServerSocketData> server_sockets; + scoped_refptr<ClientSocketData> client_sockets; + int socket_id; + }; + + // Start an accept and register a callback. + void StartSocketAccept(const std::string& extension_id, int socket_id); + + // Start an accept and register a callback. + static void StartAccept(const AcceptParams& params); + + // Called when socket accepts a new connection. + static void AcceptCallback(const AcceptParams& params, + int result_code, + net::TCPClientSocket *socket); + + // Post an extension event from |thread_id| to UI thread + static void PostEvent(const AcceptParams& params, + scoped_ptr<Event> event); + + // Dispatch an extension event on to EventRouter instance on UI thread. + static void DispatchEvent(void* profile_id, + const std::string& extension_id, + scoped_ptr<Event> event); + + // Usually IO thread (except for unit testing). + content::BrowserThread::ID thread_id_; + Profile* const profile_; + scoped_refptr<ServerSocketData> server_sockets_; + scoped_refptr<ClientSocketData> client_sockets_; +}; + +} // namespace api +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_SOCKETS_TCP_SERVER_TCP_SERVER_SOCKET_EVENT_DISPATCHER_H_ diff --git a/chrome/browser/extensions/api/system_display/display_info_provider_chromeos.cc b/chrome/browser/extensions/api/system_display/display_info_provider_chromeos.cc index 3bb69835e7..151b9bb0b6 100644 --- a/chrome/browser/extensions/api/system_display/display_info_provider_chromeos.cc +++ b/chrome/browser/extensions/api/system_display/display_info_provider_chromeos.cc @@ -167,9 +167,8 @@ void UpdateDisplayLayout(const gfx::Rect& primary_display_bounds, int target_display_id) { ash::DisplayLayout layout = GetLayoutForRectangles(primary_display_bounds, target_display_bounds); - ash::DisplayController* display_controller = - ash::Shell::GetInstance()->display_controller(); - display_controller->SetLayoutForCurrentDisplays(layout); + ash::Shell::GetInstance()->display_manager()-> + SetLayoutForCurrentDisplays(layout); } // Validates that parameters passed to the SetInfo function are valid for the diff --git a/chrome/browser/extensions/api/system_display/display_info_provider_chromeos_unittest.cc b/chrome/browser/extensions/api/system_display/display_info_provider_chromeos_unittest.cc index c5d9a0f482..c91e725251 100644 --- a/chrome/browser/extensions/api/system_display/display_info_provider_chromeos_unittest.cc +++ b/chrome/browser/extensions/api/system_display/display_info_provider_chromeos_unittest.cc @@ -279,7 +279,7 @@ TEST_F(DisplayInfoProviderChromeosTest, GetMirroring) { TEST_F(DisplayInfoProviderChromeosTest, GetBounds) { UpdateDisplay("600x600, 400x520"); - GetDisplayController()->SetLayoutForCurrentDisplays( + GetDisplayManager()->SetLayoutForCurrentDisplays( ash::DisplayLayout::FromInts(ash::DisplayLayout::LEFT, -40)); DisplayInfo result = DisplayInfoProvider::Get()->GetAllDisplaysInfo(); @@ -289,7 +289,7 @@ TEST_F(DisplayInfoProviderChromeosTest, GetBounds) { EXPECT_EQ("-400,-40 400x520", SystemInfoDisplayBoundsToString(result[1]->bounds)); - GetDisplayController()->SetLayoutForCurrentDisplays( + GetDisplayManager()->SetLayoutForCurrentDisplays( ash::DisplayLayout::FromInts(ash::DisplayLayout::TOP, 40)); result = DisplayInfoProvider::Get()->GetAllDisplaysInfo(); @@ -299,7 +299,7 @@ TEST_F(DisplayInfoProviderChromeosTest, GetBounds) { EXPECT_EQ("40,-520 400x520", SystemInfoDisplayBoundsToString(result[1]->bounds)); - GetDisplayController()->SetLayoutForCurrentDisplays( + GetDisplayManager()->SetLayoutForCurrentDisplays( ash::DisplayLayout::FromInts(ash::DisplayLayout::BOTTOM, 80)); result = DisplayInfoProvider::Get()->GetAllDisplaysInfo(); diff --git a/chrome/browser/extensions/api/system_indicator/system_indicator_apitest.cc b/chrome/browser/extensions/api/system_indicator/system_indicator_apitest.cc index 616fad4138..5b923cab8f 100644 --- a/chrome/browser/extensions/api/system_indicator/system_indicator_apitest.cc +++ b/chrome/browser/extensions/api/system_indicator/system_indicator_apitest.cc @@ -60,7 +60,7 @@ IN_PROC_BROWSER_TEST_F(SystemIndicatorApiTest, SystemIndicator) { // Lazy Background Page has been shut down. ExtensionProcessManager* pm = extensions::ExtensionSystem::Get(profile())->process_manager(); - EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); + EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id())); EXPECT_TRUE(manager->SendClickEventToExtensionForTest(extension->id())); EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); diff --git a/chrome/browser/extensions/api/system_private/system_private_apitest.cc b/chrome/browser/extensions/api/system_private/system_private_apitest.cc index 6e3d3419bc..74f9266989 100644 --- a/chrome/browser/extensions/api/system_private/system_private_apitest.cc +++ b/chrome/browser/extensions/api/system_private/system_private_apitest.cc @@ -9,8 +9,8 @@ #include "chrome/common/pref_names.h" #if defined(OS_CHROMEOS) +#include "chromeos/dbus/fake_dbus_thread_manager.h" #include "chromeos/dbus/fake_update_engine_client.h" -#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" using chromeos::UpdateEngineClient; #endif @@ -31,11 +31,11 @@ class GetUpdateStatusApiTest : public ExtensionApiTest { virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { ExtensionApiTest::SetUpInProcessBrowserTestFixture(); - chromeos::MockDBusThreadManagerWithoutGMock* mock_dbus_thread_manager = - new chromeos::MockDBusThreadManagerWithoutGMock; - chromeos::DBusThreadManager::InitializeForTesting(mock_dbus_thread_manager); + chromeos::FakeDBusThreadManager* fake_dbus_thread_manager = + new chromeos::FakeDBusThreadManager; + chromeos::DBusThreadManager::InitializeForTesting(fake_dbus_thread_manager); fake_update_engine_client_ = - mock_dbus_thread_manager->fake_update_engine_client(); + fake_dbus_thread_manager->fake_update_engine_client(); } virtual void TearDownInProcessBrowserTestFixture() OVERRIDE { diff --git a/chrome/browser/extensions/api/system_storage/storage_info_provider.cc b/chrome/browser/extensions/api/system_storage/storage_info_provider.cc index 0c1abf1965..5cdc4692aa 100644 --- a/chrome/browser/extensions/api/system_storage/storage_info_provider.cc +++ b/chrome/browser/extensions/api/system_storage/storage_info_provider.cc @@ -87,6 +87,28 @@ void StorageInfoProvider::GetAllStoragesIntoInfoList() { } } +double StorageInfoProvider::GetStorageFreeSpaceFromTransientIdOnFileThread( + const std::string& transient_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + std::vector<StorageInfo> storage_list = + StorageMonitor::GetInstance()->GetAllAvailableStorages(); + + std::string device_id = + StorageMonitor::GetInstance()->GetDeviceIdForTransientId( + transient_id); + + // Lookup the matched storage info by |device_id|. + for (std::vector<StorageInfo>::const_iterator it = + storage_list.begin(); + it != storage_list.end(); ++it) { + if (device_id == it->device_id()) + return static_cast<double>(base::SysInfo::AmountOfFreeDiskSpace( + base::FilePath(it->location()))); + } + + return -1; +} + // static StorageInfoProvider* StorageInfoProvider::Get() { if (provider_.Get().get() == NULL) diff --git a/chrome/browser/extensions/api/system_storage/storage_info_provider.h b/chrome/browser/extensions/api/system_storage/storage_info_provider.h index 5bcf4a13cc..e94246a12d 100644 --- a/chrome/browser/extensions/api/system_storage/storage_info_provider.h +++ b/chrome/browser/extensions/api/system_storage/storage_info_provider.h @@ -35,6 +35,9 @@ typedef std::vector<linked_ptr< class StorageInfoProvider : public SystemInfoProvider { public: + typedef base::Callback<void(const std::string&, double)> + GetStorageFreeSpaceCallback; + // Get the single shared instance of StorageInfoProvider. static StorageInfoProvider* Get(); @@ -43,6 +46,9 @@ class StorageInfoProvider : public SystemInfoProvider { virtual void InitializeProvider(const base::Closure& do_query_info_callback) OVERRIDE; + virtual double GetStorageFreeSpaceFromTransientIdOnFileThread( + const std::string& transient_id); + const StorageUnitInfoList& storage_unit_info_list() const; static void InitializeForTesting(scoped_refptr<StorageInfoProvider> provider); diff --git a/chrome/browser/extensions/api/system_storage/system_storage_api.cc b/chrome/browser/extensions/api/system_storage/system_storage_api.cc index 2157badefd..e374f63288 100644 --- a/chrome/browser/extensions/api/system_storage/system_storage_api.cc +++ b/chrome/browser/extensions/api/system_storage/system_storage_api.cc @@ -8,6 +8,7 @@ namespace extensions { using api::system_storage::StorageUnitInfo; namespace EjectDevice = api::system_storage::EjectDevice; +namespace GetAvailableCapacity = api::system_storage::GetAvailableCapacity; SystemStorageGetInfoFunction::SystemStorageGetInfoFunction() { } @@ -92,4 +93,53 @@ void SystemStorageEjectDeviceFunction::HandleResponse( SendResponse(true); } +SystemStorageGetAvailableCapacityFunction:: + SystemStorageGetAvailableCapacityFunction() { +} + +SystemStorageGetAvailableCapacityFunction:: + ~SystemStorageGetAvailableCapacityFunction() { +} + +bool SystemStorageGetAvailableCapacityFunction::RunImpl() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + + scoped_ptr<GetAvailableCapacity::Params> params( + GetAvailableCapacity::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + StorageMonitor::GetInstance()->EnsureInitialized(base::Bind( + &SystemStorageGetAvailableCapacityFunction::OnStorageMonitorInit, + this, + params->id)); + return true; +} + +void SystemStorageGetAvailableCapacityFunction::OnStorageMonitorInit( + const std::string& transient_id) { + content::BrowserThread::PostTaskAndReplyWithResult( + content::BrowserThread::FILE, + FROM_HERE, + base::Bind( + &StorageInfoProvider::GetStorageFreeSpaceFromTransientIdOnFileThread, + StorageInfoProvider::Get(), transient_id), + base::Bind( + &SystemStorageGetAvailableCapacityFunction::OnQueryCompleted, + this, transient_id)); +} + +void SystemStorageGetAvailableCapacityFunction::OnQueryCompleted( + const std::string& transient_id, double available_capacity) { + bool success = available_capacity >= 0; + if (success) { + api::system_storage::StorageAvailableCapacityInfo result; + result.id = transient_id; + result.available_capacity = available_capacity; + SetResult(result.ToValue().release()); + } else { + SetError("Error occurred when querying available capacity."); + } + SendResponse(success); +} + } // namespace extensions diff --git a/chrome/browser/extensions/api/system_storage/system_storage_api.h b/chrome/browser/extensions/api/system_storage/system_storage_api.h index 07e29a664c..f31793c8b0 100644 --- a/chrome/browser/extensions/api/system_storage/system_storage_api.h +++ b/chrome/browser/extensions/api/system_storage/system_storage_api.h @@ -44,6 +44,21 @@ class SystemStorageEjectDeviceFunction void HandleResponse(StorageMonitor::EjectStatus status); }; +class SystemStorageGetAvailableCapacityFunction + : public AsyncExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("system.storage.getAvailableCapacity", + SYSTEM_STORAGE_GETAVAILABLECAPACITY); + SystemStorageGetAvailableCapacityFunction(); + + private: + void OnStorageMonitorInit(const std::string& transient_id); + void OnQueryCompleted(const std::string& transient_id, + double available_capacity); + virtual ~SystemStorageGetAvailableCapacityFunction(); + virtual bool RunImpl() OVERRIDE; +}; + } // namespace extensions #endif // CHROME_BROWSER_EXTENSIONS_API_SYSTEM_STORAGE_SYSTEM_STORAGE_API_H_ diff --git a/chrome/browser/extensions/api/system_storage/system_storage_apitest.cc b/chrome/browser/extensions/api/system_storage/system_storage_apitest.cc index 5bd4fe9ac7..4ce92d38ef 100644 --- a/chrome/browser/extensions/api/system_storage/system_storage_apitest.cc +++ b/chrome/browser/extensions/api/system_storage/system_storage_apitest.cc @@ -21,13 +21,49 @@ using extensions::test::TestStorageUnitInfo; using extensions::test::kRemovableStorageData; const struct TestStorageUnitInfo kTestingData[] = { - {"dcim:device:001", "0xbeaf", 4098, 1000}, - {"path:device:002", "/home", 4098, 1000}, - {"path:device:003", "/data", 10000, 1000} + {"dcim:device:001", "0xbeaf", 4098, 1}, + {"path:device:002", "/home", 4098, 2}, + {"path:device:003", "/data", 10000, 3} }; } // namespace +class TestStorageInfoProvider : public extensions::StorageInfoProvider { + public: + TestStorageInfoProvider(const struct TestStorageUnitInfo* testing_data, + size_t n); + + private: + virtual ~TestStorageInfoProvider(); + + // StorageInfoProvider implementations. + virtual double GetStorageFreeSpaceFromTransientIdOnFileThread( + const std::string& transient_id) OVERRIDE; + + std::vector<struct TestStorageUnitInfo> testing_data_; +}; + +TestStorageInfoProvider::TestStorageInfoProvider( + const struct TestStorageUnitInfo* testing_data, size_t n) + : testing_data_(testing_data, testing_data + n) { +} + +TestStorageInfoProvider::~TestStorageInfoProvider() { +} + +double TestStorageInfoProvider::GetStorageFreeSpaceFromTransientIdOnFileThread( + const std::string& transient_id) { + std::string device_id = + StorageMonitor::GetInstance()->GetDeviceIdForTransientId( + transient_id); + for (size_t i = 0; i < testing_data_.size(); ++i) { + if (testing_data_[i].device_id == device_id) { + return static_cast<double>(testing_data_[i].available_capacity); + } + } + return -1; +} + class SystemStorageApiTest : public ExtensionApiTest { public: SystemStorageApiTest() {} @@ -65,6 +101,10 @@ class SystemStorageApiTest : public ExtensionApiTest { IN_PROC_BROWSER_TEST_F(SystemStorageApiTest, Storage) { SetUpAllMockStorageDevices(); + TestStorageInfoProvider* provider = + new TestStorageInfoProvider(kTestingData, + arraysize(kTestingData)); + extensions::StorageInfoProvider::InitializeForTesting(provider); std::vector<linked_ptr<ExtensionTestMessageListener> > device_ids_listeners; for (size_t i = 0; i < arraysize(kTestingData); ++i) { linked_ptr<ExtensionTestMessageListener> listener( diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc b/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc index b7b6978f0c..5bd518763f 100644 --- a/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc +++ b/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc @@ -231,7 +231,15 @@ class TabCapturePerformanceTest } // namespace -IN_PROC_BROWSER_TEST_P(TabCapturePerformanceTest, Performance) { +// This does not work on Aura and Mac GPU bots yet +// http://crbug.com/308236 +#if defined(USE_AURA) || defined(OS_MACOSX) +#define MAYBE_Performance DISABLED_Performance +#else +#define MAYBE_Performance Performance +#endif + +IN_PROC_BROWSER_TEST_P(TabCapturePerformanceTest, MAYBE_Performance) { RunTest("TabCapturePerformance"); } @@ -250,7 +258,7 @@ INSTANTIATE_TEST_CASE_P( kTestThroughWebRTC | kDisableVsync, kTestThroughWebRTC | kDisableVsync | kUseGpu | kForceGpuComposited)); -#ifdef USE_AURA +#if defined(USE_AURA) // TODO(hubbe): // These are temporary tests for the purpose of determining what the // appropriate scaling quality is. Once that has been determined, diff --git a/chrome/browser/extensions/api/tabs/windows_event_router.cc b/chrome/browser/extensions/api/tabs/windows_event_router.cc index 1f90f45e56..adc9a07889 100644 --- a/chrome/browser/extensions/api/tabs/windows_event_router.cc +++ b/chrome/browser/extensions/api/tabs/windows_event_router.cc @@ -9,6 +9,7 @@ #include "chrome/browser/extensions/event_router.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/window_controller.h" #include "chrome/browser/extensions/window_controller_list.h" #include "chrome/browser/profiles/profile.h" @@ -115,8 +116,9 @@ static void WillDispatchWindowFocusedEvent(Profile* new_active_profile, // can't see the new focused window across the incognito boundary. // See crbug.com/46610. if (new_active_profile && new_active_profile != profile && - !extensions::ExtensionSystem::Get(profile)->extension_service()-> - CanCrossIncognito(extension)) { + !extension_util::CanCrossIncognito( + extension, + extensions::ExtensionSystem::Get(profile)->extension_service())) { event_args->Clear(); event_args->Append(new base::FundamentalValue( extension_misc::kUnknownWindowId)); diff --git a/chrome/browser/extensions/api/usb/usb_api.cc b/chrome/browser/extensions/api/usb/usb_api.cc index 1898fcb571..48a6840b67 100644 --- a/chrome/browser/extensions/api/usb/usb_api.cc +++ b/chrome/browser/extensions/api/usb/usb_api.cc @@ -65,7 +65,9 @@ const char kErrorOpen[] = "Failed to open device."; const char kErrorCancelled[] = "Transfer was cancelled."; const char kErrorDisconnect[] = "Device disconnected."; const char kErrorGeneric[] = "Transfer failed."; +#if !defined(OS_CHROMEOS) const char kErrorNotSupported[] = "Not supported on this platform."; +#endif const char kErrorOverflow[] = "Inbound transfer overflow."; const char kErrorStalled[] = "Transfer stalled."; const char kErrorTimeout[] = "Transfer timed out."; diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc index cdf05541e0..7616dcc370 100644 --- a/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc +++ b/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc @@ -406,6 +406,7 @@ void WebNavigationTabObserver::DidStartProvisionalLoadForFrame( void WebNavigationTabObserver::DidCommitProvisionalLoadForFrame( int64 frame_num, + const string16& frame_unique_name, bool is_main_frame, const GURL& url, content::PageTransition transition_type, @@ -483,6 +484,7 @@ void WebNavigationTabObserver::DidCommitProvisionalLoadForFrame( void WebNavigationTabObserver::DidFailProvisionalLoad( int64 frame_num, + const string16& frame_unique_id, bool is_main_frame, const GURL& validated_url, int error_code, diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_api.h b/chrome/browser/extensions/api/web_navigation/web_navigation_api.h index d0a6a6996c..eeb25ec72f 100644 --- a/chrome/browser/extensions/api/web_navigation/web_navigation_api.h +++ b/chrome/browser/extensions/api/web_navigation/web_navigation_api.h @@ -66,12 +66,14 @@ class WebNavigationTabObserver content::RenderViewHost* render_view_host) OVERRIDE; virtual void DidCommitProvisionalLoadForFrame( int64 frame_num, + const string16& frame_unique_name, bool is_main_frame, const GURL& url, content::PageTransition transition_type, content::RenderViewHost* render_view_host) OVERRIDE; virtual void DidFailProvisionalLoad( int64 frame_num, + const string16& frame_unique_name, bool is_main_frame, const GURL& validated_url, int error_code, diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc index df18d25096..bb0a7d0906 100644 --- a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc +++ b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc @@ -202,6 +202,7 @@ class DelayLoadStartAndExecuteJavascript virtual void DidCommitProvisionalLoadForFrame( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& url, content::PageTransition transition_type, @@ -533,7 +534,7 @@ IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, MAYBE_UserAction) { ExtensionService* service = extensions::ExtensionSystem::Get( browser()->profile())->extension_service(); const extensions::Extension* extension = - service->GetExtensionById(last_loaded_extension_id_, false); + service->GetExtensionById(last_loaded_extension_id(), false); GURL url = extension->GetResourceURL("userAction/a.html"); ui_test_utils::NavigateToURL(browser(), url); @@ -575,7 +576,7 @@ IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, MAYBE_RequestOpenTab) { ExtensionService* service = extensions::ExtensionSystem::Get( browser()->profile())->extension_service(); const extensions::Extension* extension = - service->GetExtensionById(last_loaded_extension_id_, false); + service->GetExtensionById(last_loaded_extension_id(), false); GURL url = extension->GetResourceURL("requestOpenTab/a.html"); ui_test_utils::NavigateToURL(browser(), url); @@ -687,7 +688,7 @@ IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, CrossProcess) { ExtensionService* service = extensions::ExtensionSystem::Get( browser()->profile())->extension_service(); const extensions::Extension* extension = - service->GetExtensionById(last_loaded_extension_id_, false); + service->GetExtensionById(last_loaded_extension_id(), false); // See crossProcess/d.html. DelayLoadStartAndExecuteJavascript call_script( @@ -709,7 +710,7 @@ IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, CrossProcessFragment) { ExtensionService* service = extensions::ExtensionSystem::Get( browser()->profile())->extension_service(); const extensions::Extension* extension = - service->GetExtensionById(last_loaded_extension_id_, false); + service->GetExtensionById(last_loaded_extension_id(), false); // See crossProcess/f.html. DelayLoadStartAndExecuteJavascript call_script3( @@ -742,7 +743,7 @@ IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, CrossProcessHistory) { ExtensionService* service = extensions::ExtensionSystem::Get( browser()->profile())->extension_service(); const extensions::Extension* extension = - service->GetExtensionById(last_loaded_extension_id_, false); + service->GetExtensionById(last_loaded_extension_id(), false); // See crossProcess/e.html. DelayLoadStartAndExecuteJavascript call_script2( diff --git a/chrome/browser/extensions/api/web_request/web_request_apitest.cc b/chrome/browser/extensions/api/web_request/web_request_apitest.cc index 05de8d862b..67d8623c31 100644 --- a/chrome/browser/extensions/api/web_request/web_request_apitest.cc +++ b/chrome/browser/extensions/api/web_request/web_request_apitest.cc @@ -135,7 +135,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, MAYBE_WebRequestNewTab) { ExtensionService* service = extensions::ExtensionSystem::Get( browser()->profile())->extension_service(); const extensions::Extension* extension = - service->GetExtensionById(last_loaded_extension_id_, false); + service->GetExtensionById(last_loaded_extension_id(), false); GURL url = extension->GetResourceURL("newTab/a.html"); ui_test_utils::NavigateToURL(browser(), url); diff --git a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc index ad40866543..f01005f81e 100644 --- a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc +++ b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc @@ -468,6 +468,7 @@ void WebstorePrivateBeginInstallWithManifest3Function::InstallUIProceed() { // If we are enabling the launcher, we should not show the app list in order // to train the user to open it themselves at least once. approval->skip_post_install_ui = params_->details.enable_launcher; + approval->dummy_extension = dummy_extension_; approval->installing_icon = gfx::ImageSkia::CreateFrom1xBitmap(icon_); g_pending_approvals.Get().PushApproval(approval.Pass()); diff --git a/chrome/browser/extensions/app_background_page_apitest.cc b/chrome/browser/extensions/app_background_page_apitest.cc index f07952c868..8687af80d2 100644 --- a/chrome/browser/extensions/app_background_page_apitest.cc +++ b/chrome/browser/extensions/app_background_page_apitest.cc @@ -13,6 +13,7 @@ #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_dialogs.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension.h" @@ -69,7 +70,7 @@ class AppBackgroundPageApiTest : public ExtensionApiTest { DLOG(WARNING) << "Skipping check - background mode disabled"; return true; } - if (manager->IsBackgroundModeActiveForTest() == expected_background_mode) + if (manager->IsBackgroundModeActive() == expected_background_mode) return true; // We are not currently in the expected state - wait for the state to @@ -78,7 +79,7 @@ class AppBackgroundPageApiTest : public ExtensionApiTest { chrome::NOTIFICATION_BACKGROUND_MODE_CHANGED, content::NotificationService::AllSources()); watcher.Wait(); - return manager->IsBackgroundModeActiveForTest() == expected_background_mode; + return manager->IsBackgroundModeActive() == expected_background_mode; #endif } @@ -216,6 +217,11 @@ IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, ManifestBackgroundPage) { } IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, NoJsBackgroundPage) { + // Keep the task manager up through this test to verify that a crash doesn't + // happen when window.open creates a background page that switches + // RenderViewHosts. See http://crbug.com/165138. + chrome::ShowTaskManager(browser()); + // Make sure that no BackgroundContentses get deleted (a signal that repeated // window.open calls recreate instances, instead of being no-ops). content::TestNotificationTracker background_deleted_tracker; diff --git a/chrome/browser/extensions/component_loader.cc b/chrome/browser/extensions/component_loader.cc index 81aa454db3..1f802b5164 100644 --- a/chrome/browser/extensions/component_loader.cc +++ b/chrome/browser/extensions/component_loader.cc @@ -435,12 +435,18 @@ void ComponentLoader::AddDefaultComponentExtensionsWithBackgroundPages( #endif } + // If (!enable_background_extensions_during_testing || this isn't a test) + // install_feedback = false; + bool install_feedback = enable_background_extensions_during_testing; #if defined(GOOGLE_CHROME_BUILD) - Add(IDR_FEEDBACK_MANIFEST, base::FilePath(FILE_PATH_LITERAL("feedback"))); + install_feedback = true; #endif // defined(GOOGLE_CHROME_BUILD) + if (install_feedback) + Add(IDR_FEEDBACK_MANIFEST, base::FilePath(FILE_PATH_LITERAL("feedback"))); #if defined(OS_CHROMEOS) - if (!skip_session_components) { + if (!skip_session_components && + !command_line->HasSwitch(chromeos::switches::kGuestSession)) { Add(IDR_WALLPAPERMANAGER_MANIFEST, base::FilePath(FILE_PATH_LITERAL("chromeos/wallpaper_manager"))); @@ -500,11 +506,13 @@ void ComponentLoader::AddDefaultComponentExtensionsWithBackgroundPages( std::string enable_prefix(kEnablePrefix); std::string field_trial_result = base::FieldTrialList::FindFullName(kFieldTrialName); - if ((field_trial_result.compare( + if (((field_trial_result.compare( 0, enable_prefix.length(), - enable_prefix) == 0) || - CommandLine::ForCurrentProcess()->HasSwitch( + enable_prefix) == 0) && + !CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableGoogleNowIntegration)) || + CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableGoogleNowIntegration)) { Add(IDR_GOOGLE_NOW_MANIFEST, base::FilePath(FILE_PATH_LITERAL("google_now"))); diff --git a/chrome/browser/extensions/context_menu_matcher.cc b/chrome/browser/extensions/context_menu_matcher.cc index 950461d4d0..c125660b09 100644 --- a/chrome/browser/extensions/context_menu_matcher.cc +++ b/chrome/browser/extensions/context_menu_matcher.cc @@ -7,6 +7,7 @@ #include "chrome/browser/extensions/context_menu_matcher.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/profiles/profile.h" #include "content/public/common/context_menu_params.h" #include "ui/gfx/favicon_size.h" @@ -161,7 +162,7 @@ bool ContextMenuMatcher::GetRelevantExtensionTopLevelItems( if (!all_items || all_items->empty()) return false; - *can_cross_incognito = service->CanCrossIncognito(*extension); + *can_cross_incognito = extension_util::CanCrossIncognito(*extension, service); items = GetRelevantExtensionItems(*all_items, *can_cross_incognito); diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc index 2205608e8c..5b45c476cb 100644 --- a/chrome/browser/extensions/crx_installer.cc +++ b/chrome/browser/extensions/crx_installer.cc @@ -48,6 +48,7 @@ #include "content/public/browser/resource_dispatcher_host.h" #include "content/public/browser/user_metrics.h" #include "extensions/common/manifest.h" +#include "extensions/common/permissions/permission_message_provider.h" #include "extensions/common/user_script.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" @@ -108,7 +109,9 @@ CrxInstaller::CrxInstaller( : install_directory_(service_weak->install_directory()), install_source_(Manifest::INTERNAL), approved_(false), - expected_manifest_strict_checking_(true), + expected_manifest_check_level_( + WebstoreInstaller::MANIFEST_CHECK_LEVEL_STRICT), + expected_version_strict_checking_(false), extensions_enabled_(service_weak->extensions_enabled()), delete_source_(false), create_app_shortcut_(false), @@ -143,10 +146,16 @@ CrxInstaller::CrxInstaller( // Mark the extension as approved, but save the expected manifest and ID // so we can check that they match the CRX's. approved_ = true; - expected_manifest_.reset(approval->manifest->DeepCopy()); - expected_manifest_strict_checking_ = approval->strict_manifest_check; + expected_manifest_check_level_ = approval->manifest_check_level; + if (expected_manifest_check_level_ != + WebstoreInstaller::MANIFEST_CHECK_LEVEL_NONE) + expected_manifest_.reset(approval->manifest->DeepCopy()); expected_id_ = approval->extension_id; } + if (approval->minimum_version.get()) { + expected_version_.reset(new Version(*approval->minimum_version)); + expected_version_strict_checking_ = false; + } show_dialog_callback_ = approval->show_dialog_callback; } @@ -247,21 +256,41 @@ CrxInstallerError CrxInstaller::AllowInstall(const Extension* extension) { ASCIIToUTF16(extension->id()))); } - if (expected_version_.get() && - !expected_version_->Equals(*extension->version())) { - return CrxInstallerError( - l10n_util::GetStringFUTF16( - IDS_EXTENSION_INSTALL_UNEXPECTED_VERSION, - ASCIIToUTF16(expected_version_->GetString()), - ASCIIToUTF16(extension->version()->GetString()))); + if (expected_version_.get()) { + if (expected_version_strict_checking_) { + if (!expected_version_->Equals(*extension->version())) { + return CrxInstallerError( + l10n_util::GetStringFUTF16( + IDS_EXTENSION_INSTALL_UNEXPECTED_VERSION, + ASCIIToUTF16(expected_version_->GetString()), + ASCIIToUTF16(extension->version()->GetString()))); + } + } else { + if (extension->version()->CompareTo(*expected_version_) < 0) { + return CrxInstallerError( + l10n_util::GetStringFUTF16( + IDS_EXTENSION_INSTALL_UNEXPECTED_VERSION, + ASCIIToUTF16(expected_version_->GetString() + "+"), + ASCIIToUTF16(extension->version()->GetString()))); + } + } } // Make sure the manifests match if we want to bypass the prompt. if (approved_) { bool valid = false; - if (expected_manifest_.get()) { + if (expected_manifest_check_level_ == + WebstoreInstaller::MANIFEST_CHECK_LEVEL_NONE) { + // To skip manifest checking, the extension must be a shared module + // and not request any permissions. + if (SharedModuleInfo::IsSharedModule(extension) && + PermissionsData::GetActivePermissions(extension)->IsEmpty()) { + valid = true; + } + } else { valid = expected_manifest_->Equals(original_manifest_.get()); - if (!valid && !expected_manifest_strict_checking_) { + if (!valid && expected_manifest_check_level_ == + WebstoreInstaller::MANIFEST_CHECK_LEVEL_LOOSE) { std::string error; scoped_refptr<Extension> dummy_extension = Extension::Create(base::FilePath(), @@ -272,12 +301,14 @@ CrxInstallerError CrxInstaller::AllowInstall(const Extension* extension) { if (error.empty()) { scoped_refptr<const PermissionSet> expected_permissions = PermissionsData::GetActivePermissions(dummy_extension.get()); - valid = !(expected_permissions->HasLessPrivilegesThan( - PermissionsData::GetActivePermissions(extension), - extension->GetType())); + valid = !(PermissionMessageProvider::Get()->IsPrivilegeIncrease( + expected_permissions, + PermissionsData::GetActivePermissions(extension), + extension->GetType())); } } } + if (!valid) return CrxInstallerError( l10n_util::GetStringUTF16(IDS_EXTENSION_MANIFEST_INVALID)); diff --git a/chrome/browser/extensions/crx_installer.h b/chrome/browser/extensions/crx_installer.h index fde15b69d9..6d55481e8a 100644 --- a/chrome/browser/extensions/crx_installer.h +++ b/chrome/browser/extensions/crx_installer.h @@ -127,6 +127,7 @@ class CrxInstaller void set_expected_version(const Version& val) { expected_version_.reset(new Version(val)); + expected_version_strict_checking_ = true; } bool delete_source() const { return delete_source_; } @@ -288,10 +289,9 @@ class CrxInstaller // extension's manifest must match this for the install to proceed. scoped_ptr<Manifest> expected_manifest_; - // Set to true if we want a strict, exact match check between the actual and - // expected manifest, rather than just a check that the effective permissions - // are the same. - bool expected_manifest_strict_checking_; + // The level of checking when comparing the actual manifest against + // the |expected_manifest_|. + WebstoreInstaller::ManifestCheckLevel expected_manifest_check_level_; // If non-NULL, contains the expected version of the extension we're // installing. Important for external sources, where claiming the wrong @@ -299,6 +299,11 @@ class CrxInstaller // restart. scoped_ptr<Version> expected_version_; + // If true, the actual version should be same with the |expected_version_|, + // Otherwise the actual version should be equal to or newer than + // the |expected_version_|. + bool expected_version_strict_checking_; + // Whether manual extension installation is enabled. We can't just check this // before trying to install because themes are special-cased to always be // allowed. diff --git a/chrome/browser/extensions/crx_installer_browsertest.cc b/chrome/browser/extensions/crx_installer_browsertest.cc index a91684215e..d9c7114c7b 100644 --- a/chrome/browser/extensions/crx_installer_browsertest.cc +++ b/chrome/browser/extensions/crx_installer_browsertest.cc @@ -365,7 +365,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest, DISABLED_AllowOffStore) { } crx_installer->InstallCrx(test_data_dir_.AppendASCII("good.crx")); - EXPECT_EQ(kTestData[i], WaitForExtensionInstall()) << kTestData[i]; + EXPECT_EQ(kTestData[i], + WaitForExtensionInstall()) << kTestData[i]; EXPECT_EQ(kTestData[i], mock_prompt->did_succeed()); EXPECT_EQ(kTestData[i], mock_prompt->confirmation_requested()) << kTestData[i]; diff --git a/chrome/browser/extensions/error_console/error_console_browsertest.cc b/chrome/browser/extensions/error_console/error_console_browsertest.cc index 3e2ab2fc3c..952ff52263 100644 --- a/chrome/browser/extensions/error_console/error_console_browsertest.cc +++ b/chrome/browser/extensions/error_console/error_console_browsertest.cc @@ -247,7 +247,7 @@ class ErrorConsoleBrowserTest : public ExtensionBrowserTest { ExtensionService* service = extensions::ExtensionSystem::Get(profile())->extension_service(); service->toolbar_model()->ExecuteBrowserAction( - *extension, browser(), NULL); + *extension, browser(), NULL, true); break; } case ACTION_NONE: diff --git a/chrome/browser/extensions/event_router.cc b/chrome/browser/extensions/event_router.cc index b547a33b77..b5e88bf4e1 100644 --- a/chrome/browser/extensions/event_router.cc +++ b/chrome/browser/extensions/event_router.cc @@ -22,6 +22,7 @@ #include "chrome/browser/extensions/extension_process_manager.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/lazy_background_task_queue.h" #include "chrome/browser/extensions/process_map.h" #include "chrome/browser/profiles/profile.h" @@ -631,9 +632,9 @@ bool EventRouter::CanDispatchEventToProfile(Profile* profile, // incognito tab event sent to a normal process, or vice versa). bool cross_incognito = event->restrict_to_profile && profile != event->restrict_to_profile; - if (cross_incognito && - !ExtensionSystem::Get(profile)->extension_service()-> - CanCrossIncognito(extension)) { + if (cross_incognito && !extension_util::CanCrossIncognito( + extension, + ExtensionSystem::Get(profile)->extension_service())) { return false; } diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc index f5da8f6d0e..0d6fd91e81 100644 --- a/chrome/browser/extensions/extension_browsertest.cc +++ b/chrome/browser/extensions/extension_browsertest.cc @@ -24,12 +24,12 @@ #include "chrome/browser/extensions/extension_install_prompt.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/unpacked_installer.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_window.h" -#include "chrome/browser/ui/omnibox/location_bar.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" @@ -54,38 +54,19 @@ using extensions::ExtensionCreator; using extensions::FeatureSwitch; using extensions::Manifest; -namespace { - -bool HasExtensionPageActionCountReachedTarget(LocationBarTesting* location_bar, - int target_page_action_count) { - VLOG(1) << "Number of page actions: " << location_bar->PageActionCount(); - return location_bar->PageActionCount() == target_page_action_count; -} - -bool HasExtensionPageActionVisibilityReachedTarget( - LocationBarTesting* location_bar, - int target_visible_page_action_count) { - VLOG(1) << "Number of visible page actions: " - << location_bar->PageActionVisibleCount(); - return location_bar->PageActionVisibleCount() == - target_visible_page_action_count; -} - -} // namespace - ExtensionBrowserTest::ExtensionBrowserTest() : loaded_(false), installed_(false), - extension_installs_observed_(0), - extension_load_errors_observed_(0), current_channel_(chrome::VersionInfo::CHANNEL_DEV), override_prompt_for_external_extensions_( - FeatureSwitch::prompt_for_external_extensions(), false), + FeatureSwitch::prompt_for_external_extensions(), + false), profile_(NULL) { EXPECT_TRUE(temp_dir_.CreateUniqueTempDir()); } -ExtensionBrowserTest::~ExtensionBrowserTest() {} +ExtensionBrowserTest::~ExtensionBrowserTest() { +} Profile* ExtensionBrowserTest::profile() { if (!profile_) { @@ -114,6 +95,7 @@ const Extension* ExtensionBrowserTest::GetExtensionByPath( void ExtensionBrowserTest::SetUpCommandLine(CommandLine* command_line) { PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_); test_data_dir_ = test_data_dir_.AppendASCII("extensions"); + observer_.reset(new ExtensionTestNotificationObserver(browser())); #if defined(OS_CHROMEOS) // This makes sure that we create the Default profile first, with no @@ -127,6 +109,7 @@ void ExtensionBrowserTest::SetUpCommandLine(CommandLine* command_line) { void ExtensionBrowserTest::SetUpOnMainThread() { InProcessBrowserTest::SetUpOnMainThread(); + observer_.reset(new ExtensionTestNotificationObserver(browser())); } const Extension* ExtensionBrowserTest::LoadExtensionWithFlags( @@ -134,23 +117,21 @@ const Extension* ExtensionBrowserTest::LoadExtensionWithFlags( ExtensionService* service = extensions::ExtensionSystem::Get( profile())->extension_service(); { - content::WindowedNotificationObserver observer( - chrome::NOTIFICATION_EXTENSION_LOADED, - content::NotificationService::AllSources()); - content::NotificationRegistrar registrar; - registrar.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, - content::NotificationService::AllSources()); + observer_->Watch(chrome::NOTIFICATION_EXTENSION_LOADED, + content::NotificationService::AllSources()); + scoped_refptr<extensions::UnpackedInstaller> installer( extensions::UnpackedInstaller::Create(service)); installer->set_prompt_for_plugins(false); installer->set_require_modern_manifest_version( (flags & kFlagAllowOldManifestVersions) == 0); installer->Load(path); - observer.Wait(); + + observer_->Wait(); } // Find the loaded extension by its path. See crbug.com/59531 for why - // we cannot just use last_loaded_extension_id_. + // we cannot just use last_loaded_extension_id(). const Extension* extension = GetExtensionByPath(service->extensions(), path); if (!extension) return NULL; @@ -192,11 +173,11 @@ const Extension* ExtensionBrowserTest::LoadExtensionWithFlags( content::WindowedNotificationObserver load_signal( chrome::NOTIFICATION_EXTENSION_LOADED, content::Source<Profile>(profile())); - CHECK(!service->IsIncognitoEnabled(extension_id) || + CHECK(!extension_util::IsIncognitoEnabled(extension_id, service) || extension->force_incognito_enabled()); if (flags & kFlagEnableIncognito) { - service->SetIsIncognitoEnabled(extension_id, true); + extension_util::SetIsIncognitoEnabled(extension_id, service, true); load_signal.Wait(); extension = service->GetExtensionById(extension_id, false); CHECK(extension) << extension_id << " not found after reloading."; @@ -207,16 +188,16 @@ const Extension* ExtensionBrowserTest::LoadExtensionWithFlags( content::WindowedNotificationObserver load_signal( chrome::NOTIFICATION_EXTENSION_LOADED, content::Source<Profile>(profile())); - CHECK(service->AllowFileAccess(extension)); + CHECK(extension_util::AllowFileAccess(extension, service)); if (!(flags & kFlagEnableFileAccess)) { - service->SetAllowFileAccess(extension, false); + extension_util::SetAllowFileAccess(extension, service, false); load_signal.Wait(); extension = service->GetExtensionById(extension_id, false); CHECK(extension) << extension_id << " not found after reloading."; } } - if (!WaitForExtensionViewsToLoad()) + if (!observer_->WaitForExtensionViewsToLoad()) return NULL; return extension; @@ -248,7 +229,7 @@ const Extension* ExtensionBrowserTest::LoadExtensionAsComponentWithManifest( const Extension* extension = service->extensions()->GetByID(extension_id); if (!extension) return NULL; - last_loaded_extension_id_ = extension->id(); + observer_->set_last_loaded_extension_id(extension->id()); return extension; } @@ -456,16 +437,13 @@ const Extension* ExtensionBrowserTest::InstallOrUpdateExtension( extensions::CrxInstaller::OffStoreInstallAllowedInTest); } - content::WindowedNotificationObserver observer( + observer_->Watch( chrome::NOTIFICATION_CRX_INSTALLER_DONE, content::Source<extensions::CrxInstaller>(installer.get())); - content::NotificationRegistrar registrar; - registrar.Add(this, chrome::NOTIFICATION_CRX_INSTALLER_DONE, - content::Source<extensions::CrxInstaller>(installer.get())); installer->InstallCrx(crx_path); - observer.Wait(); + observer_->Wait(); } size_t num_after = service->extensions()->size(); @@ -489,24 +467,21 @@ const Extension* ExtensionBrowserTest::InstallOrUpdateExtension( return NULL; } - if (!WaitForExtensionViewsToLoad()) + if (!observer_->WaitForExtensionViewsToLoad()) return NULL; - return service->GetExtensionById(last_loaded_extension_id_, false); + return service->GetExtensionById(last_loaded_extension_id(), false); } void ExtensionBrowserTest::ReloadExtension(const std::string extension_id) { - content::WindowedNotificationObserver observer( - chrome::NOTIFICATION_EXTENSION_LOADED, - content::NotificationService::AllSources()); - content::NotificationRegistrar registrar; - registrar.Add(this, - chrome::NOTIFICATION_EXTENSION_LOADED, + observer_->Watch(chrome::NOTIFICATION_EXTENSION_LOADED, content::NotificationService::AllSources()); + ExtensionService* service = extensions::ExtensionSystem::Get(profile())->extension_service(); service->ReloadExtension(extension_id); - observer.Wait(); - WaitForExtensionViewsToLoad(); + + observer_->Wait(); + observer_->WaitForExtensionViewsToLoad(); } void ExtensionBrowserTest::UnloadExtension(const std::string& extension_id) { @@ -533,126 +508,11 @@ void ExtensionBrowserTest::EnableExtension(const std::string& extension_id) { service->EnableExtension(extension_id); } -bool ExtensionBrowserTest::WaitForPageActionCountChangeTo(int count) { - LocationBarTesting* location_bar = - browser()->window()->GetLocationBar()->GetLocationBarForTesting(); - if (!HasExtensionPageActionCountReachedTarget(location_bar, count)) { - content::WindowedNotificationObserver( - chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED, - base::Bind( - &HasExtensionPageActionCountReachedTarget, location_bar, count)) - .Wait(); - } - return HasExtensionPageActionCountReachedTarget(location_bar, count); -} - -bool ExtensionBrowserTest::WaitForPageActionVisibilityChangeTo(int count) { - LocationBarTesting* location_bar = - browser()->window()->GetLocationBar()->GetLocationBarForTesting(); - if (!HasExtensionPageActionVisibilityReachedTarget(location_bar, count)) { - content::WindowedNotificationObserver( - chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED, - base::Bind(&HasExtensionPageActionVisibilityReachedTarget, - location_bar, - count)) - .Wait(); - } - return HasExtensionPageActionVisibilityReachedTarget(location_bar, count); -} - -void ExtensionBrowserTest::WaitForNotification(int notification_type) { - // TODO(bauerb): Using a WindowedNotificationObserver like this can break - // easily, if the notification we're waiting for is sent before this method. - // Change it so that the WindowedNotificationObserver is constructed earlier. - content::NotificationRegistrar registrar; - registrar.Add(this, notification_type, - content::NotificationService::AllSources()); - content::WindowedNotificationObserver( - notification_type, content::NotificationService::AllSources()).Wait(); -} - -bool ExtensionBrowserTest::WaitForExtensionViewsToLoad() { - - ExtensionProcessManager* manager = - extensions::ExtensionSystem::Get(profile())->process_manager(); - ExtensionProcessManager::ViewSet all_views = manager->GetAllViews(); - for (ExtensionProcessManager::ViewSet::const_iterator iter = - all_views.begin(); - iter != all_views.end();) { - if (!(*iter)->IsLoading()) { - ++iter; - } else { - // Wait for all the extension render view hosts that exist to finish - // loading. - content::WindowedNotificationObserver observer( - content::NOTIFICATION_LOAD_STOP, - content::NotificationService::AllSources()); - observer.AddNotificationType( - content::NOTIFICATION_WEB_CONTENTS_DESTROYED, - content::NotificationService::AllSources()); - observer.Wait(); - - // Test activity may have modified the set of extension processes during - // message processing, so re-start the iteration to catch added/removed - // processes. - all_views = manager->GetAllViews(); - iter = all_views.begin(); - } - } - return true; -} - -bool ExtensionBrowserTest::WaitForExtensionInstall() { - int before = extension_installs_observed_; - WaitForNotification(chrome::NOTIFICATION_EXTENSION_INSTALLED); - return extension_installs_observed_ == (before + 1); -} - -bool ExtensionBrowserTest::WaitForExtensionInstallError() { - int before = extension_installs_observed_; - content::WindowedNotificationObserver( - chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR, - content::NotificationService::AllSources()).Wait(); - return extension_installs_observed_ == before; -} - -void ExtensionBrowserTest::WaitForExtensionLoad() { - WaitForNotification(chrome::NOTIFICATION_EXTENSION_LOADED); - WaitForExtensionViewsToLoad(); -} - -bool ExtensionBrowserTest::WaitForExtensionLoadError() { - int before = extension_load_errors_observed_; - WaitForNotification(chrome::NOTIFICATION_EXTENSION_LOAD_ERROR); - return extension_load_errors_observed_ != before; -} - -bool ExtensionBrowserTest::WaitForExtensionCrash( - const std::string& extension_id) { - ExtensionService* service = - extensions::ExtensionSystem::Get(profile())->extension_service(); - - if (!service->GetExtensionById(extension_id, true)) { - // The extension is already unloaded, presumably due to a crash. - return true; - } - content::WindowedNotificationObserver( - chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED, - content::NotificationService::AllSources()).Wait(); - return (service->GetExtensionById(extension_id, true) == NULL); -} - -bool ExtensionBrowserTest::WaitForCrxInstallerDone() { - int before = crx_installers_done_observed_; - WaitForNotification(chrome::NOTIFICATION_CRX_INSTALLER_DONE); - return crx_installers_done_observed_ == (before + 1); -} - void ExtensionBrowserTest::OpenWindow(content::WebContents* contents, const GURL& url, bool newtab_process_should_equal_opener, content::WebContents** newtab_result) { - content::WindowedNotificationObserver observer( + content::WindowedNotificationObserver windowed_observer( content::NOTIFICATION_LOAD_STOP, content::NotificationService::AllSources()); ASSERT_TRUE(content::ExecuteScript(contents, @@ -661,9 +521,10 @@ void ExtensionBrowserTest::OpenWindow(content::WebContents* contents, // The above window.open call is not user-initiated, so it will create // a popup window instead of a new tab in current window. // The stop notification will come from the new tab. - observer.Wait(); + windowed_observer.Wait(); content::NavigationController* controller = - content::Source<content::NavigationController>(observer.source()).ptr(); + content::Source<content::NavigationController>( + windowed_observer.source()).ptr(); content::WebContents* newtab = controller->GetWebContents(); ASSERT_TRUE(newtab); EXPECT_EQ(url, controller->GetLastCommittedEntry()->GetURL()); @@ -679,7 +540,7 @@ void ExtensionBrowserTest::OpenWindow(content::WebContents* contents, void ExtensionBrowserTest::NavigateInRenderer(content::WebContents* contents, const GURL& url) { bool result = false; - content::WindowedNotificationObserver observer( + content::WindowedNotificationObserver windowed_observer( content::NOTIFICATION_LOAD_STOP, content::NotificationService::AllSources()); ASSERT_TRUE(content::ExecuteScriptAndExtractBool( @@ -690,7 +551,7 @@ void ExtensionBrowserTest::NavigateInRenderer(content::WebContents* contents, "window.location = '" + url.spec() + "';", &result)); ASSERT_TRUE(result); - observer.Wait(); + windowed_observer.Wait(); EXPECT_EQ(url, contents->GetController().GetLastCommittedEntry()->GetURL()); } @@ -720,43 +581,3 @@ std::string ExtensionBrowserTest::ExecuteScriptInBackgroundPage( return extensions::browsertest_util::ExecuteScriptInBackgroundPage( profile(), extension_id, script); } - -void ExtensionBrowserTest::Observe( - int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) { - switch (type) { - case chrome::NOTIFICATION_EXTENSION_LOADED: - last_loaded_extension_id_ = - content::Details<const Extension>(details).ptr()->id(); - VLOG(1) << "Got EXTENSION_LOADED notification."; - break; - - case chrome::NOTIFICATION_CRX_INSTALLER_DONE: - VLOG(1) << "Got CRX_INSTALLER_DONE notification."; - { - const Extension* extension = - content::Details<const Extension>(details).ptr(); - if (extension) - last_loaded_extension_id_ = extension->id(); - else - last_loaded_extension_id_.clear(); - } - ++crx_installers_done_observed_; - break; - - case chrome::NOTIFICATION_EXTENSION_INSTALLED: - VLOG(1) << "Got EXTENSION_INSTALLED notification."; - ++extension_installs_observed_; - break; - - case chrome::NOTIFICATION_EXTENSION_LOAD_ERROR: - VLOG(1) << "Got EXTENSION_LOAD_ERROR notification."; - ++extension_load_errors_observed_; - break; - - default: - NOTREACHED(); - break; - } -} diff --git a/chrome/browser/extensions/extension_browsertest.h b/chrome/browser/extensions/extension_browsertest.h index ad127c3932..ff930ea28f 100644 --- a/chrome/browser/extensions/extension_browsertest.h +++ b/chrome/browser/extensions/extension_browsertest.h @@ -8,19 +8,17 @@ #include <string> #include "base/command_line.h" -#include "base/compiler_specific.h" + #include "base/files/file_path.h" #include "base/files/scoped_temp_dir.h" #include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_test_notification_observer.h" #include "chrome/browser/ui/browser.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/feature_switch.h" #include "chrome/common/extensions/features/feature_channel.h" #include "chrome/test/base/in_process_browser_test.h" -#include "content/public/browser/notification_details.h" -#include "content/public/browser/notification_observer.h" -#include "content/public/browser/notification_types.h" #include "content/public/browser/web_contents.h" #include "extensions/common/manifest.h" @@ -31,8 +29,7 @@ class Profile; // Base class for extension browser tests. Provides utilities for loading, // unloading, and installing extensions. -class ExtensionBrowserTest : virtual public InProcessBrowserTest, - public content::NotificationObserver { +class ExtensionBrowserTest : virtual public InProcessBrowserTest { protected: // Flags used to configure how the tests are run. enum Flags { @@ -61,6 +58,10 @@ class ExtensionBrowserTest : virtual public InProcessBrowserTest, return extensions::ExtensionSystem::Get(profile())->extension_service(); } + const std::string& last_loaded_extension_id() { + return observer_->last_loaded_extension_id(); + } + // Get the profile to use. Profile* profile(); @@ -194,32 +195,58 @@ class ExtensionBrowserTest : virtual public InProcessBrowserTest, void EnableExtension(const std::string& extension_id); // Wait for the total number of page actions to change to |count|. - bool WaitForPageActionCountChangeTo(int count); + bool WaitForPageActionCountChangeTo(int count) { + return observer_->WaitForPageActionCountChangeTo(count); + } // Wait for the number of visible page actions to change to |count|. - bool WaitForPageActionVisibilityChangeTo(int count); + bool WaitForPageActionVisibilityChangeTo(int count) { + return observer_->WaitForPageActionVisibilityChangeTo(count); + } // Waits until an extension is installed and loaded. Returns true if an // install happened before timeout. - bool WaitForExtensionInstall(); + bool WaitForExtensionInstall() { + return observer_->WaitForExtensionInstall(); + } // Wait for an extension install error to be raised. Returns true if an // error was raised. - bool WaitForExtensionInstallError(); + bool WaitForExtensionInstallError() { + return observer_->WaitForExtensionInstallError(); + } + + // Waits until an extension is loaded and all view have loaded. + void WaitForExtensionAndViewLoad() { + return observer_->WaitForExtensionAndViewLoad(); + } // Waits until an extension is loaded. - void WaitForExtensionLoad(); + void WaitForExtensionLoad() { + return observer_->WaitForExtensionLoad(); + } // Waits for an extension load error. Returns true if the error really // happened. - bool WaitForExtensionLoadError(); + bool WaitForExtensionLoadError() { + return observer_->WaitForExtensionLoadError(); + } // Wait for the specified extension to crash. Returns true if it really // crashed. - bool WaitForExtensionCrash(const std::string& extension_id); + bool WaitForExtensionCrash(const std::string& extension_id) { + return observer_->WaitForExtensionCrash(extension_id); + } // Wait for the crx installer to be done. Returns true if it really is done. - bool WaitForCrxInstallerDone(); + bool WaitForCrxInstallerDone() { + return observer_->WaitForCrxInstallerDone(); + } + + // Wait for all extension views to load. + bool WaitForExtensionViewsToLoad() { + return observer_->WaitForExtensionViewsToLoad(); + } // Simulates a page calling window.open on an URL and waits for the // navigation. @@ -238,27 +265,19 @@ class ExtensionBrowserTest : virtual public InProcessBrowserTest, extensions::ExtensionHost* FindHostWithPath(ExtensionProcessManager* manager, const std::string& path, int expected_hosts); - // Returns // extensions::browsertest_util::ExecuteScriptInBackgroundPage(profile(), // extension_id, script). std::string ExecuteScriptInBackgroundPage(const std::string& extension_id, const std::string& script); - // content::NotificationObserver - virtual void Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) OVERRIDE; - bool loaded_; bool installed_; // test_data/extensions. base::FilePath test_data_dir_; - std::string last_loaded_extension_id_; - int extension_installs_observed_; - int extension_load_errors_observed_; - int crx_installers_done_observed_; + + scoped_ptr<ExtensionTestNotificationObserver> observer_; private: // Temporary directory for testing. @@ -301,12 +320,6 @@ class ExtensionBrowserTest : virtual public InProcessBrowserTest, extensions::Extension::InitFromValueFlags creation_flags, bool wait_for_idle); - // Wait for a notification of the specified type to be sent. - // |notification_type| must be a type that this class handles in Observe(). - void WaitForNotification(int notification_type); - - bool WaitForExtensionViewsToLoad(); - // Make the current channel "dev" for the duration of the test. extensions::ScopedCurrentChannel current_channel_; diff --git a/chrome/browser/extensions/extension_commands_global_registry_apitest.cc b/chrome/browser/extensions/extension_commands_global_registry_apitest.cc new file mode 100644 index 0000000000..85f3ac40c6 --- /dev/null +++ b/chrome/browser/extensions/extension_commands_global_registry_apitest.cc @@ -0,0 +1,128 @@ +// Copyright (c) 2013 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/extensions/extension_apitest.h" +#include "chrome/browser/extensions/window_controller.h" +#include "chrome/browser/ui/browser_window.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/test/base/interactive_test_utils.h" +#include "content/public/test/browser_test_utils.h" +#include "ui/base/base_window.h" + +#if defined(TOOLKIT_GTK) +#include <X11/Xlib.h> +#include <X11/extensions/XTest.h> +#include <X11/keysym.h> + +#include "ui/events/keycodes/keyboard_code_conversion_x.h" +#include "ui/gfx/x/x11_types.h" +#endif + +namespace extensions { + +typedef ExtensionApiTest GlobalCommandsApiTest; + +#if defined(TOOLKIT_GTK) +// Send a simulated key press and release event, where |control|, |shift| or +// |alt| indicates whether the key is struck with corresponding modifier. +void SendNativeKeyEventToXDisplay(ui::KeyboardCode key, + bool control, + bool shift, + bool alt) { + Display* display = gfx::GetXDisplay(); + KeyCode ctrl_key_code = XKeysymToKeycode(display, XK_Control_L); + KeyCode shift_key_code = XKeysymToKeycode(display, XK_Shift_L); + KeyCode alt_key_code = XKeysymToKeycode(display, XK_Alt_L); + + // Release modifiers first of all to make sure this function can work as + // expected. For example, when |control| is false, but the status of Ctrl key + // is down, we will generate a keyboard event with unwanted Ctrl key. + XTestFakeKeyEvent(display, ctrl_key_code, False, CurrentTime); + XTestFakeKeyEvent(display, shift_key_code, False, CurrentTime); + XTestFakeKeyEvent(display, alt_key_code, False, CurrentTime); + + typedef std::vector<KeyCode> KeyCodes; + KeyCodes key_codes; + if (control) + key_codes.push_back(ctrl_key_code); + if (shift) + key_codes.push_back(shift_key_code); + if (alt) + key_codes.push_back(alt_key_code); + + key_codes.push_back(XKeysymToKeycode(display, + XKeysymForWindowsKeyCode(key, false))); + + // Simulate the keys being pressed. + for (KeyCodes::iterator it = key_codes.begin(); it != key_codes.end(); it++) + XTestFakeKeyEvent(display, *it, True, CurrentTime); + + // Simulate the keys being released. + for (KeyCodes::iterator it = key_codes.begin(); it != key_codes.end(); it++) + XTestFakeKeyEvent(display, *it, False, CurrentTime); + + XFlush(display); +} +#endif // TOOLKIT_GTK + +#if defined(OS_WIN) || defined(TOOLKIT_GTK) +// The feature is only fully implemented on Windows and Linux GTK+, other +// platforms coming. +#define MAYBE_GlobalCommand GlobalCommand +#else +#define MAYBE_GlobalCommand DISABLED_GlobalCommand +#endif + +// Test the basics of global commands and make sure they work when Chrome +// doesn't have focus. Also test that non-global commands are not treated as +// global and that keys beyond Ctrl+Shift+[0..9] cannot be auto-assigned by an +// extension. +IN_PROC_BROWSER_TEST_F(GlobalCommandsApiTest, MAYBE_GlobalCommand) { + FeatureSwitch::ScopedOverride enable_global_commands( + FeatureSwitch::global_commands(), true); + + // Load the extension in the non-incognito browser. + ResultCatcher catcher; + ASSERT_TRUE(RunExtensionTest("keybinding/global")) << message_; + ASSERT_TRUE(catcher.GetNextResult()); + +#if !defined(TOOLKIT_GTK) + // Our infrastructure for sending keys expects a browser to send them to, but + // to properly test global shortcuts you need to send them to another target. + // So, create an incognito browser to use as a target to send the shortcuts + // to. It will ignore all of them and allow us test whether the global + // shortcut really is global in nature and also that the non-global shortcut + // is non-global. + Browser* incognito_browser = CreateIncognitoBrowser(); + + // Try to activate the non-global shortcut (Ctrl+Shift+1) and the + // non-assignable shortcut (Ctrl+Shift+A) by sending the keystrokes to the + // incognito browser. Both shortcuts should have no effect (extension is not + // loaded there). + ASSERT_TRUE(ui_test_utils::SendKeyPressSync( + incognito_browser, ui::VKEY_1, true, true, false, false)); + ASSERT_TRUE(ui_test_utils::SendKeyPressSync( + incognito_browser, ui::VKEY_A, true, true, false, false)); + + // Activate the shortcut (Ctrl+Shift+9). This should have an effect. + ASSERT_TRUE(ui_test_utils::SendKeyPressSync( + incognito_browser, ui::VKEY_9, true, true, false, false)); +#else + // On Linux GTK+, our infrastructure for sending keys just synthesize keyboard + // event and send them directly to the specified window, without notifying the + // X root window. It didn't work while testing global shortcut because the + // stuff of global shortcut on Linux need to be notified when KeyPress event + // is happening on X root window. So we simulate the keyboard input here. + SendNativeKeyEventToXDisplay(ui::VKEY_1, true, true, false); + SendNativeKeyEventToXDisplay(ui::VKEY_A, true, true, false); + SendNativeKeyEventToXDisplay(ui::VKEY_9, true, true, false); +#endif + + // If this fails, it might be because the global shortcut failed to work, + // but it might also be because the non-global shortcuts unexpectedly + // worked. + ASSERT_TRUE(catcher.GetNextResult()) << catcher.message(); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/extension_disabled_ui.cc b/chrome/browser/extensions/extension_disabled_ui.cc index 42767a1bf1..0192142a32 100644 --- a/chrome/browser/extensions/extension_disabled_ui.cc +++ b/chrome/browser/extensions/extension_disabled_ui.cc @@ -34,6 +34,7 @@ #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" #include "content/public/browser/notification_source.h" +#include "extensions/common/permissions/permission_message_provider.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" @@ -265,8 +266,8 @@ std::vector<string16> ExtensionDisabledGlobalError::GetBubbleViewMessages() { messages.push_back(l10n_util::GetStringUTF16( IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO)); std::vector<string16> permission_warnings = - extension_->GetActivePermissions()->GetWarningMessages( - extension_->GetType()); + extensions::PermissionMessageProvider::Get()->GetWarningMessages( + extension_->GetActivePermissions(), extension_->GetType()); for (size_t i = 0; i < permission_warnings.size(); ++i) { messages.push_back(l10n_util::GetStringFUTF16( IDS_EXTENSION_PERMISSION_LINE, permission_warnings[i])); diff --git a/chrome/browser/extensions/extension_function.cc b/chrome/browser/extensions/extension_function.cc index 766a10687d..5b5ea2a365 100644 --- a/chrome/browser/extensions/extension_function.cc +++ b/chrome/browser/extensions/extension_function.cc @@ -21,9 +21,12 @@ #include "content/public/browser/notification_types.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_observer.h" using content::BrowserThread; using content::RenderViewHost; +using content::WebContents; using extensions::ExtensionAPI; using extensions::Feature; @@ -32,25 +35,36 @@ void ExtensionFunctionDeleteTraits::Destruct(const ExtensionFunction* x) { x->Destruct(); } -UIThreadExtensionFunction::RenderViewHostTracker::RenderViewHostTracker( - UIThreadExtensionFunction* function) - : content::RenderViewHostObserver(function->render_view_host()), - function_(function) { -} +// Helper class to track the lifetime of ExtensionFunction's RenderViewHost +// pointer and NULL it out when it dies. It also allows us to filter IPC +// messages coming from the RenderViewHost. +class UIThreadExtensionFunction::RenderViewHostTracker + : public content::WebContentsObserver { + public: + explicit RenderViewHostTracker(UIThreadExtensionFunction* function) + : content::WebContentsObserver( + WebContents::FromRenderViewHost(function->render_view_host())), + function_(function) { + } -void UIThreadExtensionFunction::RenderViewHostTracker::RenderViewHostDestroyed( - RenderViewHost* render_view_host) { - // Overidding the default behavior of RenderViewHostObserver which is to - // delete this. In our case, we'll be deleted when the - // UIThreadExtensionFunction that contains us goes away. + private: + // content::WebContentsObserver: + virtual void RenderViewDeleted( + content::RenderViewHost* render_view_host) OVERRIDE { + if (render_view_host != function_->render_view_host()) + return; - function_->SetRenderViewHost(NULL); -} + function_->SetRenderViewHost(NULL); + } -bool UIThreadExtensionFunction::RenderViewHostTracker::OnMessageReceived( - const IPC::Message& message) { - return function_->OnMessageReceivedFromRenderView(message); -} + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { + return function_->OnMessageReceivedFromRenderView(message); + } + + UIThreadExtensionFunction* function_; + + DISALLOW_COPY_AND_ASSIGN(RenderViewHostTracker); +}; ExtensionFunction::ExtensionFunction() : request_id_(-1), diff --git a/chrome/browser/extensions/extension_function.h b/chrome/browser/extensions/extension_function.h index 8ffee2e63f..d26ae1e6e7 100644 --- a/chrome/browser/extensions/extension_function.h +++ b/chrome/browser/extensions/extension_function.h @@ -19,7 +19,6 @@ #include "chrome/browser/extensions/extension_info_map.h" #include "chrome/common/extensions/extension.h" #include "content/public/browser/browser_thread.h" -#include "content/public/browser/render_view_host_observer.h" #include "content/public/common/console_message_level.h" #include "ipc/ipc_message.h" @@ -368,23 +367,7 @@ class UIThreadExtensionFunction : public ExtensionFunction { Profile* profile_; private: - // Helper class to track the lifetime of ExtensionFunction's RenderViewHost - // pointer and NULL it out when it dies. It also allows us to filter IPC - // messages coming from the RenderViewHost. - class RenderViewHostTracker : public content::RenderViewHostObserver { - public: - explicit RenderViewHostTracker(UIThreadExtensionFunction* function); - - private: - // content::RenderViewHostObserver: - virtual void RenderViewHostDestroyed( - content::RenderViewHost* render_view_host) OVERRIDE; - virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; - - UIThreadExtensionFunction* function_; - - DISALLOW_COPY_AND_ASSIGN(RenderViewHostTracker); - }; + class RenderViewHostTracker; virtual void Destruct() const OVERRIDE; diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc index 056dda4b8f..85961e3012 100644 --- a/chrome/browser/extensions/extension_function_dispatcher.cc +++ b/chrome/browser/extensions/extension_function_dispatcher.cc @@ -18,6 +18,7 @@ #include "chrome/browser/extensions/extension_function_registry.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/extension_web_ui.h" #include "chrome/browser/extensions/extensions_quota_service.h" #include "chrome/browser/extensions/process_map.h" @@ -31,7 +32,8 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" -#include "content/public/browser/render_view_host_observer.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_observer.h" #include "content/public/browser/user_metrics.h" #include "content/public/common/result_codes.h" #include "ipc/ipc_message.h" @@ -144,30 +146,34 @@ void IOThreadResponseCallback( } // namespace class ExtensionFunctionDispatcher::UIThreadResponseCallbackWrapper - : public content::RenderViewHostObserver { + : public content::WebContentsObserver { public: UIThreadResponseCallbackWrapper( const base::WeakPtr<ExtensionFunctionDispatcher>& dispatcher, RenderViewHost* render_view_host) - : content::RenderViewHostObserver(render_view_host), + : content::WebContentsObserver( + content::WebContents::FromRenderViewHost(render_view_host)), dispatcher_(dispatcher), + render_view_host_(render_view_host), weak_ptr_factory_(this) { } virtual ~UIThreadResponseCallbackWrapper() { } - // content::RenderViewHostObserver overrides. - virtual void RenderViewHostDestroyed( + // content::WebContentsObserver overrides. + virtual void RenderViewDeleted( RenderViewHost* render_view_host) OVERRIDE { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (render_view_host != render_view_host_) + return; + if (dispatcher_.get()) { dispatcher_->ui_thread_response_callback_wrappers_ .erase(render_view_host); } - // This call will delete |this|. - content::RenderViewHostObserver::RenderViewHostDestroyed(render_view_host); + delete this; } ExtensionFunction::ResponseCallback CreateCallback(int request_id) { @@ -183,12 +189,13 @@ class ExtensionFunctionDispatcher::UIThreadResponseCallbackWrapper const base::ListValue& results, const std::string& error) { CommonResponseCallback( - render_view_host(), render_view_host()->GetRoutingID(), - render_view_host()->GetProcess()->GetHandle(), request_id, type, + render_view_host_, render_view_host_->GetRoutingID(), + render_view_host_->GetProcess()->GetHandle(), request_id, type, results, error); } base::WeakPtr<ExtensionFunctionDispatcher> dispatcher_; + content::RenderViewHost* render_view_host_; base::WeakPtrFactory<UIThreadResponseCallbackWrapper> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(UIThreadResponseCallbackWrapper); @@ -346,7 +353,8 @@ void ExtensionFunctionDispatcher::DispatchWithCallback( function_ui->SetRenderViewHost(render_view_host); function_ui->set_dispatcher(AsWeakPtr()); function_ui->set_profile(profile_); - function->set_include_incognito(service->CanCrossIncognito(extension)); + function->set_include_incognito(extension_util::CanCrossIncognito(extension, + service)); if (!CheckPermissions(function.get(), extension, params, callback)) return; diff --git a/chrome/browser/extensions/extension_function_histogram_value.h b/chrome/browser/extensions/extension_function_histogram_value.h index a62b014712..59f7aae3af 100644 --- a/chrome/browser/extensions/extension_function_histogram_value.h +++ b/chrome/browser/extensions/extension_function_histogram_value.h @@ -660,6 +660,19 @@ enum HistogramValue { SOCKETS_TCP_CLOSE, SOCKETS_TCP_GETINFO, SOCKETS_TCP_GETSOCKETS, + NETWORKINGPRIVATE_GETENABLEDNETWORKTYPES, + NETWORKINGPRIVATE_ENABLENETWORKTYPE, + NETWORKINGPRIVATE_DISABLENETWORKTYPE, + SOCKETS_TCP_SERVER_CREATE, + SOCKETS_TCP_SERVER_UPDATE, + SOCKETS_TCP_SERVER_SETPAUSED, + SOCKETS_TCP_SERVER_LISTEN, + SOCKETS_TCP_SERVER_DISCONNECT, + SOCKETS_TCP_SERVER_CLOSE, + SOCKETS_TCP_SERVER_GETINFO, + SOCKETS_TCP_SERVER_GETSOCKETS, + SYSTEM_STORAGE_GETAVAILABLECAPACITY, + BROWSERACTION_OPEN_POPUP, ENUM_BOUNDARY // Last entry: Add new entries above. }; diff --git a/chrome/browser/extensions/extension_functional_browsertest.cc b/chrome/browser/extensions/extension_functional_browsertest.cc index 19c7e0ad14..cf43c565a3 100644 --- a/chrome/browser/extensions/extension_functional_browsertest.cc +++ b/chrome/browser/extensions/extension_functional_browsertest.cc @@ -6,6 +6,7 @@ #include "chrome/browser/extensions/crx_installer.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser_commands.h" #include "chrome/test/base/ui_test_utils.h" @@ -34,22 +35,19 @@ public: installer->set_off_store_install_allow_reason( extensions::CrxInstaller::OffStoreInstallAllowedInTest); - content::WindowedNotificationObserver observer( + observer_->Watch( chrome::NOTIFICATION_CRX_INSTALLER_DONE, content::Source<extensions::CrxInstaller>(installer.get())); - content::NotificationRegistrar registrar; - registrar.Add(this, chrome::NOTIFICATION_CRX_INSTALLER_DONE, - content::Source<extensions::CrxInstaller>(installer.get())); installer->InstallCrx(path); - observer.Wait(); + observer_->Wait(); size_t num_after = service->extensions()->size(); EXPECT_EQ(num_before + 1, num_after); extension_loaded_observer.Wait(); const Extension* extension = service->GetExtensionById( - last_loaded_extension_id_, false); + last_loaded_extension_id(), false); EXPECT_TRUE(extension != NULL); } }; @@ -64,8 +62,9 @@ IN_PROC_BROWSER_TEST_F(ExtensionFunctionalTest, TestAdblockExtensionCrash) { ExtensionService* service = profile()->GetExtensionService(); // Verify that the extension is enabled and allowed in incognito // is disabled. - EXPECT_TRUE(service->IsExtensionEnabled(last_loaded_extension_id_)); - EXPECT_FALSE(service->IsIncognitoEnabled(last_loaded_extension_id_)); + EXPECT_TRUE(service->IsExtensionEnabled(last_loaded_extension_id())); + EXPECT_FALSE( + extension_util::IsIncognitoEnabled(last_loaded_extension_id(), service)); } IN_PROC_BROWSER_TEST_F(ExtensionFunctionalTest, TestSetExtensionsState) { @@ -73,24 +72,30 @@ IN_PROC_BROWSER_TEST_F(ExtensionFunctionalTest, TestSetExtensionsState) { InstallExtensionSilently(service, "google_talk.crx"); // Disable the extension and verify. - service->SetIsIncognitoEnabled(last_loaded_extension_id_, false); - service->DisableExtension(last_loaded_extension_id_, + extension_util::SetIsIncognitoEnabled( + last_loaded_extension_id(), service, false); + service->DisableExtension(last_loaded_extension_id(), Extension::DISABLE_USER_ACTION); - EXPECT_FALSE(service->IsExtensionEnabled(last_loaded_extension_id_)); + EXPECT_FALSE(service->IsExtensionEnabled(last_loaded_extension_id())); // Enable the extension and verify. - service->SetIsIncognitoEnabled(last_loaded_extension_id_, false); - service->EnableExtension(last_loaded_extension_id_); - EXPECT_TRUE(service->IsExtensionEnabled(last_loaded_extension_id_)); + extension_util::SetIsIncognitoEnabled( + last_loaded_extension_id(), service, false); + service->EnableExtension(last_loaded_extension_id()); + EXPECT_TRUE(service->IsExtensionEnabled(last_loaded_extension_id())); // Allow extension in incognito mode and verify. - service->EnableExtension(last_loaded_extension_id_); - service->SetIsIncognitoEnabled(last_loaded_extension_id_, true); - EXPECT_TRUE(service->IsIncognitoEnabled(last_loaded_extension_id_)); + service->EnableExtension(last_loaded_extension_id()); + extension_util::SetIsIncognitoEnabled( + last_loaded_extension_id(), service, true); + EXPECT_TRUE( + extension_util::IsIncognitoEnabled(last_loaded_extension_id(), service)); // Disallow extension in incognito mode and verify. - service->EnableExtension(last_loaded_extension_id_); - service->SetIsIncognitoEnabled(last_loaded_extension_id_, false); - EXPECT_FALSE(service->IsIncognitoEnabled(last_loaded_extension_id_)); + service->EnableExtension(last_loaded_extension_id()); + extension_util::SetIsIncognitoEnabled( + last_loaded_extension_id(), service, false); + EXPECT_FALSE( + extension_util::IsIncognitoEnabled(last_loaded_extension_id(), service)); } } // namespace extensions diff --git a/chrome/browser/extensions/extension_host.cc b/chrome/browser/extensions/extension_host.cc index 59dd814429..b859c41797 100644 --- a/chrome/browser/extensions/extension_host.cc +++ b/chrome/browser/extensions/extension_host.cc @@ -22,6 +22,7 @@ #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/extensions/extension_tab_util.h" +#include "chrome/browser/extensions/extension_web_contents_observer.h" #include "chrome/browser/extensions/window_controller.h" #include "chrome/browser/file_select_helper.h" #include "chrome/browser/media/media_capture_devices_dispatcher.h" @@ -155,6 +156,7 @@ ExtensionHost::ExtensionHost(const Extension* extension, host_contents_->SetDelegate(this); SetViewType(host_contents_.get(), host_type); + ExtensionWebContentsObserver::CreateForWebContents(host_contents()); PrefsTabHelper::CreateForWebContents(host_contents()); render_view_host_ = host_contents_->GetRenderViewHost(); diff --git a/chrome/browser/extensions/extension_install_prompt.cc b/chrome/browser/extensions/extension_install_prompt.cc index ae201f1034..66c7ab9f98 100644 --- a/chrome/browser/extensions/extension_install_prompt.cc +++ b/chrome/browser/extensions/extension_install_prompt.cc @@ -36,6 +36,7 @@ #include "extensions/common/extension_resource.h" #include "extensions/common/manifest.h" #include "extensions/common/manifest_constants.h" +#include "extensions/common/permissions/permission_message_provider.h" #include "extensions/common/url_pattern.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" @@ -801,9 +802,11 @@ void ExtensionInstallPrompt::ShowConfirmation() { Manifest::Type extension_type = extension_ ? extension_->GetType() : Manifest::TYPE_UNKNOWN; prompt_.SetPermissions( - permissions_->GetWarningMessages(extension_type)); + extensions::PermissionMessageProvider::Get()-> + GetWarningMessages(permissions_, extension_type)); prompt_.SetPermissionsDetails( - permissions_->GetWarningMessagesDetails(extension_type)); + extensions::PermissionMessageProvider::Get()-> + GetWarningMessagesDetails(permissions_, extension_type)); } switch (prompt_.type()) { diff --git a/chrome/browser/extensions/extension_install_ui_browsertest.cc b/chrome/browser/extensions/extension_install_ui_browsertest.cc index 6a1aac8728..615584e654 100644 --- a/chrome/browser/extensions/extension_install_ui_browsertest.cc +++ b/chrome/browser/extensions/extension_install_ui_browsertest.cc @@ -186,7 +186,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionInstallUIBrowserTest, } } -class NewTabUISortingBrowserTest : public ExtensionInstallUIBrowserTest { +class NewTabUISortingBrowserTest : public ExtensionInstallUIBrowserTest, + public content::NotificationObserver { public: NewTabUISortingBrowserTest() {} @@ -194,7 +195,7 @@ class NewTabUISortingBrowserTest : public ExtensionInstallUIBrowserTest { const content::NotificationSource& source, const content::NotificationDetails& details) OVERRIDE { if (type != chrome::NOTIFICATION_EXTENSION_LAUNCHER_REORDERED) { - ExtensionInstallUIBrowserTest::Observe(type, source, details); + observer_->Observe(type, source, details); return; } const std::string* id = content::Details<const std::string>(details).ptr(); diff --git a/chrome/browser/extensions/extension_keybinding_apitest_new.cc b/chrome/browser/extensions/extension_keybinding_apitest_new.cc deleted file mode 100644 index 848a799b49..0000000000 --- a/chrome/browser/extensions/extension_keybinding_apitest_new.cc +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright (c) 2012 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/extensions/active_tab_permission_granter.h" -#include "chrome/browser/extensions/browser_action_test_util.h" -#include "chrome/browser/extensions/extension_action.h" -#include "chrome/browser/extensions/extension_action_manager.h" -#include "chrome/browser/extensions/extension_apitest.h" -#include "chrome/browser/extensions/tab_helper.h" -#include "chrome/browser/sessions/session_tab_helper.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/tabs/tab_strip_model.h" -#include "chrome/common/chrome_switches.h" -#include "chrome/common/extensions/extension.h" -#include "chrome/test/base/interactive_test_utils.h" -#include "chrome/test/base/ui_test_utils.h" -#include "content/public/browser/notification_service.h" -#include "content/public/browser/web_contents.h" -#include "content/public/test/browser_test_utils.h" - -using content::WebContents; - -namespace extensions { - -class CommandsApiTest : public ExtensionApiTest { - public: - CommandsApiTest() {} - virtual ~CommandsApiTest() {} - - protected: - BrowserActionTestUtil GetBrowserActionsBar() { - return BrowserActionTestUtil(browser()); - } -}; - -class ScriptBadgesCommandsApiTest : public ExtensionApiTest { - public: - ScriptBadgesCommandsApiTest() { - // We cannot add this to CommandsApiTest because then PageActions get - // treated like BrowserActions and the PageAction test starts failing. - CommandLine::ForCurrentProcess()->AppendSwitchASCII( - switches::kScriptBadges, "1"); - } - virtual ~ScriptBadgesCommandsApiTest() {} - - bool IsGrantedForTab(const Extension* extension, - const content::WebContents* web_contents) { - return PermissionsData::HasAPIPermissionForTab( - extension, - SessionID::IdForTab(web_contents), - APIPermission::kTab); - } -}; - -// Test the basic functionality of the Keybinding API: -// - That pressing the shortcut keys should perform actions (activate the -// browser action or send an event). -// - Note: Page action keybindings are tested in PageAction test below. -// - The shortcut keys taken by one extension are not overwritten by the last -// installed extension. -IN_PROC_BROWSER_TEST_F(CommandsApiTest, Basic) { - ASSERT_TRUE(test_server()->Start()); - ASSERT_TRUE(RunExtensionTest("keybinding/basics")) << message_; - const Extension* extension = GetSingleLoadedExtension(); - ASSERT_TRUE(extension) << message_; - - // Load this extension, which uses the same keybindings but sets the page - // to different colors. This is so we can see that it doesn't interfere. We - // don't test this extension in any other way (it should otherwise be - // immaterial to this test). - ASSERT_TRUE(RunExtensionTest("keybinding/conflicting")) << message_; - - // Test that there are two browser actions in the toolbar. - ASSERT_EQ(2, GetBrowserActionsBar().NumberOfBrowserActions()); - - ui_test_utils::NavigateToURL(browser(), - test_server()->GetURL("files/extensions/test_file.txt")); - - // activeTab shouldn't have been granted yet. - WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents(); - ASSERT_TRUE(tab); - - EXPECT_FALSE(IsGrantedForTab(extension, tab)); - - // Activate the shortcut (Ctrl+Shift+F). - ASSERT_TRUE(ui_test_utils::SendKeyPressSync( - browser(), ui::VKEY_F, true, true, false, false)); - - // activeTab should now be granted. - EXPECT_TRUE(IsGrantedForTab(extension, tab)); - - // Verify the command worked. - bool result = false; - ASSERT_TRUE(content::ExecuteScriptAndExtractBool( - tab, - "setInterval(function(){" - " if(document.body.bgColor == 'red'){" - " window.domAutomationController.send(true)}}, 100)", - &result)); - ASSERT_TRUE(result); - - // Activate the shortcut (Ctrl+Shift+Y). - ASSERT_TRUE(ui_test_utils::SendKeyPressSync( - browser(), ui::VKEY_Y, true, true, false, false)); - - result = false; - ASSERT_TRUE(content::ExecuteScriptAndExtractBool( - tab, - "setInterval(function(){" - " if(document.body.bgColor == 'blue'){" - " window.domAutomationController.send(true)}}, 100)", - &result)); - ASSERT_TRUE(result); -} - -// Flaky on linux and chromeos, http://crbug.com/165825 -#if defined(OS_MACOSX) || defined(OS_WIN) -#define MAYBE_PageAction PageAction -#else -#define MAYBE_PageAction DISABLED_PageAction -#endif -IN_PROC_BROWSER_TEST_F(CommandsApiTest, MAYBE_PageAction) { - ASSERT_TRUE(test_server()->Start()); - ASSERT_TRUE(RunExtensionTest("keybinding/page_action")) << message_; - const Extension* extension = GetSingleLoadedExtension(); - ASSERT_TRUE(extension) << message_; - - { - // Load a page, the extension will detect the navigation and request to show - // the page action icon. - ResultCatcher catcher; - ui_test_utils::NavigateToURL(browser(), - test_server()->GetURL("files/extensions/test_file.txt")); - ASSERT_TRUE(catcher.GetNextResult()); - } - - // Make sure it appears and is the right one. - ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1)); - int tab_id = SessionTabHelper::FromWebContents( - browser()->tab_strip_model()->GetActiveWebContents())->session_id().id(); - ExtensionAction* action = - ExtensionActionManager::Get(browser()->profile())-> - GetPageAction(*extension); - ASSERT_TRUE(action); - EXPECT_EQ("Make this page red", action->GetTitle(tab_id)); - - // Activate the shortcut (Alt+Shift+F). - ASSERT_TRUE(ui_test_utils::SendKeyPressSync( - browser(), ui::VKEY_F, false, true, true, false)); - - // Verify the command worked (the page action turns the page red). - WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents(); - bool result = false; - ASSERT_TRUE(content::ExecuteScriptAndExtractBool( - tab, - "setInterval(function(){" - " if(document.body.bgColor == 'red'){" - " window.domAutomationController.send(true)}}, 100)", - &result)); - ASSERT_TRUE(result); -} - -// Checked-in in a disabled state, because the necessary functionality to -// automatically verify that the test works hasn't been implemented for the -// script badges yet (see http://crbug.com/140016). The test results, can be -// verified manually by running the test and verifying that the synthesized -// popup for script badges appear. When bug 140016 has been fixed, the popup -// code can signal to the test that the test passed. -// TODO(finnur): Enable this test once the bug is fixed. -IN_PROC_BROWSER_TEST_F(ScriptBadgesCommandsApiTest, DISABLED_ScriptBadge) { - ASSERT_TRUE(test_server()->Start()); - ASSERT_TRUE(RunExtensionTest("keybinding/script_badge")) << message_; - const Extension* extension = GetSingleLoadedExtension(); - ASSERT_TRUE(extension) << message_; - - { - ResultCatcher catcher; - // Tell the extension to update the script badge state. - ui_test_utils::NavigateToURL( - browser(), GURL(extension->GetResourceURL("show.html"))); - ASSERT_TRUE(catcher.GetNextResult()); - } - - { - ResultCatcher catcher; - // Activate the shortcut (Ctrl+Shift+F). - ASSERT_TRUE(ui_test_utils::SendKeyPressSync( - browser(), ui::VKEY_F, true, true, false, false)); - ASSERT_TRUE(catcher.GetNextResult()); - } -} - -// This test validates that the getAll query API function returns registered -// commands as well as synthesized ones and that inactive commands (like the -// synthesized ones are in nature) have no shortcuts. -IN_PROC_BROWSER_TEST_F(CommandsApiTest, SynthesizedCommand) { - ASSERT_TRUE(test_server()->Start()); - ASSERT_TRUE(RunExtensionTest("keybinding/synthesized")) << message_; -} - -} // namespace extensions diff --git a/chrome/browser/extensions/extension_messages_apitest.cc b/chrome/browser/extensions/extension_messages_apitest.cc index 712b5ed606..131a743dcb 100644 --- a/chrome/browser/extensions/extension_messages_apitest.cc +++ b/chrome/browser/extensions/extension_messages_apitest.cc @@ -13,6 +13,7 @@ #include "base/synchronization/waitable_event.h" #include "base/values.h" #include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/extensions/api/messaging/incognito_connectability.h" #include "chrome/browser/extensions/event_router.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/extensions/extension_prefs.h" @@ -586,26 +587,74 @@ IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, CanConnectAndSendMessages(not_connectable->id())); } -// Tests connection from incognito tabs. Spanning mode only. +// Tests connection from incognito tabs when the user denies the connection +// request. Spanning mode only. // // TODO(kalman): ensure that we exercise split vs spanning incognito logic // somewhere. This is a test that should be shared with the content script logic // so it's not really our specific concern for web connectable. -IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, FromIncognito) { +// +// TODO(kalman): test messages from incognito extensions too. +IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, FromIncognitoDeny) { InitializeTestServer(); const Extension* chromium_connectable = LoadChromiumConnectableExtension(); + const std::string& id = chromium_connectable->id(); Browser* incognito_browser = ui_test_utils::OpenURLOffTheRecord( profile()->GetOffTheRecordProfile(), chromium_org_url()); - // No connection because incognito enabled hasn't been set. + // No connection because incognito-enabled hasn't been set for the extension, + // and the user denied our interactive request. + { + IncognitoConnectability::ScopedAlertTracker alert_tracker( + IncognitoConnectability::ScopedAlertTracker::ALWAYS_DENY); + + EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR, + CanConnectAndSendMessages(incognito_browser, id)); + EXPECT_EQ(1, alert_tracker.GetAndResetAlertCount()); + + // Try again. User has already denied. + EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR, + CanConnectAndSendMessages(incognito_browser, id)); + EXPECT_EQ(0, alert_tracker.GetAndResetAlertCount()); + } + + // Allowing the extension in incognito mode will bypass the deny. + ExtensionPrefs::Get(profile())->SetIsIncognitoEnabled(id, true); + EXPECT_EQ(OK, CanConnectAndSendMessages(incognito_browser, id)); +} + +// Tests connection from incognito tabs when the user accepts the connection +// request. Spanning mode only. +// +// TODO(kalman): see comment above about split mode. +IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, FromIncognitoAllow) { + InitializeTestServer(); + + const Extension* chromium_connectable = LoadChromiumConnectableExtension(); const std::string& id = chromium_connectable->id(); - EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR, - CanConnectAndSendMessages(incognito_browser, id)); - // Then yes. + Browser* incognito_browser = ui_test_utils::OpenURLOffTheRecord( + profile()->GetOffTheRecordProfile(), + chromium_org_url()); + + // Connection allowed even with incognito disabled, because the user accepted + // the interactive request. + { + IncognitoConnectability::ScopedAlertTracker alert_tracker( + IncognitoConnectability::ScopedAlertTracker::ALWAYS_ALLOW); + + EXPECT_EQ(OK, CanConnectAndSendMessages(incognito_browser, id)); + EXPECT_EQ(1, alert_tracker.GetAndResetAlertCount()); + + // Try again. User has already allowed. + EXPECT_EQ(OK, CanConnectAndSendMessages(incognito_browser, id)); + EXPECT_EQ(0, alert_tracker.GetAndResetAlertCount()); + } + + // Allowing the extension in incognito mode will continue to allow. ExtensionPrefs::Get(profile())->SetIsIncognitoEnabled(id, true); EXPECT_EQ(OK, CanConnectAndSendMessages(incognito_browser, id)); } diff --git a/chrome/browser/extensions/extension_nacl_browsertest.cc b/chrome/browser/extensions/extension_nacl_browsertest.cc index df151de798..ba724d55e9 100644 --- a/chrome/browser/extensions/extension_nacl_browsertest.cc +++ b/chrome/browser/extensions/extension_nacl_browsertest.cc @@ -77,7 +77,7 @@ class NaClExtensionTest : public ExtensionBrowserTest { case INSTALL_TYPE_FROM_WEBSTORE: // Install native_client.crx from the webstore. if (InstallExtensionFromWebstore(file_path, 1)) { - extension = service->GetExtensionById(last_loaded_extension_id_, + extension = service->GetExtensionById(last_loaded_extension_id(), false); } break; @@ -85,7 +85,7 @@ class NaClExtensionTest : public ExtensionBrowserTest { case INSTALL_TYPE_NON_WEBSTORE: // Install native_client.crx but not from the webstore. if (ExtensionBrowserTest::InstallExtension(file_path, 1)) { - extension = service->GetExtensionById(last_loaded_extension_id_, + extension = service->GetExtensionById(last_loaded_extension_id(), false); } break; diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc index 751d5361b5..cc04550b05 100644 --- a/chrome/browser/extensions/extension_prefs.cc +++ b/chrome/browser/extensions/extension_prefs.cc @@ -1001,7 +1001,7 @@ bool ExtensionPrefs::IsActive(const std::string& extension_id) { return is_active; } -bool ExtensionPrefs::IsIncognitoEnabled(const std::string& extension_id) { +bool ExtensionPrefs::IsIncognitoEnabled(const std::string& extension_id) const { return ReadPrefAsBooleanAndReturn(extension_id, kPrefIncognitoEnabled); } @@ -1011,7 +1011,7 @@ void ExtensionPrefs::SetIsIncognitoEnabled(const std::string& extension_id, new base::FundamentalValue(enabled)); } -bool ExtensionPrefs::AllowFileAccess(const std::string& extension_id) { +bool ExtensionPrefs::AllowFileAccess(const std::string& extension_id) const { return ReadPrefAsBooleanAndReturn(extension_id, kPrefAllowFileAccess); } diff --git a/chrome/browser/extensions/extension_prefs.h b/chrome/browser/extensions/extension_prefs.h index 22a00b154a..a079c0eadf 100644 --- a/chrome/browser/extensions/extension_prefs.h +++ b/chrome/browser/extensions/extension_prefs.h @@ -369,14 +369,17 @@ class ExtensionPrefs : public ExtensionScopedPrefs, // Returns true if the user enabled this extension to be loaded in incognito // mode. // - // IMPORTANT: you probably want to use ExtensionService::IsIncognitoEnabled + // IMPORTANT: you probably want to use extension_utils::IsIncognitoEnabled // instead of this method. - bool IsIncognitoEnabled(const std::string& extension_id); + bool IsIncognitoEnabled(const std::string& extension_id) const; void SetIsIncognitoEnabled(const std::string& extension_id, bool enabled); // Returns true if the user has chosen to allow this extension to inject // scripts into pages with file URLs. - bool AllowFileAccess(const std::string& extension_id); + // + // IMPORTANT: you probably want to use extension_utils::AllowFileAccess + // instead of this method. + bool AllowFileAccess(const std::string& extension_id) const; void SetAllowFileAccess(const std::string& extension_id, bool allow); bool HasAllowFileAccessSetting(const std::string& extension_id) const; diff --git a/chrome/browser/extensions/extension_process_manager.cc b/chrome/browser/extensions/extension_process_manager.cc index b86df25e4b..a4b2374301 100644 --- a/chrome/browser/extensions/extension_process_manager.cc +++ b/chrome/browser/extensions/extension_process_manager.cc @@ -20,6 +20,7 @@ #include "chrome/browser/extensions/extension_info_map.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/browser.h" @@ -40,6 +41,8 @@ #include "content/public/browser/site_instance.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_delegate.h" +#include "content/public/browser/web_contents_observer.h" +#include "content/public/browser/web_contents_user_data.h" #include "content/public/common/renderer_preferences.h" #include "extensions/browser/view_type_utils.h" @@ -55,6 +58,9 @@ using extensions::BackgroundManifestHandler; using extensions::Extension; using extensions::ExtensionHost; +class RenderViewHostDestructionObserver; +DEFINE_WEB_CONTENTS_USER_DATA_KEY(RenderViewHostDestructionObserver); + namespace { std::string GetExtensionID(RenderViewHost* render_view_host) { @@ -111,6 +117,33 @@ static void CreateBackgroundHostForExtensionLoad( } // namespace +class RenderViewHostDestructionObserver + : public content::WebContentsObserver, + public content::WebContentsUserData<RenderViewHostDestructionObserver> { + public: + virtual ~RenderViewHostDestructionObserver() {} + + private: + explicit RenderViewHostDestructionObserver(WebContents* web_contents) + : WebContentsObserver(web_contents) { + Profile* profile = + Profile::FromBrowserContext(web_contents->GetBrowserContext()); + process_manager_ = + extensions::ExtensionSystem::Get(profile)->process_manager(); + } + + friend class content::WebContentsUserData<RenderViewHostDestructionObserver>; + + // content::WebContentsObserver overrides. + virtual void RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE { + process_manager_->UnregisterRenderViewHost(render_view_host); + } + + ExtensionProcessManager* process_manager_; + + DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestructionObserver); +}; + struct ExtensionProcessManager::BackgroundPageData { // The count of things keeping the lazy background page alive. int lazy_keepalive_count; @@ -654,6 +687,11 @@ void ExtensionProcessManager::Observe( RVHPair* switched_details = content::Details<RVHPair>(details).ptr(); if (switched_details->first) UnregisterRenderViewHost(switched_details->first); + + // The above will unregister a RVH when it gets swapped out with a new + // one. However we need to watch the WebContents to know when a RVH is + // deleted because the WebContents has gone away. + RenderViewHostDestructionObserver::CreateForWebContents(contents); RegisterRenderViewHost(switched_details->second); break; } @@ -916,7 +954,7 @@ bool IncognitoExtensionProcessManager::IsIncognitoEnabled( const Extension* extension) { // Keep in sync with duplicate in extension_info_map.cc. ExtensionService* service = GetProfile()->GetExtensionService(); - return service && service->IsIncognitoEnabled(extension->id()); + return extension_util::IsIncognitoEnabled(extension->id(), service); } void IncognitoExtensionProcessManager::Observe( diff --git a/chrome/browser/extensions/extension_renderer_state.cc b/chrome/browser/extensions/extension_renderer_state.cc index 2075d30773..34c998134e 100644 --- a/chrome/browser/extensions/extension_renderer_state.cc +++ b/chrome/browser/extensions/extension_renderer_state.cc @@ -17,8 +17,8 @@ #include "content/public/browser/notification_types.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" -#include "content/public/browser/render_view_host_observer.h" #include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_observer.h" using content::BrowserThread; using content::RenderProcessHost; @@ -30,13 +30,16 @@ using content::WebContents; // class ExtensionRendererState::RenderViewHostObserver - : public content::RenderViewHostObserver { + : public content::WebContentsObserver { public: - explicit RenderViewHostObserver(content::RenderViewHost* host) - : content::RenderViewHostObserver(host) { + RenderViewHostObserver(RenderViewHost* host, WebContents* web_contents) + : content::WebContentsObserver(web_contents), + render_view_host_(host) { } - virtual void RenderViewHostDestroyed(content::RenderViewHost* host) OVERRIDE { + virtual void RenderViewDeleted(content::RenderViewHost* host) OVERRIDE { + if (host != render_view_host_) + return; BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind( @@ -48,6 +51,8 @@ class ExtensionRendererState::RenderViewHostObserver } private: + RenderViewHost* render_view_host_; + DISALLOW_COPY_AND_ASSIGN(RenderViewHostObserver); }; @@ -111,7 +116,7 @@ void ExtensionRendererState::TabObserver::Observe( session_tab_helper->window_id().id())); // The observer deletes itself. - new ExtensionRendererState::RenderViewHostObserver(host); + new ExtensionRendererState::RenderViewHostObserver(host, web_contents); break; } diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc index 18bcbeb7fc..c560220f40 100644 --- a/chrome/browser/extensions/extension_service.cc +++ b/chrome/browser/extensions/extension_service.cc @@ -50,6 +50,7 @@ #include "chrome/browser/extensions/extension_special_storage_policy.h" #include "chrome/browser/extensions/extension_sync_data.h" #include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/external_install_ui.h" #include "chrome/browser/extensions/external_provider_impl.h" #include "chrome/browser/extensions/external_provider_interface.h" @@ -98,6 +99,7 @@ #include "extensions/common/error_utils.h" #include "extensions/common/manifest.h" #include "extensions/common/manifest_constants.h" +#include "extensions/common/permissions/permission_message_provider.h" #include "grit/generated_resources.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "sync/api/sync_change.h" @@ -284,28 +286,6 @@ bool ExtensionService::IsInstalledApp(const GURL& url) const { return !!GetInstalledApp(url); } -const Extension* ExtensionService::GetIsolatedAppForRenderer( - int renderer_child_id) const { - std::set<std::string> extension_ids = - process_map_.GetExtensionsInProcess(renderer_child_id); - // All apps in one process share the same partition. - // It is only possible for the app to have isolated storage - // if there is only 1 app in the process. - if (extension_ids.size() != 1) - return NULL; - - const extensions::Extension* extension = - extensions_.GetByID(*(extension_ids.begin())); - // We still need to check if the extension has isolated storage, - // because it's common for there to be one extension in a process - // without isolated storage. - if (extension && - extensions::AppIsolationInfo::HasIsolatedStorage(extension)) - return extension; - - return NULL; -} - // static // This function is used to implement the command-line switch // --uninstall-extension, and to uninstall an extension via sync. The LOG @@ -1250,6 +1230,10 @@ extensions::ExtensionPrefs* ExtensionService::extension_prefs() { return extension_prefs_; } +const extensions::ExtensionPrefs* ExtensionService::extension_prefs() const { + return extension_prefs_; +} + extensions::SettingsFrontend* ExtensionService::settings_frontend() { return settings_frontend_.get(); } @@ -1415,9 +1399,10 @@ syncer::SyncError ExtensionService::ProcessSyncChanges( extensions::ExtensionSyncData ExtensionService::GetExtensionSyncData( const Extension& extension) const { - return extensions::ExtensionSyncData(extension, - IsExtensionEnabled(extension.id()), - IsIncognitoEnabled(extension.id())); + return extensions::ExtensionSyncData( + extension, + IsExtensionEnabled(extension.id()), + extension_util::IsIncognitoEnabled(extension.id(), this)); } extensions::AppSyncData ExtensionService::GetAppSyncData( @@ -1425,7 +1410,7 @@ extensions::AppSyncData ExtensionService::GetAppSyncData( return extensions::AppSyncData( extension, IsExtensionEnabled(extension.id()), - IsIncognitoEnabled(extension.id()), + extension_util::IsIncognitoEnabled(extension.id(), this), extension_prefs_->extension_sorting()->GetAppLaunchOrdinal( extension.id()), extension_prefs_->extension_sorting()->GetPageOrdinal(extension.id())); @@ -1570,7 +1555,8 @@ bool ExtensionService::ProcessExtensionSyncDataHelper( bool extension_installed = (extension != NULL); int result = extension ? extension->version()->CompareTo(extension_sync_data.version()) : 0; - SetIsIncognitoEnabled(id, extension_sync_data.incognito_enabled()); + extension_util::SetIsIncognitoEnabled( + id, this, extension_sync_data.incognito_enabled()); extension = NULL; // No longer safe to use. if (extension_installed) { @@ -1607,79 +1593,6 @@ bool ExtensionService::ProcessExtensionSyncDataHelper( return true; } -bool ExtensionService::IsIncognitoEnabled( - const std::string& extension_id) const { - const Extension* extension = GetInstalledExtension(extension_id); - if (extension && !extension->can_be_incognito_enabled()) - return false; - // If this is an existing component extension we always allow it to - // work in incognito mode. - if (extension && extension->location() == Manifest::COMPONENT) - return true; - if (extension && extension->force_incognito_enabled()) - return true; - - // Check the prefs. - return extension_prefs_->IsIncognitoEnabled(extension_id); -} - -void ExtensionService::SetIsIncognitoEnabled( - const std::string& extension_id, bool enabled) { - const Extension* extension = GetInstalledExtension(extension_id); - if (extension && !extension->can_be_incognito_enabled()) - return; - if (extension && extension->location() == Manifest::COMPONENT) { - // This shouldn't be called for component extensions unless they are - // syncable. - DCHECK(extensions::sync_helper::IsSyncable(extension)); - - // If we are here, make sure the we aren't trying to change the value. - DCHECK_EQ(enabled, IsIncognitoEnabled(extension_id)); - - return; - } - - // Broadcast unloaded and loaded events to update browser state. Only bother - // if the value changed and the extension is actually enabled, since there is - // no UI otherwise. - bool old_enabled = extension_prefs_->IsIncognitoEnabled(extension_id); - if (enabled == old_enabled) - return; - - extension_prefs_->SetIsIncognitoEnabled(extension_id, enabled); - - bool extension_is_enabled = extensions_.Contains(extension_id); - - // When we reload the extension the ID may be invalidated if we've passed it - // by const ref everywhere. Make a copy to be safe. - std::string id = extension_id; - if (extension_is_enabled) - ReloadExtension(id); - - // Reloading the extension invalidates the |extension| pointer. - extension = GetInstalledExtension(id); - if (extension) - SyncExtensionChangeIfNeeded(*extension); -} - -bool ExtensionService::CanCrossIncognito(const Extension* extension) const { - // We allow the extension to see events and data from another profile iff it - // uses "spanning" behavior and it has incognito access. "split" mode - // extensions only see events for a matching profile. - CHECK(extension); - return IsIncognitoEnabled(extension->id()) && - !extensions::IncognitoInfo::IsSplitMode(extension); -} - -bool ExtensionService::CanLoadInIncognito(const Extension* extension) const { - if (extension->is_hosted_app()) - return true; - // Packaged apps and regular extensions need to be enabled specifically for - // incognito (and split mode should be set). - return extensions::IncognitoInfo::IsSplitMode(extension) && - IsIncognitoEnabled(extension->id()); -} - void ExtensionService::OnExtensionMoved( const std::string& moved_extension_id, const std::string& predecessor_extension_id, @@ -1694,27 +1607,6 @@ void ExtensionService::OnExtensionMoved( SyncExtensionChangeIfNeeded(*extension); } -bool ExtensionService::AllowFileAccess(const Extension* extension) const { - return (CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableExtensionsFileAccessCheck) || - extension_prefs_->AllowFileAccess(extension->id())); -} - -void ExtensionService::SetAllowFileAccess(const Extension* extension, - bool allow) { - // Reload to update browser state. Only bother if the value changed and the - // extension is actually enabled, since there is no UI otherwise. - bool old_allow = AllowFileAccess(extension); - if (allow == old_allow) - return; - - extension_prefs_->SetAllowFileAccess(extension->id(), allow); - - bool extension_is_enabled = extensions_.Contains(extension->id()); - if (extension_is_enabled) - ReloadExtension(extension->id()); -} - // Some extensions will autoupdate themselves externally from Chrome. These // are typically part of some larger client application package. To support // these, the extension will register its location in the the preferences file @@ -2323,8 +2215,11 @@ void ExtensionService::CheckPermissionsIncrease(const Extension* extension, // that requires the user's approval. This could occur because the browser // upgraded and recognized additional privileges, or an extension upgrades // to a version that requires additional privileges. - is_privilege_increase = granted_permissions->HasLessPrivilegesThan( - extension->GetActivePermissions().get(), extension->GetType()); + is_privilege_increase = + extensions::PermissionMessageProvider::Get()->IsPrivilegeIncrease( + granted_permissions, + extension->GetActivePermissions().get(), + extension->GetType()); } if (is_extension_installed) { @@ -2384,12 +2279,14 @@ void ExtensionService::UpdateActiveExtensionsInCrashReporter() { crash_keys::SetActiveExtensions(extension_ids); } -ExtensionService::ImportStatus ExtensionService::SatisfyImports( - const Extension* extension) { +ExtensionService::ImportStatus ExtensionService::CheckImports( + const extensions::Extension* extension, + std::list<SharedModuleInfo::ImportInfo>* missing_modules, + std::list<SharedModuleInfo::ImportInfo>* outdated_modules) { + DCHECK(extension); + DCHECK(missing_modules && missing_modules->empty()); + DCHECK(outdated_modules && outdated_modules->empty()); ImportStatus status = IMPORT_STATUS_OK; - std::vector<std::string> pending; - // TODO(elijahtaylor): Message the user if there is a failure that is - // unrecoverable. if (SharedModuleInfo::ImportsModules(extension)) { const std::vector<SharedModuleInfo::ImportInfo>& imports = SharedModuleInfo::GetImports(extension); @@ -2401,7 +2298,7 @@ ExtensionService::ImportStatus ExtensionService::SatisfyImports( if (!imported_module) { if (extension->from_webstore()) { status = IMPORT_STATUS_UNSATISFIED; - pending.push_back(i->extension_id); + missing_modules->push_back(*i); } else { return IMPORT_STATUS_UNRECOVERABLE; } @@ -2410,6 +2307,7 @@ ExtensionService::ImportStatus ExtensionService::SatisfyImports( } else if (version_required.IsValid() && imported_module->version()->CompareTo(version_required) < 0) { if (imported_module->from_webstore()) { + outdated_modules->push_back(*i); status = IMPORT_STATUS_UNSATISFIED; } else { return IMPORT_STATUS_UNRECOVERABLE; @@ -2417,12 +2315,21 @@ ExtensionService::ImportStatus ExtensionService::SatisfyImports( } } } + return status; +} + +ExtensionService::ImportStatus ExtensionService::SatisfyImports( + const Extension* extension) { + std::list<SharedModuleInfo::ImportInfo> noinstalled; + std::list<SharedModuleInfo::ImportInfo> outdated; + ImportStatus status = CheckImports(extension, &noinstalled, &outdated); + if (status == IMPORT_STATUS_UNRECOVERABLE) + return status; if (status == IMPORT_STATUS_UNSATISFIED) { - for (std::vector<std::string>::const_iterator iter = pending.begin(); - iter != pending.end(); - ++iter) { + std::list<SharedModuleInfo::ImportInfo>::const_iterator iter; + for (iter = noinstalled.begin(); iter != noinstalled.end(); ++iter) { pending_extension_manager()->AddFromExtensionImport( - *iter, + iter->extension_id, extension_urls::GetWebstoreUpdateUrl(), IsSharedModule); } diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h index ee26e92028..e9e7cba230 100644 --- a/chrome/browser/extensions/extension_service.h +++ b/chrome/browser/extensions/extension_service.h @@ -5,6 +5,7 @@ #ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_SERVICE_H_ #define CHROME_BROWSER_EXTENSIONS_EXTENSION_SERVICE_H_ +#include <list> #include <map> #include <set> #include <string> @@ -36,6 +37,7 @@ #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/extension_set.h" +#include "chrome/common/extensions/manifest_handlers/shared_module_info.h" #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" @@ -144,11 +146,6 @@ class ExtensionService // Returns whether the URL is from either a hosted or packaged app. bool IsInstalledApp(const GURL& url) const; - // If the renderer is hosting an installed app with isolated storage, - // returns it, otherwise returns NULL. - const extensions::Extension* GetIsolatedAppForRenderer( - int renderer_child_id) const; - // Attempts to uninstall an extension from a given ExtensionService. Returns // true iff the target extension exists. static bool UninstallExtensionHelper(ExtensionService* extensions_service, @@ -186,11 +183,6 @@ class ExtensionService extensions::ProcessMap* process_map() { return &process_map_; } - // Whether this extension can run in an incognito window. - virtual bool IsIncognitoEnabled(const std::string& extension_id) const; - virtual void SetIsIncognitoEnabled(const std::string& extension_id, - bool enabled); - // Updates the app launcher value for the moved extension so that it is now // located after the given predecessor and before the successor. This will // trigger a sync if needed. Empty strings are used to indicate no successor @@ -199,19 +191,6 @@ class ExtensionService const std::string& predecessor_extension_id, const std::string& successor_extension_id); - // Returns true if the given extension can see events and data from another - // sub-profile (incognito to original profile, or vice versa). - bool CanCrossIncognito(const extensions::Extension* extension) const; - - // Returns true if the given extension can be loaded in incognito. - bool CanLoadInIncognito(const extensions::Extension* extension) const; - - // Whether this extension can inject scripts into pages with file URLs. - bool AllowFileAccess(const extensions::Extension* extension) const; - // Will reload the extension since this permission is applied at loading time - // only. - void SetAllowFileAccess(const extensions::Extension* extension, bool allow); - // Whether the persistent background page, if any, is ready. We don't load // other components until then. If there is no background page, or if it is // non-persistent (lazy), we consider it to be ready. @@ -397,6 +376,13 @@ class ExtensionService IMPORT_STATUS_UNRECOVERABLE }; + // Checks an extension's imports. No installed and outdated imports will be + // stored in |missing_modules| and |outdated_modules|. + ImportStatus CheckImports( + const extensions::Extension* extension, + std::list<extensions::SharedModuleInfo::ImportInfo>* missing_modules, + std::list<extensions::SharedModuleInfo::ImportInfo>* outdated_modules); + // Checks an extension's shared module imports to see if they are satisfied. // If they are not, this function adds the dependencies to the pending install // list if |extension| came from the webstore. @@ -503,6 +489,7 @@ class ExtensionService // TODO(skerner): Change to const ExtensionPrefs& extension_prefs() const, // ExtensionPrefs* mutable_extension_prefs(). extensions::ExtensionPrefs* extension_prefs(); + const extensions::ExtensionPrefs* extension_prefs() const; extensions::SettingsFrontend* settings_frontend(); diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc index 38c51b0762..b9e4dcec90 100644 --- a/chrome/browser/extensions/extension_service_unittest.cc +++ b/chrome/browser/extensions/extension_service_unittest.cc @@ -44,6 +44,7 @@ #include "chrome/browser/extensions/extension_special_storage_policy.h" #include "chrome/browser/extensions/extension_sync_data.h" #include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/external_install_ui.h" #include "chrome/browser/extensions/external_policy_loader.h" #include "chrome/browser/extensions/external_pref_loader.h" @@ -449,7 +450,7 @@ class MockProviderVisitor ExtensionServiceTestBase::ExtensionServiceInitParams:: ExtensionServiceInitParams() - : autoupdate_enabled(false), is_first_run(true) { + : autoupdate_enabled(false), is_first_run(true), profile_is_managed(false) { } // Our message loop may be used in tests which require it to be an IO loop. @@ -487,6 +488,10 @@ void ExtensionServiceTestBase::InitializeExtensionService( chrome::RegisterUserProfilePrefs(registry.get()); profile_builder.SetPrefService(prefs.Pass()); } + + if (params.profile_is_managed) + profile_builder.SetManagedUserId("asdf"); + profile_builder.SetPath(params.profile_path); profile_ = profile_builder.Build(); @@ -512,6 +517,8 @@ void ExtensionServiceTestBase::InitializeExtensionService( management_policy_ = ExtensionSystem::Get(profile_.get())->management_policy(); + extensions_install_dir_ = params.extensions_install_dir; + // When we start up, we want to make sure there is no external provider, // since the ExtensionService on Windows will use the Registry as a default // provider and if there is something already registered there then it will @@ -529,22 +536,25 @@ void ExtensionServiceTestBase::InitializeExtensionService( void ExtensionServiceTestBase::InitializeInstalledExtensionService( const base::FilePath& prefs_file, const base::FilePath& source_install_dir) { - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + EXPECT_TRUE(temp_dir_.CreateUniqueTempDir()); base::FilePath path = temp_dir_.path(); path = path.Append(FILE_PATH_LITERAL("TestingExtensionsPath")); - base::DeleteFile(path, true); - file_util::CreateDirectory(path); + EXPECT_TRUE(base::DeleteFile(path, true)); + base::PlatformFileError error = base::PLATFORM_FILE_OK; + EXPECT_TRUE(file_util::CreateDirectoryAndGetError(path, &error)) << error; base::FilePath temp_prefs = path.Append(FILE_PATH_LITERAL("Preferences")); - base::CopyFile(prefs_file, temp_prefs); + EXPECT_TRUE(base::CopyFile(prefs_file, temp_prefs)); - extensions_install_dir_ = path.Append(FILE_PATH_LITERAL("Extensions")); - base::DeleteFile(extensions_install_dir_, true); - base::CopyDirectory(source_install_dir, extensions_install_dir_, true); + base::FilePath extensions_install_dir = + path.Append(FILE_PATH_LITERAL("Extensions")); + EXPECT_TRUE(base::DeleteFile(extensions_install_dir, true)); + EXPECT_TRUE( + base::CopyDirectory(source_install_dir, extensions_install_dir, true)); ExtensionServiceInitParams params; params.profile_path = path; params.pref_file = temp_prefs; - params.extensions_install_dir = extensions_install_dir_; + params.extensions_install_dir = extensions_install_dir; InitializeExtensionService(params); } @@ -559,7 +569,7 @@ void ExtensionServiceTestBase::InitializeGoodInstalledExtensionService() { } void ExtensionServiceTestBase::InitializeEmptyExtensionService() { - InitializeExtensionServiceHelper(false, true); + InitializeExtensionService(CreateDefaultInitParams()); } void ExtensionServiceTestBase::InitializeExtensionProcessManager() { @@ -569,30 +579,33 @@ void ExtensionServiceTestBase::InitializeExtensionProcessManager() { } void ExtensionServiceTestBase::InitializeExtensionServiceWithUpdater() { - InitializeExtensionServiceHelper(true, true); + ExtensionServiceInitParams params = CreateDefaultInitParams(); + params.autoupdate_enabled = true; + InitializeExtensionService(params); service_->updater()->Start(); } -void ExtensionServiceTestBase::InitializeExtensionServiceHelper( - bool autoupdate_enabled, bool is_first_run) { - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); +ExtensionServiceTestBase::ExtensionServiceInitParams +ExtensionServiceTestBase::CreateDefaultInitParams() { + ExtensionServiceInitParams params; + EXPECT_TRUE(temp_dir_.CreateUniqueTempDir()); base::FilePath path = temp_dir_.path(); path = path.Append(FILE_PATH_LITERAL("TestingExtensionsPath")); - base::DeleteFile(path, true); - file_util::CreateDirectory(path); + EXPECT_TRUE(base::DeleteFile(path, true)); + base::PlatformFileError error = base::PLATFORM_FILE_OK; + EXPECT_TRUE(file_util::CreateDirectoryAndGetError(path, &error)) << error; base::FilePath prefs_filename = path.Append(FILE_PATH_LITERAL("TestPreferences")); - extensions_install_dir_ = path.Append(FILE_PATH_LITERAL("Extensions")); - base::DeleteFile(extensions_install_dir_, true); - file_util::CreateDirectory(extensions_install_dir_); + base::FilePath extensions_install_dir = + path.Append(FILE_PATH_LITERAL("Extensions")); + EXPECT_TRUE(base::DeleteFile(extensions_install_dir, true)); + EXPECT_TRUE(file_util::CreateDirectoryAndGetError(extensions_install_dir, + &error)) << error; - ExtensionServiceInitParams params; params.profile_path = path; params.pref_file = prefs_filename; - params.extensions_install_dir = extensions_install_dir_; - params.autoupdate_enabled = autoupdate_enabled; - params.is_first_run = is_first_run; - InitializeExtensionService(params); + params.extensions_install_dir = extensions_install_dir; + return params; } // static @@ -2925,7 +2938,7 @@ TEST_F(ExtensionServiceTest, UpdateExtensionPreservesState) { // Disable it and allow it to run in incognito. These settings should carry // over to the updated version. service_->DisableExtension(good->id(), Extension::DISABLE_USER_ACTION); - service_->SetIsIncognitoEnabled(good->id(), true); + extension_util::SetIsIncognitoEnabled(good->id(), service_, true); service_->extension_prefs()->SetDidExtensionEscalatePermissions(good, true); path = data_dir_.AppendASCII("good2.crx"); @@ -2933,7 +2946,7 @@ TEST_F(ExtensionServiceTest, UpdateExtensionPreservesState) { ASSERT_EQ(1u, service_->disabled_extensions()->size());\ const Extension* good2 = service_->GetExtensionById(good_crx, true); ASSERT_EQ("1.0.0.1", good2->version()->GetString()); - EXPECT_TRUE(service_->IsIncognitoEnabled(good2->id())); + EXPECT_TRUE(extension_util::IsIncognitoEnabled(good2->id(), service_)); EXPECT_TRUE(service_->extension_prefs()->DidExtensionEscalatePermissions( good2->id())); } @@ -3187,7 +3200,7 @@ TEST_F(ExtensionServiceTest, MAYBE_UpdatePendingExternalCrx) { EXPECT_FALSE( service_->extension_prefs()->IsExtensionDisabled(extension->id())); EXPECT_TRUE(service_->IsExtensionEnabled(extension->id())); - EXPECT_FALSE(service_->IsIncognitoEnabled(extension->id())); + EXPECT_FALSE(extension_util::IsIncognitoEnabled(extension->id(), service_)); } // Test updating a pending CRX as if the source is an external extension @@ -3769,6 +3782,12 @@ TEST_F(ExtensionServiceTest, ManagementPolicyRequiresEnable) { EXPECT_EQ(0u, service_->disabled_extensions()->size()); } +// Flaky on windows; http://crbug.com/309833 +#if defined(OS_WIN) +#define MAYBE_ExternalExtensionAutoAcknowledgement DISABLED_ExternalExtensionAutoAcknowledgement +#else +#define MAYBE_ExternalExtensionAutoAcknowledgement ExternalExtensionAutoAcknowledgement +#endif TEST_F(ExtensionServiceTest, ExternalExtensionAutoAcknowledgement) { InitializeEmptyExtensionService(); set_extensions_enabled(true); @@ -5366,7 +5385,8 @@ TEST_F(ExtensionServiceTest, GetSyncData) { EXPECT_EQ(extension->id(), data.id()); EXPECT_FALSE(data.uninstalled()); EXPECT_EQ(service_->IsExtensionEnabled(good_crx), data.enabled()); - EXPECT_EQ(service_->IsIncognitoEnabled(good_crx), data.incognito_enabled()); + EXPECT_EQ(extension_util::IsIncognitoEnabled(good_crx, service_), + data.incognito_enabled()); EXPECT_TRUE(data.version().Equals(*extension->version())); EXPECT_EQ(extensions::ManifestURL::GetUpdateURL(extension), data.update_url()); @@ -5392,7 +5412,8 @@ TEST_F(ExtensionServiceTest, GetSyncDataTerminated) { EXPECT_EQ(extension->id(), data.id()); EXPECT_FALSE(data.uninstalled()); EXPECT_EQ(service_->IsExtensionEnabled(good_crx), data.enabled()); - EXPECT_EQ(service_->IsIncognitoEnabled(good_crx), data.incognito_enabled()); + EXPECT_EQ(extension_util::IsIncognitoEnabled(good_crx, service_), + data.incognito_enabled()); EXPECT_TRUE(data.version().Equals(*extension->version())); EXPECT_EQ(extensions::ManifestURL::GetUpdateURL(extension), data.update_url()); @@ -5443,7 +5464,7 @@ TEST_F(ExtensionServiceTest, GetSyncExtensionDataUserSettings) { EXPECT_FALSE(data.incognito_enabled()); } - service_->SetIsIncognitoEnabled(good_crx, true); + extension_util::SetIsIncognitoEnabled(good_crx, service_, true); { syncer::SyncDataList list = service_->GetAllSyncData(syncer::EXTENSIONS); ASSERT_EQ(list.size(), 1U); @@ -5705,7 +5726,7 @@ TEST_F(ExtensionServiceTest, ProcessSyncDataSettings) { InstallCRX(data_dir_.AppendASCII("good.crx"), INSTALL_NEW); EXPECT_TRUE(service_->IsExtensionEnabled(good_crx)); - EXPECT_FALSE(service_->IsIncognitoEnabled(good_crx)); + EXPECT_FALSE(extension_util::IsIncognitoEnabled(good_crx, service_)); sync_pb::EntitySpecifics specifics; sync_pb::ExtensionSpecifics* ext_specifics = specifics.mutable_extension(); @@ -5724,7 +5745,7 @@ TEST_F(ExtensionServiceTest, ProcessSyncDataSettings) { list[0] = sync_change; service_->ProcessSyncChanges(FROM_HERE, list); EXPECT_FALSE(service_->IsExtensionEnabled(good_crx)); - EXPECT_FALSE(service_->IsIncognitoEnabled(good_crx)); + EXPECT_FALSE(extension_util::IsIncognitoEnabled(good_crx, service_)); } { @@ -5739,7 +5760,7 @@ TEST_F(ExtensionServiceTest, ProcessSyncDataSettings) { list[0] = sync_change; service_->ProcessSyncChanges(FROM_HERE, list); EXPECT_TRUE(service_->IsExtensionEnabled(good_crx)); - EXPECT_TRUE(service_->IsIncognitoEnabled(good_crx)); + EXPECT_TRUE(extension_util::IsIncognitoEnabled(good_crx, service_)); } { @@ -5754,7 +5775,7 @@ TEST_F(ExtensionServiceTest, ProcessSyncDataSettings) { list[0] = sync_change; service_->ProcessSyncChanges(FROM_HERE, list); EXPECT_FALSE(service_->IsExtensionEnabled(good_crx)); - EXPECT_TRUE(service_->IsIncognitoEnabled(good_crx)); + EXPECT_TRUE(extension_util::IsIncognitoEnabled(good_crx, service_)); } EXPECT_FALSE(service_->pending_extension_manager()->IsIdPending(good_crx)); @@ -5771,7 +5792,7 @@ TEST_F(ExtensionServiceTest, ProcessSyncDataTerminatedExtension) { InstallCRX(data_dir_.AppendASCII("good.crx"), INSTALL_NEW); TerminateExtension(good_crx); EXPECT_TRUE(service_->IsExtensionEnabled(good_crx)); - EXPECT_FALSE(service_->IsIncognitoEnabled(good_crx)); + EXPECT_FALSE(extension_util::IsIncognitoEnabled(good_crx, service_)); sync_pb::EntitySpecifics specifics; sync_pb::ExtensionSpecifics* ext_specifics = specifics.mutable_extension(); @@ -5790,7 +5811,7 @@ TEST_F(ExtensionServiceTest, ProcessSyncDataTerminatedExtension) { service_->ProcessSyncChanges(FROM_HERE, list); EXPECT_FALSE(service_->IsExtensionEnabled(good_crx)); - EXPECT_TRUE(service_->IsIncognitoEnabled(good_crx)); + EXPECT_TRUE(extension_util::IsIncognitoEnabled(good_crx, service_)); EXPECT_FALSE(service_->pending_extension_manager()->IsIdPending(good_crx)); } @@ -5805,7 +5826,7 @@ TEST_F(ExtensionServiceTest, ProcessSyncDataVersionCheck) { InstallCRX(data_dir_.AppendASCII("good.crx"), INSTALL_NEW); EXPECT_TRUE(service_->IsExtensionEnabled(good_crx)); - EXPECT_FALSE(service_->IsIncognitoEnabled(good_crx)); + EXPECT_FALSE(extension_util::IsIncognitoEnabled(good_crx, service_)); sync_pb::EntitySpecifics specifics; sync_pb::ExtensionSpecifics* ext_specifics = specifics.mutable_extension(); @@ -5887,11 +5908,11 @@ TEST_F(ExtensionServiceTest, ProcessSyncDataNotInstalled) { EXPECT_TRUE(service_->IsExtensionEnabled(good_crx)); - EXPECT_FALSE(service_->IsIncognitoEnabled(good_crx)); + EXPECT_FALSE(extension_util::IsIncognitoEnabled(good_crx, service_)); service_->ProcessSyncChanges(FROM_HERE, list); EXPECT_TRUE(service_->updater()->WillCheckSoon()); EXPECT_FALSE(service_->IsExtensionEnabled(good_crx)); - EXPECT_TRUE(service_->IsIncognitoEnabled(good_crx)); + EXPECT_TRUE(extension_util::IsIncognitoEnabled(good_crx, service_)); const extensions::PendingExtensionInfo* info; EXPECT_TRUE((info = service_->pending_extension_manager()-> @@ -6475,7 +6496,9 @@ TEST_F(ExtensionServiceTest, ExternalInstallUpdatesFromWebstoreOldProfile) { // This sets up the ExtensionPrefs used by our ExtensionService to be // post-first run. - InitializeExtensionServiceHelper(false, false); + ExtensionServiceInitParams params = CreateDefaultInitParams(); + params.is_first_run = false; + InitializeExtensionService(params); base::FilePath crx_path = temp_dir_.path().AppendASCII("webstore.crx"); PackCRX(data_dir_.AppendASCII("update_from_webstore"), diff --git a/chrome/browser/extensions/extension_service_unittest.h b/chrome/browser/extensions/extension_service_unittest.h index 05b71a0877..2972c6db16 100644 --- a/chrome/browser/extensions/extension_service_unittest.h +++ b/chrome/browser/extensions/extension_service_unittest.h @@ -36,6 +36,7 @@ class ExtensionServiceTestBase : public testing::Test { base::FilePath extensions_install_dir; bool autoupdate_enabled; bool is_first_run; + bool profile_is_managed; ExtensionServiceInitParams(); }; @@ -67,8 +68,7 @@ class ExtensionServiceTestBase : public testing::Test { } protected: - void InitializeExtensionServiceHelper(bool autoupdate_enabled, - bool is_first_run); + ExtensionServiceInitParams CreateDefaultInitParams(); // Destroying at_exit_manager_ will delete all LazyInstances, so it must come // after thread_bundle_ in the destruction order. diff --git a/chrome/browser/extensions/extension_startup_browsertest.cc b/chrome/browser/extensions/extension_startup_browsertest.cc index 300f0230b2..b0250545b9 100644 --- a/chrome/browser/extensions/extension_startup_browsertest.cc +++ b/chrome/browser/extensions/extension_startup_browsertest.cc @@ -12,6 +12,7 @@ #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/user_script_master.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" @@ -188,7 +189,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionsStartupTest, MAYBE_NoFileAccess) { it != service->extensions()->end(); ++it) { if ((*it)->location() == extensions::Manifest::COMPONENT) continue; - if (service->AllowFileAccess(it->get())) + if (extension_util::AllowFileAccess(it->get(), service)) extension_list.push_back(it->get()); } @@ -196,7 +197,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionsStartupTest, MAYBE_NoFileAccess) { content::WindowedNotificationObserver user_scripts_observer( chrome::NOTIFICATION_USER_SCRIPTS_UPDATED, content::NotificationService::AllSources()); - service->SetAllowFileAccess(extension_list[i], false); + extension_util::SetAllowFileAccess(extension_list[i], service, false); user_scripts_observer.Wait(); } diff --git a/chrome/browser/extensions/extension_system.cc b/chrome/browser/extensions/extension_system.cc index 348c5ee08a..ddb687dfb1 100644 --- a/chrome/browser/extensions/extension_system.cc +++ b/chrome/browser/extensions/extension_system.cc @@ -24,6 +24,7 @@ #include "chrome/browser/extensions/extension_process_manager.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system_factory.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/extension_warning_badge_service.h" #include "chrome/browser/extensions/extension_warning_set.h" #include "chrome/browser/extensions/lazy_background_task_queue.h" @@ -395,7 +396,7 @@ void ExtensionSystemImpl::RegisterExtensionWithRequestContexts( GetInstallTime(extension->id()); } bool incognito_enabled = - extension_service()->IsIncognitoEnabled(extension->id()); + extension_util::IsIncognitoEnabled(extension->id(), extension_service()); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&ExtensionInfoMap::AddExtension, info_map(), diff --git a/chrome/browser/extensions/extension_test_notification_observer.cc b/chrome/browser/extensions/extension_test_notification_observer.cc new file mode 100644 index 0000000000..f8b7a6d4ec --- /dev/null +++ b/chrome/browser/extensions/extension_test_notification_observer.cc @@ -0,0 +1,234 @@ +// Copyright 2013 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/extensions/extension_test_notification_observer.h" + +#include "chrome/browser/extensions/extension_process_manager.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_window.h" +#include "chrome/common/extensions/extension.h" +#include "content/public/browser/notification_registrar.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/test/test_utils.h" + +using extensions::Extension; + +namespace { + +bool HasExtensionPageActionCountReachedTarget(LocationBarTesting* location_bar, + int target_page_action_count) { + VLOG(1) << "Number of page actions: " << location_bar->PageActionCount(); + return location_bar->PageActionCount() == target_page_action_count; +} + +bool HasExtensionPageActionVisibilityReachedTarget( + LocationBarTesting* location_bar, + int target_visible_page_action_count) { + VLOG(1) << "Number of visible page actions: " + << location_bar->PageActionVisibleCount(); + return location_bar->PageActionVisibleCount() == + target_visible_page_action_count; +} + +} // namespace + +ExtensionTestNotificationObserver::ExtensionTestNotificationObserver( + Browser* browser) + : browser_(browser), + profile_(NULL), + extension_installs_observed_(0), + extension_load_errors_observed_(0), + crx_installers_done_observed_(0) { +} + +ExtensionTestNotificationObserver::~ExtensionTestNotificationObserver() {} + +Profile* ExtensionTestNotificationObserver::GetProfile() { + if (!profile_) { + if (browser_) + profile_ = browser_->profile(); + else + profile_ = ProfileManager::GetDefaultProfile(); + } + return profile_; +} + +void ExtensionTestNotificationObserver::WaitForNotification( + int notification_type) { + // TODO(bauerb): Using a WindowedNotificationObserver like this can break + // easily, if the notification we're waiting for is sent before this method. + // Change it so that the WindowedNotificationObserver is constructed earlier. + content::NotificationRegistrar registrar; + registrar.Add( + this, notification_type, content::NotificationService::AllSources()); + content::WindowedNotificationObserver( + notification_type, content::NotificationService::AllSources()).Wait(); +} + +bool ExtensionTestNotificationObserver::WaitForPageActionCountChangeTo( + int count) { + LocationBarTesting* location_bar = + browser_->window()->GetLocationBar()->GetLocationBarForTesting(); + if (!HasExtensionPageActionCountReachedTarget(location_bar, count)) { + content::WindowedNotificationObserver( + chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED, + base::Bind( + &HasExtensionPageActionCountReachedTarget, location_bar, count)) + .Wait(); + } + return HasExtensionPageActionCountReachedTarget(location_bar, count); +} + +bool ExtensionTestNotificationObserver::WaitForPageActionVisibilityChangeTo( + int count) { + LocationBarTesting* location_bar = + browser_->window()->GetLocationBar()->GetLocationBarForTesting(); + if (!HasExtensionPageActionVisibilityReachedTarget(location_bar, count)) { + content::WindowedNotificationObserver( + chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED, + base::Bind(&HasExtensionPageActionVisibilityReachedTarget, + location_bar, + count)).Wait(); + } + return HasExtensionPageActionVisibilityReachedTarget(location_bar, count); +} + +bool ExtensionTestNotificationObserver::WaitForExtensionViewsToLoad() { + ExtensionProcessManager* manager = + extensions::ExtensionSystem::Get(GetProfile())->process_manager(); + ExtensionProcessManager::ViewSet all_views = manager->GetAllViews(); + for (ExtensionProcessManager::ViewSet::const_iterator iter = + all_views.begin(); + iter != all_views.end();) { + if (!(*iter)->IsLoading()) { + ++iter; + } else { + // Wait for all the extension render view hosts that exist to finish + // loading. + content::WindowedNotificationObserver observer( + content::NOTIFICATION_LOAD_STOP, + content::NotificationService::AllSources()); + observer.AddNotificationType( + content::NOTIFICATION_WEB_CONTENTS_DESTROYED, + content::NotificationService::AllSources()); + observer.Wait(); + + // Test activity may have modified the set of extension processes during + // message processing, so re-start the iteration to catch added/removed + // processes. + all_views = manager->GetAllViews(); + iter = all_views.begin(); + } + } + return true; +} + +bool ExtensionTestNotificationObserver::WaitForExtensionInstall() { + int before = extension_installs_observed_; + WaitForNotification(chrome::NOTIFICATION_EXTENSION_INSTALLED); + return extension_installs_observed_ == (before + 1); +} + +bool ExtensionTestNotificationObserver::WaitForExtensionInstallError() { + int before = extension_installs_observed_; + content::WindowedNotificationObserver( + chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR, + content::NotificationService::AllSources()).Wait(); + return extension_installs_observed_ == before; +} + +void ExtensionTestNotificationObserver::WaitForExtensionLoad() { + WaitForNotification(chrome::NOTIFICATION_EXTENSION_LOADED); +} + +void ExtensionTestNotificationObserver::WaitForExtensionAndViewLoad() { + this->WaitForExtensionLoad(); + WaitForExtensionViewsToLoad(); +} + +bool ExtensionTestNotificationObserver::WaitForExtensionLoadError() { + int before = extension_load_errors_observed_; + WaitForNotification(chrome::NOTIFICATION_EXTENSION_LOAD_ERROR); + return extension_load_errors_observed_ != before; +} + +bool ExtensionTestNotificationObserver::WaitForExtensionCrash( + const std::string& extension_id) { + ExtensionService* service = extensions::ExtensionSystem::Get( + GetProfile())->extension_service(); + + if (!service->GetExtensionById(extension_id, true)) { + // The extension is already unloaded, presumably due to a crash. + return true; + } + content::WindowedNotificationObserver( + chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED, + content::NotificationService::AllSources()).Wait(); + return (service->GetExtensionById(extension_id, true) == NULL); +} + +bool ExtensionTestNotificationObserver::WaitForCrxInstallerDone() { + int before = crx_installers_done_observed_; + WaitForNotification(chrome::NOTIFICATION_CRX_INSTALLER_DONE); + return crx_installers_done_observed_ == (before + 1); +} + +void ExtensionTestNotificationObserver::Watch( + int type, + const content::NotificationSource& source) { + CHECK(!observer_); + observer_.reset(new content::WindowedNotificationObserver(type, source)); + registrar_.Add(this, type, source); +} + +void ExtensionTestNotificationObserver::Wait() { + observer_->Wait(); + + registrar_.RemoveAll(); + observer_.reset(); +} + +void ExtensionTestNotificationObserver::Observe( + int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + switch (type) { + case chrome::NOTIFICATION_EXTENSION_LOADED: + last_loaded_extension_id_ = + content::Details<const Extension>(details).ptr()->id(); + VLOG(1) << "Got EXTENSION_LOADED notification."; + break; + + case chrome::NOTIFICATION_CRX_INSTALLER_DONE: + VLOG(1) << "Got CRX_INSTALLER_DONE notification."; + { + const Extension* extension = + content::Details<const Extension>(details).ptr(); + if (extension) + last_loaded_extension_id_ = extension->id(); + else + last_loaded_extension_id_.clear(); + } + ++crx_installers_done_observed_; + break; + + case chrome::NOTIFICATION_EXTENSION_INSTALLED: + VLOG(1) << "Got EXTENSION_INSTALLED notification."; + ++extension_installs_observed_; + break; + + case chrome::NOTIFICATION_EXTENSION_LOAD_ERROR: + VLOG(1) << "Got EXTENSION_LOAD_ERROR notification."; + ++extension_load_errors_observed_; + break; + + default: + NOTREACHED(); + break; + } +} diff --git a/chrome/browser/extensions/extension_test_notification_observer.h b/chrome/browser/extensions/extension_test_notification_observer.h new file mode 100644 index 0000000000..14636b6675 --- /dev/null +++ b/chrome/browser/extensions/extension_test_notification_observer.h @@ -0,0 +1,100 @@ +// Copyright 2013 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_EXTENSIONS_EXTENSION_TEST_NOTIFICATION_OBSERVER_H_ +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_TEST_NOTIFICATION_OBSERVER_H_ + +#include <string> + +#include "base/compiler_specific.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/omnibox/location_bar.h" +#include "content/public/browser/notification_details.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_types.h" + +namespace content { +class WindowedNotificationObserver; +} + +// Test helper class for observing extension-related events. +class ExtensionTestNotificationObserver : public content::NotificationObserver { + public: + explicit ExtensionTestNotificationObserver(Browser* browser); + virtual ~ExtensionTestNotificationObserver(); + + // Wait for the total number of page actions to change to |count|. + bool WaitForPageActionCountChangeTo(int count); + + // Wait for the number of visible page actions to change to |count|. + bool WaitForPageActionVisibilityChangeTo(int count); + + // Waits until an extension is installed and loaded. Returns true if an + // install happened before timeout. + bool WaitForExtensionInstall(); + + // Wait for an extension install error to be raised. Returns true if an + // error was raised. + bool WaitForExtensionInstallError(); + + // Waits until an extension is loaded and all view have loaded. + void WaitForExtensionAndViewLoad(); + + // Waits until an extension is loaded. + void WaitForExtensionLoad(); + + // Waits for an extension load error. Returns true if the error really + // happened. + bool WaitForExtensionLoadError(); + + // Wait for the specified extension to crash. Returns true if it really + // crashed. + bool WaitForExtensionCrash(const std::string& extension_id); + + // Wait for the crx installer to be done. Returns true if it really is done. + bool WaitForCrxInstallerDone(); + + // Wait for all extension views to load. + bool WaitForExtensionViewsToLoad(); + + // Watch for the given event type from the given source. + // After calling this method, call Wait() to ensure that RunMessageLoop() is + // called appropriately and cleanup is performed. + void Watch(int type, const content::NotificationSource& source); + + // After registering one or more event types with Watch(), call + // this method to run the message loop and perform cleanup. + void Wait(); + + const std::string& last_loaded_extension_id() { + return last_loaded_extension_id_; + } + void set_last_loaded_extension_id(std::string last_loaded_extension_id) { + last_loaded_extension_id_ = last_loaded_extension_id; + } + + // content::NotificationObserver + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE; + + private: + Profile* GetProfile(); + + void WaitForNotification(int notification_type); + + Browser* browser_; + Profile* profile_; + + content::NotificationRegistrar registrar_; + scoped_ptr<content::WindowedNotificationObserver> observer_; + + std::string last_loaded_extension_id_; + int extension_installs_observed_; + int extension_load_errors_observed_; + int crx_installers_done_observed_; +}; + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_TEST_NOTIFICATION_OBSERVER_H_ diff --git a/chrome/browser/extensions/extension_toolbar_model.cc b/chrome/browser/extensions/extension_toolbar_model.cc index c869fd91d1..a3a61e6d0f 100644 --- a/chrome/browser/extensions/extension_toolbar_model.cc +++ b/chrome/browser/extensions/extension_toolbar_model.cc @@ -4,6 +4,8 @@ #include "chrome/browser/extensions/extension_toolbar_model.h" +#include <string> + #include "base/prefs/pref_service.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/extensions/api/extension_action/extension_action_api.h" @@ -12,6 +14,7 @@ #include "chrome/browser/extensions/extension_prefs.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_tab_util.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/tab_helper.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" @@ -41,6 +44,11 @@ bool IsInExtensionList(const Extension* extension, } // namespace +bool ExtensionToolbarModel::Observer::BrowserActionShowPopup( + const extensions::Extension* extension) { + return false; +} + ExtensionToolbarModel::ExtensionToolbarModel(ExtensionService* service) : service_(service), prefs_(service->profile()->GetPrefs()), @@ -128,7 +136,8 @@ void ExtensionToolbarModel::MoveBrowserAction(const Extension* extension, ExtensionToolbarModel::Action ExtensionToolbarModel::ExecuteBrowserAction( const Extension* extension, Browser* browser, - GURL* popup_url_out) { + GURL* popup_url_out, + bool should_grant) { content::WebContents* web_contents = NULL; int tab_id = 0; if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, &tab_id)) @@ -142,8 +151,10 @@ ExtensionToolbarModel::Action ExtensionToolbarModel::ExecuteBrowserAction( if (!browser_action->GetIsVisible(tab_id)) return ACTION_NONE; - extensions::TabHelper::FromWebContents(web_contents)-> - active_tab_permission_granter()->GrantIfRequested(extension); + if (should_grant) { + extensions::TabHelper::FromWebContents(web_contents)-> + active_tab_permission_granter()->GrantIfRequested(extension); + } if (browser_action->HasPopup(tab_id)) { if (popup_url_out) @@ -402,7 +413,7 @@ int ExtensionToolbarModel::IncognitoIndexToOriginal(int incognito_index) { for (ExtensionList::iterator iter = toolbar_items_.begin(); iter != toolbar_items_.end(); ++iter, ++original_index) { - if (service_->IsIncognitoEnabled((*iter)->id())) { + if (extension_util::IsIncognitoEnabled((*iter)->id(), service_)) { if (incognito_index == i) break; ++i; @@ -418,7 +429,7 @@ int ExtensionToolbarModel::OriginalIndexToIncognito(int original_index) { ++iter, ++i) { if (original_index == i) break; - if (service_->IsIncognitoEnabled((*iter)->id())) + if (extension_util::IsIncognitoEnabled((*iter)->id(), service_)) ++incognito_index; } return incognito_index; @@ -454,3 +465,15 @@ void ExtensionToolbarModel::OnExtensionToolbarPrefChange() { weak_ptr_factory_.GetWeakPtr())); } } + +bool ExtensionToolbarModel::ShowBrowserActionPopup( + const extensions::Extension* extension) { + ObserverListBase<Observer>::Iterator it(observers_); + Observer* obs = NULL; + while ((obs = it.GetNext()) != NULL) { + // Stop after first popup since it should only show in the active window. + if (obs->BrowserActionShowPopup(extension)) + return true; + } + return false; +} diff --git a/chrome/browser/extensions/extension_toolbar_model.h b/chrome/browser/extensions/extension_toolbar_model.h index df01de5d17..1d77d4ee81 100644 --- a/chrome/browser/extensions/extension_toolbar_model.h +++ b/chrome/browser/extensions/extension_toolbar_model.h @@ -33,7 +33,7 @@ class ExtensionToolbarModel : public content::NotificationObserver { }; // A class which is informed of changes to the model; represents the view of - // MVC. + // MVC. Also used for signaling view changes such as showing extension popups. class Observer { public: // An extension with a browser action button has been added, and should go @@ -48,6 +48,10 @@ class ExtensionToolbarModel : public content::NotificationObserver { virtual void BrowserActionMoved(const extensions::Extension* extension, int index) {} + // Signal the |extension| to show the popup now in the active window. + // Returns true if a popup was slated to be shown. + virtual bool BrowserActionShowPopup(const extensions::Extension* extension); + // Called when the model has finished loading. virtual void ModelLoaded() {} @@ -62,10 +66,14 @@ class ExtensionToolbarModel : public content::NotificationObserver { // Executes the browser action for an extension and returns the action that // the UI should perform in response. // |popup_url_out| will be set if the extension should show a popup, with - // the URL that should be shown, if non-NULL. + // the URL that should be shown, if non-NULL. |should_grant| controls whether + // the extension should be granted page tab permissions, which is what happens + // when the user clicks the browser action, but not, for example, when the + // showPopup API is called. Action ExecuteBrowserAction(const extensions::Extension* extension, Browser* browser, - GURL* popup_url_out); + GURL* popup_url_out, + bool should_grant); // If count == size(), this will set the visible icon count to -1, meaning // "show all actions". void SetVisibleIconCount(int count); @@ -85,6 +93,10 @@ class ExtensionToolbarModel : public content::NotificationObserver { void OnExtensionToolbarPrefChange(); + // Tells observers to display a popup without granting tab permissions and + // returns whether the popup was slated to be shown. + bool ShowBrowserActionPopup(const extensions::Extension* extension); + private: // content::NotificationObserver implementation. virtual void Observe(int type, diff --git a/chrome/browser/extensions/extension_url_rewrite_browsertest.cc b/chrome/browser/extensions/extension_url_rewrite_browsertest.cc index 0db2dcbf8d..6dc93f0ed7 100644 --- a/chrome/browser/extensions/extension_url_rewrite_browsertest.cc +++ b/chrome/browser/extensions/extension_url_rewrite_browsertest.cc @@ -111,14 +111,6 @@ IN_PROC_BROWSER_TEST_F(ExtensionURLRewriteBrowserTest, MAYBE_BookmarksURL) { EXPECT_TRUE(navigation->GetURL().SchemeIs(extensions::kExtensionScheme)); } -#if defined(FILE_MANAGER_EXTENSION) -IN_PROC_BROWSER_TEST_F(ExtensionURLRewriteBrowserTest, FileManagerURL) { - // Navigate to chrome://files and check that the location bar URL is - // what was entered and the internal URL uses the chrome-extension:// scheme. - TestExtensionURLOverride(GURL(chrome::kChromeUIFileManagerURL)); -} -#endif - IN_PROC_BROWSER_TEST_F(ExtensionURLRewriteBrowserTest, BookmarksURLWithRef) { // Navigate to chrome://bookmarks/#1 and check that the location bar URL is // what was entered and the internal URL uses the chrome-extension:// scheme. diff --git a/chrome/browser/extensions/extension_util.cc b/chrome/browser/extensions/extension_util.cc new file mode 100644 index 0000000000..71375ddcfc --- /dev/null +++ b/chrome/browser/extensions/extension_util.cc @@ -0,0 +1,124 @@ +// Copyright 2013 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/extensions/extension_util.h" + +#include "base/command_line.h" +#include "chrome/browser/extensions/extension_prefs.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/incognito_handler.h" +#include "chrome/common/extensions/sync_helper.h" +#include "extensions/common/manifest.h" + +using extensions::Extension; +using extensions::ExtensionPrefs; + +namespace extension_util { + +bool IsIncognitoEnabled(const std::string& extension_id, + const ExtensionService* service) { + if (!service) + return false; + + const Extension* extension = service->GetInstalledExtension(extension_id); + if (extension && !extension->can_be_incognito_enabled()) + return false; + // If this is an existing component extension we always allow it to + // work in incognito mode. + if (extension && extension->location() == extensions::Manifest::COMPONENT) + return true; + if (extension && extension->force_incognito_enabled()) + return true; + + // Check the prefs. + return service->extension_prefs()->IsIncognitoEnabled(extension_id); +} + +void SetIsIncognitoEnabled(const std::string& extension_id, + ExtensionService* service, + bool enabled) { + const Extension* extension = service->GetInstalledExtension(extension_id); + if (extension && !extension->can_be_incognito_enabled()) + return; + if (extension && extension->location() == extensions::Manifest::COMPONENT) { + // This shouldn't be called for component extensions unless it is called + // by sync, for syncable component extensions. + // See http://crbug.com/112290 and associated CLs for the sordid history. + DCHECK(extensions::sync_helper::IsSyncable(extension)); + + // If we are here, make sure the we aren't trying to change the value. + DCHECK_EQ(enabled, IsIncognitoEnabled(extension_id, service)); + return; + } + + ExtensionPrefs* extension_prefs = service->extension_prefs(); + // Broadcast unloaded and loaded events to update browser state. Only bother + // if the value changed and the extension is actually enabled, since there is + // no UI otherwise. + bool old_enabled = extension_prefs->IsIncognitoEnabled(extension_id); + if (enabled == old_enabled) + return; + + extension_prefs->SetIsIncognitoEnabled(extension_id, enabled); + + bool extension_is_enabled = service->extensions()->Contains(extension_id); + + // When we reload the extension the ID may be invalidated if we've passed it + // by const ref everywhere. Make a copy to be safe. + std::string id = extension_id; + if (extension_is_enabled) + service->ReloadExtension(id); + + // Reloading the extension invalidates the |extension| pointer. + extension = service->GetInstalledExtension(id); + if (extension) + service->SyncExtensionChangeIfNeeded(*extension); +} + +bool CanCrossIncognito(const Extension* extension, + const ExtensionService* service) { + // We allow the extension to see events and data from another profile iff it + // uses "spanning" behavior and it has incognito access. "split" mode + // extensions only see events for a matching profile. + CHECK(extension); + return extension_util::IsIncognitoEnabled(extension->id(), service) && + !extensions::IncognitoInfo::IsSplitMode(extension); +} + +bool CanLoadInIncognito(const Extension* extension, + const ExtensionService* service) { + if (extension->is_hosted_app()) + return true; + // Packaged apps and regular extensions need to be enabled specifically for + // incognito (and split mode should be set). + return extensions::IncognitoInfo::IsSplitMode(extension) && + extension_util::IsIncognitoEnabled(extension->id(), service); +} + +bool AllowFileAccess(const Extension* extension, + const ExtensionService* service) { + return (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableExtensionsFileAccessCheck) || + service->extension_prefs()->AllowFileAccess(extension->id())); +} + +void SetAllowFileAccess(const Extension* extension, + ExtensionService* service, + bool allow) { + // Reload to update browser state. Only bother if the value changed and the + // extension is actually enabled, since there is no UI otherwise. + bool old_allow = AllowFileAccess(extension, service); + if (allow == old_allow) + return; + + service->extension_prefs()->SetAllowFileAccess(extension->id(), allow); + + bool extension_is_enabled = service->extensions()->Contains(extension->id()); + if (extension_is_enabled) + service->ReloadExtension(extension->id()); +} + +} // namespace extension_util diff --git a/chrome/browser/extensions/extension_util.h b/chrome/browser/extensions/extension_util.h new file mode 100644 index 0000000000..fb9d74e4cd --- /dev/null +++ b/chrome/browser/extensions/extension_util.h @@ -0,0 +1,49 @@ +// Copyright 2013 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_EXTENSIONS_EXTENSION_UTIL_H_ +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_UTIL_H_ + +#include <string> + +namespace extensions { +class Extension; +} + +class ExtensionService; + +namespace extension_util { + +// Whether this extension can run in an incognito window. +bool IsIncognitoEnabled(const std::string& extension_id, + const ExtensionService* service); + +// Will reload the extension since this permission is applied at loading time +// only. +void SetIsIncognitoEnabled(const std::string& extension_id, + ExtensionService* service, + bool enabled); + +// Returns true if the given extension can see events and data from another +// sub-profile (incognito to original profile, or vice versa). +bool CanCrossIncognito(const extensions::Extension* extension, + const ExtensionService* service); + +// Returns true if the given extension can be loaded in incognito. +bool CanLoadInIncognito(const extensions::Extension* extension, + const ExtensionService* service); + +// Whether this extension can inject scripts into pages with file URLs. +bool AllowFileAccess(const extensions::Extension* extension, + const ExtensionService* service); + +// Will reload the extension since this permission is applied at loading time +// only. +void SetAllowFileAccess(const extensions::Extension* extension, + ExtensionService* service, + bool allow); + +} // namespace extension_util + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_UTIL_H_ diff --git a/chrome/browser/extensions/extension_web_contents_observer.cc b/chrome/browser/extensions/extension_web_contents_observer.cc new file mode 100644 index 0000000000..8f648b6066 --- /dev/null +++ b/chrome/browser/extensions/extension_web_contents_observer.cc @@ -0,0 +1,137 @@ +// Copyright 2013 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/extensions/extension_web_contents_observer.h" + +#include "chrome/browser/extensions/api/messaging/message_service.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/extensions/extension_messages.h" +#include "chrome/common/url_constants.h" +#include "content/public/browser/child_process_security_policy.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/site_instance.h" +#include "content/public/browser/web_contents.h" +#include "extensions/browser/view_type_utils.h" +#include "extensions/common/constants.h" + +DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::ExtensionWebContentsObserver); + +namespace extensions { + +ExtensionWebContentsObserver::ExtensionWebContentsObserver( + content::WebContents* web_contents) + : content::WebContentsObserver(web_contents), + profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())) { +} + +ExtensionWebContentsObserver::~ExtensionWebContentsObserver() { +} + +void ExtensionWebContentsObserver::RenderViewCreated( + content::RenderViewHost* render_view_host) { + render_view_host->Send(new ExtensionMsg_NotifyRenderViewType( + render_view_host->GetRoutingID(), + extensions::GetViewType(web_contents()))); + + const Extension* extension = GetExtension(render_view_host); + if (!extension) + return; + + content::RenderProcessHost* process = render_view_host->GetProcess(); + + // Some extensions use chrome:// URLs. + // This is a temporary solution. Replace it with access to chrome-static:// + // once it is implemented. See: crbug.com/226927. + Manifest::Type type = extension->GetType(); + if (type == Manifest::TYPE_EXTENSION || + type == Manifest::TYPE_LEGACY_PACKAGED_APP || + (type == Manifest::TYPE_PLATFORM_APP && + extension->location() == Manifest::COMPONENT)) { + content::ChildProcessSecurityPolicy::GetInstance()->GrantScheme( + process->GetID(), chrome::kChromeUIScheme); + } + + // Some extensions use file:// URLs. + if (type == Manifest::TYPE_EXTENSION || + type == Manifest::TYPE_LEGACY_PACKAGED_APP) { + if (ExtensionSystem::Get(profile_)->extension_service()-> + extension_prefs()->AllowFileAccess(extension->id())) { + content::ChildProcessSecurityPolicy::GetInstance()->GrantScheme( + process->GetID(), chrome::kFileScheme); + } + } + + switch (type) { + case Manifest::TYPE_EXTENSION: + case Manifest::TYPE_USER_SCRIPT: + case Manifest::TYPE_HOSTED_APP: + case Manifest::TYPE_LEGACY_PACKAGED_APP: + case Manifest::TYPE_PLATFORM_APP: + // Always send a Loaded message before ActivateExtension so that + // ExtensionDispatcher knows what Extension is active, not just its ID. + // This is important for classifying the Extension's JavaScript context + // correctly (see ExtensionDispatcher::ClassifyJavaScriptContext). + render_view_host->Send(new ExtensionMsg_Loaded( + std::vector<ExtensionMsg_Loaded_Params>( + 1, ExtensionMsg_Loaded_Params(extension)))); + render_view_host->Send( + new ExtensionMsg_ActivateExtension(extension->id())); + break; + + case Manifest::TYPE_UNKNOWN: + case Manifest::TYPE_THEME: + case Manifest::TYPE_SHARED_MODULE: + break; + } +} + +bool ExtensionWebContentsObserver::OnMessageReceived( + const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(ExtensionWebContentsObserver, message) + IPC_MESSAGE_HANDLER(ExtensionHostMsg_PostMessage, OnPostMessage) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void ExtensionWebContentsObserver::OnPostMessage(int port_id, + const std::string& message) { + MessageService* message_service = MessageService::Get(profile_); + if (message_service) { + message_service->PostMessage(port_id, message); + } +} + +const Extension* ExtensionWebContentsObserver::GetExtension( + content::RenderViewHost* render_view_host) { + // Note that due to ChromeContentBrowserClient::GetEffectiveURL(), hosted apps + // (excluding bookmark apps) will have a chrome-extension:// URL for their + // site, so we can ignore that wrinkle here. + content::SiteInstance* site_instance = render_view_host->GetSiteInstance(); + const GURL& site = site_instance->GetSiteURL(); + + if (!site.SchemeIs(kExtensionScheme)) + return NULL; + + ExtensionService* service = profile_->GetExtensionService(); + if (!service) + return NULL; + + // Reload the extension if it has crashed. + // TODO(yoz): This reload doesn't happen synchronously for unpacked + // extensions. It seems to be fast enough, but there is a race. + // We should delay loading until the extension has reloaded. + if (service->GetTerminatedExtension(site.host())) + service->ReloadExtension(site.host()); + + // May be null if the extension doesn't exist, for example if somebody typos + // a chrome-extension:// URL. + return service->extensions()->GetByID(site.host()); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/extension_web_contents_observer.h b/chrome/browser/extensions/extension_web_contents_observer.h new file mode 100644 index 0000000000..dbac7f9b6e --- /dev/null +++ b/chrome/browser/extensions/extension_web_contents_observer.h @@ -0,0 +1,45 @@ +// Copyright 2013 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_EXTENSIONS_EXTENSION_WEB_CONTENTS_OBSERVER_H_ +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_WEB_CONTENTS_OBSERVER_H_ + +#include "content/public/browser/web_contents_observer.h" +#include "content/public/browser/web_contents_user_data.h" + +class Profile; + +namespace extensions { +class Extension; + +// A web contents observer that's used for WebContents in renderer and extension +// processes. +class ExtensionWebContentsObserver + : public content::WebContentsObserver, + public content::WebContentsUserData<ExtensionWebContentsObserver> { + public: + virtual ~ExtensionWebContentsObserver(); + + private: + explicit ExtensionWebContentsObserver(content::WebContents* web_contents); + friend class content::WebContentsUserData<ExtensionWebContentsObserver>; + + // content::WebContentsObserver overrides. + virtual void RenderViewCreated( + content::RenderViewHost* render_view_host) OVERRIDE; + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + + void OnPostMessage(int port_id, const std::string& message); + + // Gets the extension or app (if any) that is associated with a RVH. + const Extension* GetExtension(content::RenderViewHost* render_view_host); + + Profile* profile_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionWebContentsObserver); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_WEB_CONTENTS_OBSERVER_H_ diff --git a/chrome/browser/extensions/extension_web_ui.cc b/chrome/browser/extensions/extension_web_ui.cc index 631ba560a7..b2002c2644 100644 --- a/chrome/browser/extensions/extension_web_ui.cc +++ b/chrome/browser/extensions/extension_web_ui.cc @@ -14,6 +14,7 @@ #include "chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_tab_util.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/image_loader.h" #include "chrome/browser/favicon/favicon_util.h" #include "chrome/browser/prefs/scoped_user_pref_update.h" @@ -228,7 +229,7 @@ bool ExtensionWebUI::HandleChromeURLOverride( // extension uses split mode. bool incognito_override_allowed = extensions::IncognitoInfo::IsSplitMode(extension) && - service->IsIncognitoEnabled(extension->id()); + extension_util::IsIncognitoEnabled(extension->id(), service); if (profile->IsOffTheRecord() && !incognito_override_allowed) { ++i; continue; diff --git a/chrome/browser/extensions/external_provider_impl.cc b/chrome/browser/extensions/external_provider_impl.cc index 20a72b8817..df29f6ac93 100644 --- a/chrome/browser/extensions/external_provider_impl.cc +++ b/chrome/browser/extensions/external_provider_impl.cc @@ -34,9 +34,13 @@ #include "ui/base/l10n/l10n_util.h" #if defined(OS_CHROMEOS) +#include "chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.h" #include "chrome/browser/chromeos/extensions/external_pref_cache_loader.h" +#include "chrome/browser/chromeos/login/user.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/policy/app_pack_updater.h" +#include "chrome/browser/chromeos/policy/device_local_account.h" +#include "chrome/browser/chromeos/policy/device_local_account_policy_service.h" #include "chrome/browser/policy/browser_policy_connector.h" #else #include "chrome/browser/extensions/default_apps.h" @@ -61,12 +65,13 @@ const char ExternalProviderImpl::kKeepIfPresent[] = "keep_if_present"; const char ExternalProviderImpl::kRequirePermissionsConsent[] = "require_permissions_consent"; -ExternalProviderImpl::ExternalProviderImpl(VisitorInterface* service, - ExternalLoader* loader, - Profile* profile, - Manifest::Location crx_location, - Manifest::Location download_location, - int creation_flags) +ExternalProviderImpl::ExternalProviderImpl( + VisitorInterface* service, + const scoped_refptr<ExternalLoader>& loader, + Profile* profile, + Manifest::Location crx_location, + Manifest::Location download_location, + int creation_flags) : crx_location_(crx_location), download_location_(download_location), service_(service), @@ -346,16 +351,41 @@ void ExternalProviderImpl::CreateExternalProviders( VisitorInterface* service, Profile* profile, ProviderCollection* provider_list) { + scoped_refptr<ExternalLoader> external_loader; + extensions::Manifest::Location crx_location = Manifest::INVALID_LOCATION; +#if defined(OS_CHROMEOS) + const chromeos::User* user = + chromeos::UserManager::Get()->GetUserByProfile(profile); + if (user && policy::IsDeviceLocalAccountUser(user->email(), NULL)) { + policy::DeviceLocalAccountPolicyBroker* broker = + g_browser_process->browser_policy_connector()-> + GetDeviceLocalAccountPolicyService()-> + GetBrokerForUser(user->email()); + if (broker) { + external_loader = broker->extension_loader(); + crx_location = Manifest::EXTERNAL_POLICY; + } else { + NOTREACHED(); + } + } else { + external_loader = new ExternalPolicyLoader(profile); + } +#else + external_loader = new ExternalPolicyLoader(profile); +#endif + // Policies are mandatory so they can't be skipped with command line flag. - provider_list->push_back( - linked_ptr<ExternalProviderInterface>( - new ExternalProviderImpl( - service, - new ExternalPolicyLoader(profile), - profile, - Manifest::INVALID_LOCATION, - Manifest::EXTERNAL_POLICY_DOWNLOAD, - Extension::NO_FLAGS))); + if (external_loader) { + provider_list->push_back( + linked_ptr<ExternalProviderInterface>( + new ExternalProviderImpl( + service, + external_loader, + profile, + crx_location, + Manifest::EXTERNAL_POLICY_DOWNLOAD, + Extension::NO_FLAGS))); + } // In tests don't install extensions from default external sources. // It would only slowdown tests and make them flaky. diff --git a/chrome/browser/extensions/external_provider_impl.h b/chrome/browser/extensions/external_provider_impl.h index 991d0bb87a..8d95e3e365 100644 --- a/chrome/browser/extensions/external_provider_impl.h +++ b/chrome/browser/extensions/external_provider_impl.h @@ -21,7 +21,6 @@ class Version; } namespace extensions { -class ExternalLoader; // A specialization of the ExternalProvider that uses an instance of // ExternalLoader to provide external extensions. This class can be seen as a @@ -37,7 +36,7 @@ class ExternalProviderImpl : public ExternalProviderInterface { // If either of the origins is not supported by this provider, then it should // be initialized as Manifest::INVALID_LOCATION. ExternalProviderImpl(VisitorInterface* service, - ExternalLoader* loader, + const scoped_refptr<ExternalLoader>& loader, Profile* profile, Manifest::Location crx_location, Manifest::Location download_location, diff --git a/chrome/browser/extensions/global_shortcut_listener.cc b/chrome/browser/extensions/global_shortcut_listener.cc index 94547adfff..aad5453eca 100644 --- a/chrome/browser/extensions/global_shortcut_listener.cc +++ b/chrome/browser/extensions/global_shortcut_listener.cc @@ -1,62 +1,62 @@ -// Copyright (c) 2013 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/extensions/global_shortcut_listener.h"
-#include "chrome/browser/profiles/profile.h"
-#include "ui/base/accelerators/accelerator.h"
-
-namespace extensions {
-
-GlobalShortcutListener::GlobalShortcutListener() {
-}
-
-GlobalShortcutListener::~GlobalShortcutListener() {
- DCHECK(accelerator_map_.empty()); // Make sure we've cleaned up.
-}
-
-void GlobalShortcutListener::RegisterAccelerator(
- const ui::Accelerator& accelerator, Observer* observer) {
- AcceleratorMap::const_iterator it = accelerator_map_.find(accelerator);
- if (it == accelerator_map_.end()) {
- if (accelerator_map_.empty())
- GlobalShortcutListener::GetInstance()->StartListening();
- Observers* observers = new Observers;
- observers->AddObserver(observer);
- accelerator_map_[accelerator] = observers;
- } else {
- // Make sure we don't register the same accelerator twice.
- DCHECK(!accelerator_map_[accelerator]->HasObserver(observer));
- accelerator_map_[accelerator]->AddObserver(observer);
- }
-}
-
-void GlobalShortcutListener::UnregisterAccelerator(
- const ui::Accelerator& accelerator, Observer* observer) {
- AcceleratorMap::iterator it = accelerator_map_.find(accelerator);
- DCHECK(it != accelerator_map_.end());
- DCHECK(it->second->HasObserver(observer));
- it->second->RemoveObserver(observer);
- if (!it->second->might_have_observers()) {
- accelerator_map_.erase(it);
- if (accelerator_map_.empty())
- GlobalShortcutListener::GetInstance()->StopListening();
- }
-}
-
-void GlobalShortcutListener::NotifyKeyPressed(
- const ui::Accelerator& accelerator) {
- AcceleratorMap::iterator iter = accelerator_map_.find(accelerator);
- if (iter == accelerator_map_.end()) {
- // This should never occur, because if it does, we have failed to unregister
- // or failed to clean up the map after unregistering the shortcut.
- NOTREACHED();
- return; // No-one is listening to this key.
- }
- // The observer list should not be empty.
- DCHECK(iter->second->might_have_observers());
-
- FOR_EACH_OBSERVER(Observer, *(iter->second), OnKeyPressed(accelerator));
-}
-
-} // namespace extensions
+// Copyright (c) 2013 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/extensions/global_shortcut_listener.h" +#include "chrome/browser/profiles/profile.h" +#include "ui/base/accelerators/accelerator.h" + +namespace extensions { + +GlobalShortcutListener::GlobalShortcutListener() { +} + +GlobalShortcutListener::~GlobalShortcutListener() { + DCHECK(accelerator_map_.empty()); // Make sure we've cleaned up. +} + +void GlobalShortcutListener::RegisterAccelerator( + const ui::Accelerator& accelerator, Observer* observer) { + AcceleratorMap::const_iterator it = accelerator_map_.find(accelerator); + if (it == accelerator_map_.end()) { + if (accelerator_map_.empty()) + GlobalShortcutListener::GetInstance()->StartListening(); + Observers* observers = new Observers; + observers->AddObserver(observer); + accelerator_map_[accelerator] = observers; + } else { + // Make sure we don't register the same accelerator twice. + DCHECK(!accelerator_map_[accelerator]->HasObserver(observer)); + accelerator_map_[accelerator]->AddObserver(observer); + } +} + +void GlobalShortcutListener::UnregisterAccelerator( + const ui::Accelerator& accelerator, Observer* observer) { + AcceleratorMap::iterator it = accelerator_map_.find(accelerator); + DCHECK(it != accelerator_map_.end()); + DCHECK(it->second->HasObserver(observer)); + it->second->RemoveObserver(observer); + if (!it->second->might_have_observers()) { + accelerator_map_.erase(it); + if (accelerator_map_.empty()) + GlobalShortcutListener::GetInstance()->StopListening(); + } +} + +void GlobalShortcutListener::NotifyKeyPressed( + const ui::Accelerator& accelerator) { + AcceleratorMap::iterator iter = accelerator_map_.find(accelerator); + if (iter == accelerator_map_.end()) { + // This should never occur, because if it does, we have failed to unregister + // or failed to clean up the map after unregistering the shortcut. + NOTREACHED(); + return; // No-one is listening to this key. + } + // The observer list should not be empty. + DCHECK(iter->second->might_have_observers()); + + FOR_EACH_OBSERVER(Observer, *(iter->second), OnKeyPressed(accelerator)); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/global_shortcut_listener.h b/chrome/browser/extensions/global_shortcut_listener.h index 4ca692124c..c61abe65f0 100644 --- a/chrome/browser/extensions/global_shortcut_listener.h +++ b/chrome/browser/extensions/global_shortcut_listener.h @@ -1,65 +1,65 @@ -// Copyright (c) 2013 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_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_H_
-#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_H_
-
-#include <map>
-
-#include "base/observer_list.h"
-#include "ui/events/keycodes/keyboard_codes.h"
-#include "ui/gfx/native_widget_types.h"
-
-namespace ui {
-class Accelerator;
-}
-
-namespace extensions {
-
-// Platform-neutral implementation of a class that keeps track of observers and
-// monitors keystrokes. It relays messages to the appropriate observers when a
-// global shortcut has been struck by the user.
-class GlobalShortcutListener {
- public:
- class Observer {
- public:
- // Called when your global shortcut (|accelerator|) is struck.
- virtual void OnKeyPressed(const ui::Accelerator& accelerator) = 0;
- };
-
- virtual ~GlobalShortcutListener();
-
- static GlobalShortcutListener* GetInstance();
-
- // Implemented by platform-specific implementations of this class.
- virtual void StartListening() = 0;
- virtual void StopListening() = 0;
-
- // Register an observer for when a certain |accelerator| is struck.
- virtual void RegisterAccelerator(
- const ui::Accelerator& accelerator, Observer* observer);
- // Stop listening for the given |accelerator|.
- virtual void UnregisterAccelerator(
- const ui::Accelerator& accelerator, Observer* observer);
-
- protected:
- GlobalShortcutListener();
-
- // Called by platform specific implementations of this class whenever a key
- // is struck. Only called for keys that have observers registered.
- void NotifyKeyPressed(const ui::Accelerator& accelerator);
-
- // The map of accelerators that have been successfully registered as global
- // shortcuts and their observer lists.
- typedef ObserverList<Observer> Observers;
- typedef std::map< ui::Accelerator, Observers* > AcceleratorMap;
- AcceleratorMap accelerator_map_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListener);
-};
-
-} // namespace extensions
-
-#endif // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_H_
+// Copyright (c) 2013 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_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_H_ +#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_H_ + +#include <map> + +#include "base/observer_list.h" +#include "ui/events/keycodes/keyboard_codes.h" +#include "ui/gfx/native_widget_types.h" + +namespace ui { +class Accelerator; +} + +namespace extensions { + +// Platform-neutral implementation of a class that keeps track of observers and +// monitors keystrokes. It relays messages to the appropriate observers when a +// global shortcut has been struck by the user. +class GlobalShortcutListener { + public: + class Observer { + public: + // Called when your global shortcut (|accelerator|) is struck. + virtual void OnKeyPressed(const ui::Accelerator& accelerator) = 0; + }; + + virtual ~GlobalShortcutListener(); + + static GlobalShortcutListener* GetInstance(); + + // Implemented by platform-specific implementations of this class. + virtual void StartListening() = 0; + virtual void StopListening() = 0; + + // Register an observer for when a certain |accelerator| is struck. + virtual void RegisterAccelerator( + const ui::Accelerator& accelerator, Observer* observer); + // Stop listening for the given |accelerator|. + virtual void UnregisterAccelerator( + const ui::Accelerator& accelerator, Observer* observer); + + protected: + GlobalShortcutListener(); + + // Called by platform specific implementations of this class whenever a key + // is struck. Only called for keys that have observers registered. + void NotifyKeyPressed(const ui::Accelerator& accelerator); + + // The map of accelerators that have been successfully registered as global + // shortcuts and their observer lists. + typedef ObserverList<Observer> Observers; + typedef std::map< ui::Accelerator, Observers* > AcceleratorMap; + AcceleratorMap accelerator_map_; + + private: + DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListener); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_H_ diff --git a/chrome/browser/extensions/global_shortcut_listener_ozone.cc b/chrome/browser/extensions/global_shortcut_listener_ozone.cc new file mode 100644 index 0000000000..f1067d45e8 --- /dev/null +++ b/chrome/browser/extensions/global_shortcut_listener_ozone.cc @@ -0,0 +1,75 @@ +// Copyright 2013 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/extensions/global_shortcut_listener_ozone.h" + +#include "content/public/browser/browser_thread.h" + +using content::BrowserThread; + +namespace { + +static base::LazyInstance<extensions::GlobalShortcutListenerOzone> instance = + LAZY_INSTANCE_INITIALIZER; + +} // namespace + +namespace extensions { + +// static +GlobalShortcutListener* GlobalShortcutListener::GetInstance() { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + return instance.Pointer(); +} + +GlobalShortcutListenerOzone::GlobalShortcutListenerOzone() + : is_listening_(false) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // TODO(implementor): Remove this. + LOG(ERROR) << "GlobalShortcutListenerOzone object created"; +} + +GlobalShortcutListenerOzone::~GlobalShortcutListenerOzone() { + if (is_listening_) + StopListening(); +} + +void GlobalShortcutListenerOzone::StartListening() { + DCHECK(!is_listening_); // Don't start twice. + NOTIMPLEMENTED(); + is_listening_ = true; +} + +void GlobalShortcutListenerOzone::StopListening() { + DCHECK(is_listening_); // No point if we are not already listening. + NOTIMPLEMENTED(); + is_listening_ = false; +} + +void GlobalShortcutListenerOzone::RegisterAccelerator( + const ui::Accelerator& accelerator, + GlobalShortcutListener::Observer* observer) { + NOTIMPLEMENTED(); + // To implement: + // 1) Convert modifiers to platform specific modifiers. + // 2) Register for the hotkey. + // 3) If not successful, log why. + // 4) Else, call base class RegisterAccelerator. + + GlobalShortcutListener::RegisterAccelerator(accelerator, observer); +} + +void GlobalShortcutListenerOzone::UnregisterAccelerator( + const ui::Accelerator& accelerator, + GlobalShortcutListener::Observer* observer) { + NOTIMPLEMENTED(); + // To implement: + // 1) Unregister for the hotkey. + // 2) Call base class UnregisterAccelerator. + + GlobalShortcutListener::UnregisterAccelerator(accelerator, observer); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/global_shortcut_listener_ozone.h b/chrome/browser/extensions/global_shortcut_listener_ozone.h new file mode 100644 index 0000000000..01d2953553 --- /dev/null +++ b/chrome/browser/extensions/global_shortcut_listener_ozone.h @@ -0,0 +1,45 @@ +// Copyright 2013 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_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_OZONE_H_ +#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_OZONE_H_ + +#include "base/lazy_instance.h" +#include "chrome/browser/extensions/global_shortcut_listener.h" + +namespace extensions { + +// Ozone-specific implementation of the GlobalShortcutListener class that +// listens for global shortcuts. Handles basic keyboard intercepting and +// forwards its output to the base class for processing. +class GlobalShortcutListenerOzone : public GlobalShortcutListener { + public: + virtual ~GlobalShortcutListenerOzone(); + + virtual void StartListening() OVERRIDE; + virtual void StopListening() OVERRIDE; + + private: + friend struct base::DefaultLazyInstanceTraits<GlobalShortcutListenerOzone>; + + GlobalShortcutListenerOzone(); + + // Register an |accelerator| with the particular |observer|. + virtual void RegisterAccelerator( + const ui::Accelerator& accelerator, + GlobalShortcutListener::Observer* observer) OVERRIDE; + // Unregister an |accelerator| with the particular |observer|. + virtual void UnregisterAccelerator( + const ui::Accelerator& accelerator, + GlobalShortcutListener::Observer* observer) OVERRIDE; + + // Whether this object is listening for global shortcuts. + bool is_listening_; + + DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListenerOzone); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_OZONE_H_ diff --git a/chrome/browser/extensions/global_shortcut_listener_win.cc b/chrome/browser/extensions/global_shortcut_listener_win.cc index 462ff673de..a5987f7ab9 100644 --- a/chrome/browser/extensions/global_shortcut_listener_win.cc +++ b/chrome/browser/extensions/global_shortcut_listener_win.cc @@ -1,112 +1,116 @@ -// Copyright (c) 2013 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/extensions/global_shortcut_listener_win.h"
-
-#include "base/win/win_util.h"
-#include "content/public/browser/browser_thread.h"
-#include "ui/base/accelerators/accelerator.h"
-#include "ui/events/event_constants.h"
-#include "ui/events/keycodes/keyboard_code_conversion_win.h"
-
-using content::BrowserThread;
-
-namespace {
-
-static base::LazyInstance<extensions::GlobalShortcutListenerWin> instance =
- LAZY_INSTANCE_INITIALIZER;
-
-} // namespace
-
-namespace extensions {
-
-// static
-GlobalShortcutListener* GlobalShortcutListener::GetInstance() {
- CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- return instance.Pointer();
-}
-
-GlobalShortcutListenerWin::GlobalShortcutListenerWin()
- : is_listening_(false) {
- CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-}
-
-GlobalShortcutListenerWin::~GlobalShortcutListenerWin() {
- if (is_listening_)
- StopListening();
-}
-
-void GlobalShortcutListenerWin::StartListening() {
- DCHECK(!is_listening_); // Don't start twice.
- DCHECK(!hotkey_ids_.empty()); // Also don't start if no hotkey is registered.
- gfx::SingletonHwnd::GetInstance()->AddObserver(this);
- is_listening_ = true;
-}
-
-void GlobalShortcutListenerWin::StopListening() {
- DCHECK(is_listening_); // No point if we are not already listening.
- DCHECK(hotkey_ids_.empty()); // Make sure the map is clean before ending.
- gfx::SingletonHwnd::GetInstance()->RemoveObserver(this);
- is_listening_ = false;
-}
-
-void GlobalShortcutListenerWin::OnWndProc(HWND hwnd,
- UINT message,
- WPARAM wparam,
- LPARAM lparam) {
- if (message != WM_HOTKEY)
- return;
-
- int key_code = HIWORD(lparam);
- int modifiers = 0;
- modifiers |= (LOWORD(lparam) & MOD_SHIFT) ? ui::EF_SHIFT_DOWN : 0;
- modifiers |= (LOWORD(lparam) & MOD_ALT) ? ui::EF_ALT_DOWN : 0;
- modifiers |= (LOWORD(lparam) & MOD_CONTROL) ? ui::EF_CONTROL_DOWN : 0;
- ui::Accelerator accelerator(
- ui::KeyboardCodeForWindowsKeyCode(key_code), modifiers);
-
- instance.Get().NotifyKeyPressed(accelerator);
-}
-
-void GlobalShortcutListenerWin::RegisterAccelerator(
- const ui::Accelerator& accelerator,
- GlobalShortcutListener::Observer* observer) {
- int modifiers = 0;
- modifiers |= accelerator.IsShiftDown() ? MOD_SHIFT : 0;
- modifiers |= accelerator.IsCtrlDown() ? MOD_CONTROL : 0;
- modifiers |= accelerator.IsAltDown() ? MOD_ALT : 0;
- static int hotkey_id = 0;
- bool success = !!RegisterHotKey(
- gfx::SingletonHwnd::GetInstance()->hwnd(),
- hotkey_id,
- modifiers,
- accelerator.key_code());
-
- if (!success) {
- // Most likely error: 1409 (Hotkey already registered).
- LOG(ERROR) << "RegisterHotKey failed, error: " << GetLastError();
- return;
- }
-
- hotkey_ids_[accelerator] = hotkey_id++;
- GlobalShortcutListener::RegisterAccelerator(accelerator, observer);
-}
-
-void GlobalShortcutListenerWin::UnregisterAccelerator(
- const ui::Accelerator& accelerator,
- GlobalShortcutListener::Observer* observer) {
- // We may get asked to unregister something that we couldn't register (for
- // example if the shortcut was already taken by another app), so we
- // need to handle that gracefully.
- HotkeyIdMap::iterator it = hotkey_ids_.find(accelerator);
- if (it == hotkey_ids_.end())
- return;
-
- UnregisterHotKey(gfx::SingletonHwnd::GetInstance()->hwnd(), it->second);
- hotkey_ids_.erase(it);
-
- GlobalShortcutListener::UnregisterAccelerator(accelerator, observer);
-}
-
-} // namespace extensions
+// Copyright (c) 2013 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/extensions/global_shortcut_listener_win.h" + +#include "base/win/win_util.h" +#include "content/public/browser/browser_thread.h" +#include "ui/base/accelerators/accelerator.h" +#include "ui/events/event_constants.h" +#include "ui/events/keycodes/keyboard_code_conversion_win.h" + +using content::BrowserThread; + +namespace { + +static base::LazyInstance<extensions::GlobalShortcutListenerWin> instance = + LAZY_INSTANCE_INITIALIZER; + +} // namespace + +namespace extensions { + +// static +GlobalShortcutListener* GlobalShortcutListener::GetInstance() { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + return instance.Pointer(); +} + +GlobalShortcutListenerWin::GlobalShortcutListenerWin() + : is_listening_(false) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); +} + +GlobalShortcutListenerWin::~GlobalShortcutListenerWin() { + if (is_listening_) + StopListening(); +} + +void GlobalShortcutListenerWin::StartListening() { + DCHECK(!is_listening_); // Don't start twice. + DCHECK(!hotkey_ids_.empty()); // Also don't start if no hotkey is registered. + gfx::SingletonHwnd::GetInstance()->AddObserver(this); + is_listening_ = true; +} + +void GlobalShortcutListenerWin::StopListening() { + DCHECK(is_listening_); // No point if we are not already listening. + DCHECK(hotkey_ids_.empty()); // Make sure the map is clean before ending. + gfx::SingletonHwnd::GetInstance()->RemoveObserver(this); + is_listening_ = false; +} + +void GlobalShortcutListenerWin::OnWndProc(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) { + if (message != WM_HOTKEY) + return; + + int key_code = HIWORD(lparam); + int modifiers = 0; + modifiers |= (LOWORD(lparam) & MOD_SHIFT) ? ui::EF_SHIFT_DOWN : 0; + modifiers |= (LOWORD(lparam) & MOD_ALT) ? ui::EF_ALT_DOWN : 0; + modifiers |= (LOWORD(lparam) & MOD_CONTROL) ? ui::EF_CONTROL_DOWN : 0; + ui::Accelerator accelerator( + ui::KeyboardCodeForWindowsKeyCode(key_code), modifiers); + + instance.Get().NotifyKeyPressed(accelerator); +} + +void GlobalShortcutListenerWin::RegisterAccelerator( + const ui::Accelerator& accelerator, + GlobalShortcutListener::Observer* observer) { + int modifiers = 0; + modifiers |= accelerator.IsShiftDown() ? MOD_SHIFT : 0; + modifiers |= accelerator.IsCtrlDown() ? MOD_CONTROL : 0; + modifiers |= accelerator.IsAltDown() ? MOD_ALT : 0; + static int hotkey_id = 0; + bool success = !!RegisterHotKey( + gfx::SingletonHwnd::GetInstance()->hwnd(), + hotkey_id, + modifiers, + accelerator.key_code()); + + if (!success) { + // Most likely error: 1409 (Hotkey already registered). + LOG(ERROR) << "RegisterHotKey failed, error: " << GetLastError(); + return; + } + + hotkey_ids_[accelerator] = hotkey_id++; + GlobalShortcutListener::RegisterAccelerator(accelerator, observer); +} + +void GlobalShortcutListenerWin::UnregisterAccelerator( + const ui::Accelerator& accelerator, + GlobalShortcutListener::Observer* observer) { + // We may get asked to unregister something that we couldn't register (for + // example if the shortcut was already taken by another app), so we + // need to handle that gracefully. + HotkeyIdMap::iterator it = hotkey_ids_.find(accelerator); + if (it == hotkey_ids_.end()) + return; + + bool success = !!UnregisterHotKey( + gfx::SingletonHwnd::GetInstance()->hwnd(), it->second); + // This call should always succeed, as long as we pass in the right HWND and + // an id we've used to register before. + DCHECK(success); + + hotkey_ids_.erase(it); + GlobalShortcutListener::UnregisterAccelerator(accelerator, observer); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/global_shortcut_listener_win.h b/chrome/browser/extensions/global_shortcut_listener_win.h index 1844ee455d..f327f70043 100644 --- a/chrome/browser/extensions/global_shortcut_listener_win.h +++ b/chrome/browser/extensions/global_shortcut_listener_win.h @@ -1,59 +1,59 @@ -// Copyright (c) 2013 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_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_WIN_H_
-#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_WIN_H_
-
-#include <windows.h>
-
-#include "base/lazy_instance.h"
-#include "chrome/browser/extensions/global_shortcut_listener.h"
-#include "ui/gfx/win/singleton_hwnd.h"
-
-namespace extensions {
-
-// Windows-specific implementation of the GlobalShortcutListener class that
-// listens for global shortcuts. Handles setting up a keyboard hook and
-// forwarding its output to the base class for processing.
-class GlobalShortcutListenerWin : public GlobalShortcutListener,
- public gfx::SingletonHwnd::Observer {
- public:
- virtual ~GlobalShortcutListenerWin();
-
- virtual void StartListening() OVERRIDE;
- virtual void StopListening() OVERRIDE;
-
- private:
- friend struct base::DefaultLazyInstanceTraits<GlobalShortcutListenerWin>;
-
- GlobalShortcutListenerWin();
-
- // The implementation of our Window Proc, called by SingletonHwnd.
- virtual void OnWndProc(HWND hwnd,
- UINT message,
- WPARAM wparam,
- LPARAM lparam) OVERRIDE;
-
- // Register an |accelerator| with the particular |observer|.
- virtual void RegisterAccelerator(
- const ui::Accelerator& accelerator,
- GlobalShortcutListener::Observer* observer) OVERRIDE;
- // Unregister an |accelerator| with the particular |observer|.
- virtual void UnregisterAccelerator(
- const ui::Accelerator& accelerator,
- GlobalShortcutListener::Observer* observer) OVERRIDE;
-
- // Whether this object is listening for global shortcuts.
- bool is_listening_;
-
- // A map of registered accelerators and their registration ids.
- typedef std::map< ui::Accelerator, int > HotkeyIdMap;
- HotkeyIdMap hotkey_ids_;
-
- DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListenerWin);
-};
-
-} // namespace extensions
-
-#endif // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_WIN_H_
+// Copyright (c) 2013 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_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_WIN_H_ +#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_WIN_H_ + +#include <windows.h> + +#include "base/lazy_instance.h" +#include "chrome/browser/extensions/global_shortcut_listener.h" +#include "ui/gfx/win/singleton_hwnd.h" + +namespace extensions { + +// Windows-specific implementation of the GlobalShortcutListener class that +// listens for global shortcuts. Handles setting up a keyboard hook and +// forwarding its output to the base class for processing. +class GlobalShortcutListenerWin : public GlobalShortcutListener, + public gfx::SingletonHwnd::Observer { + public: + virtual ~GlobalShortcutListenerWin(); + + virtual void StartListening() OVERRIDE; + virtual void StopListening() OVERRIDE; + + private: + friend struct base::DefaultLazyInstanceTraits<GlobalShortcutListenerWin>; + + GlobalShortcutListenerWin(); + + // The implementation of our Window Proc, called by SingletonHwnd. + virtual void OnWndProc(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) OVERRIDE; + + // Register an |accelerator| with the particular |observer|. + virtual void RegisterAccelerator( + const ui::Accelerator& accelerator, + GlobalShortcutListener::Observer* observer) OVERRIDE; + // Unregister an |accelerator| with the particular |observer|. + virtual void UnregisterAccelerator( + const ui::Accelerator& accelerator, + GlobalShortcutListener::Observer* observer) OVERRIDE; + + // Whether this object is listening for global shortcuts. + bool is_listening_; + + // A map of registered accelerators and their registration ids. + typedef std::map< ui::Accelerator, int > HotkeyIdMap; + HotkeyIdMap hotkey_ids_; + + DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListenerWin); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_WIN_H_ diff --git a/chrome/browser/extensions/global_shortcut_listener_x11.cc b/chrome/browser/extensions/global_shortcut_listener_x11.cc index 53913c5968..0574c10984 100644 --- a/chrome/browser/extensions/global_shortcut_listener_x11.cc +++ b/chrome/browser/extensions/global_shortcut_listener_x11.cc @@ -156,7 +156,7 @@ void GlobalShortcutListenerX11::UnregisterAccelerator( #if defined(TOOLKIT_GTK) GdkFilterReturn GlobalShortcutListenerX11::OnXEvent(GdkXEvent* gdk_x_event, - GdkEvent* gdk_event) { + GdkEvent* gdk_event) { XEvent* x_event = static_cast<XEvent*>(gdk_x_event); if (x_event->type == KeyPress) OnXKeyPressEvent(x_event); diff --git a/chrome/browser/extensions/lazy_background_page_apitest.cc b/chrome/browser/extensions/lazy_background_page_apitest.cc index 7bd8ac95fe..df1bf3dfd3 100644 --- a/chrome/browser/extensions/lazy_background_page_apitest.cc +++ b/chrome/browser/extensions/lazy_background_page_apitest.cc @@ -104,7 +104,7 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, BrowserActionCreateTab) { // Lazy Background Page doesn't exist yet. ExtensionProcessManager* pm = extensions::ExtensionSystem::Get(browser()->profile())->process_manager(); - EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); + EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id())); int num_tabs_before = browser()->tab_strip_model()->count(); // Observe background page being created and closed after @@ -114,7 +114,7 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, BrowserActionCreateTab) { page_complete.Wait(); // Background page created a new tab before it closed. - EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); + EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id())); EXPECT_EQ(num_tabs_before + 1, browser()->tab_strip_model()->count()); EXPECT_EQ(std::string(chrome::kChromeUIExtensionsURL), browser()->tab_strip_model()->GetActiveWebContents()-> @@ -128,7 +128,7 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, // Lazy Background Page doesn't exist yet. ExtensionProcessManager* pm = extensions::ExtensionSystem::Get(browser()->profile())->process_manager(); - EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); + EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id())); int num_tabs_before = browser()->tab_strip_model()->count(); // Observe background page being created and closed after @@ -138,7 +138,7 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, page_complete.Wait(); // Background page is closed after creating a new tab. - EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); + EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id())); EXPECT_EQ(num_tabs_before + 1, browser()->tab_strip_model()->count()); } @@ -151,7 +151,7 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, BroadcastEvent) { // Lazy Background Page doesn't exist yet. ExtensionProcessManager* pm = extensions::ExtensionSystem::Get(browser()->profile())->process_manager(); - EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); + EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id())); int num_page_actions = browser()->window()->GetLocationBar()-> GetLocationBarForTesting()->PageActionVisibleCount(); @@ -164,7 +164,7 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, BroadcastEvent) { browser(), embedded_test_server()->GetURL("/extensions/test_file.html")); page_complete.Wait(); - EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); + EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id())); // Page action is shown. page_action_changed.Wait(); @@ -182,7 +182,7 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, Filters) { // Lazy Background Page doesn't exist yet. ExtensionProcessManager* pm = extensions::ExtensionSystem::Get(browser()->profile())->process_manager(); - EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); + EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id())); // Open a tab to a URL that will fire a webNavigation event. LazyBackgroundObserver page_complete; @@ -201,7 +201,7 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, OnInstalled) { // Lazy Background Page has been shut down. ExtensionProcessManager* pm = extensions::ExtensionSystem::Get(browser()->profile())->process_manager(); - EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); + EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id())); } // Tests that the lazy background page stays alive until all visible views are @@ -224,7 +224,7 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, WaitForView) { // to an extension page. ExtensionProcessManager* pm = extensions::ExtensionSystem::Get(browser()->profile())->process_manager(); - EXPECT_TRUE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); + EXPECT_TRUE(pm->GetBackgroundHostForExtension(last_loaded_extension_id())); // Close the new tab. browser()->tab_strip_model()->CloseWebContentsAt( @@ -232,7 +232,7 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, WaitForView) { page_complete.Wait(); // Lazy Background Page has been shut down. - EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); + EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id())); } // Tests that the lazy background page stays alive until all network requests @@ -253,7 +253,7 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, WaitForRequest) { ExtensionProcessManager* pm = extensions::ExtensionSystem::Get(browser()->profile())->process_manager(); extensions::ExtensionHost* host = - pm->GetBackgroundHostForExtension(last_loaded_extension_id_); + pm->GetBackgroundHostForExtension(last_loaded_extension_id()); ASSERT_TRUE(host); // Abort the request. @@ -264,7 +264,7 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, WaitForRequest) { page_complete.Wait(); // Lazy Background Page has been shut down. - EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); + EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id())); } // Tests that the lazy background page stays alive until all visible views are @@ -293,14 +293,14 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, MAYBE_WaitForNTP) { // to an extension page. ExtensionProcessManager* pm = extensions::ExtensionSystem::Get(browser()->profile())->process_manager(); - EXPECT_TRUE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); + EXPECT_TRUE(pm->GetBackgroundHostForExtension(last_loaded_extension_id())); // Navigate away from the NTP, which should close the event page. ui_test_utils::NavigateToURL(browser(), GURL("about:blank")); lazybg.Wait(); // Lazy Background Page has been shut down. - EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); + EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id())); } // See crbug.com/248437 @@ -332,8 +332,8 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, MAYBE_IncognitoSplitMode) { ExtensionProcessManager* pmi = extensions::ExtensionSystem::Get(incognito_browser->profile())-> process_manager(); - EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); - EXPECT_FALSE(pmi->GetBackgroundHostForExtension(last_loaded_extension_id_)); + EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id())); + EXPECT_FALSE(pmi->GetBackgroundHostForExtension(last_loaded_extension_id())); // Trigger a browserAction event in the original profile and ensure only // the original event page received it (since the event is scoped to the @@ -347,8 +347,9 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, MAYBE_IncognitoSplitMode) { page_complete.Wait(); // Only the original event page received the message. - EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); - EXPECT_FALSE(pmi->GetBackgroundHostForExtension(last_loaded_extension_id_)); + EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id())); + EXPECT_FALSE( + pmi->GetBackgroundHostForExtension(last_loaded_extension_id())); EXPECT_TRUE(listener.was_satisfied()); EXPECT_FALSE(listener_incognito.was_satisfied()); } @@ -370,8 +371,9 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, MAYBE_IncognitoSplitMode) { page2_complete.Wait(); // Both pages received the message. - EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); - EXPECT_FALSE(pmi->GetBackgroundHostForExtension(last_loaded_extension_id_)); + EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id())); + EXPECT_FALSE( + pmi->GetBackgroundHostForExtension(last_loaded_extension_id())); EXPECT_TRUE(listener.was_satisfied()); EXPECT_TRUE(listener_incognito.was_satisfied()); } @@ -386,7 +388,7 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, Messaging) { // Lazy Background Page doesn't exist yet. ExtensionProcessManager* pm = extensions::ExtensionSystem::Get(browser()->profile())->process_manager(); - EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); + EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id())); EXPECT_EQ(1, browser()->tab_strip_model()->count()); // Navigate to a page that opens a message channel to the background page. @@ -399,14 +401,14 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, Messaging) { // Background page got the content script's message and is still loaded // until we close the channel. EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); - EXPECT_TRUE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); + EXPECT_TRUE(pm->GetBackgroundHostForExtension(last_loaded_extension_id())); // Navigate away, closing the message channel and therefore the background // page. ui_test_utils::NavigateToURL(browser(), GURL("about:blank")); lazybg.WaitUntilClosed(); - EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); + EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id())); } // Tests that the lazy background page receives the unload event when we @@ -418,7 +420,7 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, OnUnload) { // Lazy Background Page has been shut down. ExtensionProcessManager* pm = extensions::ExtensionSystem::Get(browser()->profile())->process_manager(); - EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); + EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id())); // The browser action has a new title. BrowserActionTestUtil browser_action(browser()); @@ -481,7 +483,7 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, UpdateExtensionsPage) { // to an extension page. ExtensionProcessManager* pm = extensions::ExtensionSystem::Get(browser()->profile())->process_manager(); - EXPECT_TRUE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); + EXPECT_TRUE(pm->GetBackgroundHostForExtension(last_loaded_extension_id())); // Close the new tab. LazyBackgroundObserver page_complete; @@ -490,7 +492,7 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, UpdateExtensionsPage) { page_complete.WaitUntilClosed(); // Lazy Background Page has been shut down. - EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); + EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id())); // Verify that extensions page shows that the lazy background page is // inactive. diff --git a/chrome/browser/extensions/message_handler.cc b/chrome/browser/extensions/message_handler.cc deleted file mode 100644 index 8389b9a428..0000000000 --- a/chrome/browser/extensions/message_handler.cc +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2012 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/extensions/message_handler.h" - -#include "chrome/browser/extensions/api/messaging/message_service.h" -#include "chrome/browser/extensions/extension_system.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/common/extensions/extension_messages.h" -#include "content/public/browser/render_process_host.h" -#include "content/public/browser/render_view_host.h" -#include "content/public/browser/web_contents.h" -#include "extensions/browser/view_type_utils.h" - -using content::WebContents; - -namespace extensions { - -MessageHandler::MessageHandler( - content::RenderViewHost* render_view_host) - : content::RenderViewHostObserver(render_view_host) { -} - -MessageHandler::~MessageHandler() { -} - -bool MessageHandler::OnMessageReceived( - const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(MessageHandler, message) - IPC_MESSAGE_HANDLER(ExtensionHostMsg_PostMessage, OnPostMessage) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; -} - -void MessageHandler::RenderViewHostInitialized() { - WebContents* web_contents = - WebContents::FromRenderViewHost(render_view_host()); - Send(new ExtensionMsg_NotifyRenderViewType( - routing_id(), extensions::GetViewType(web_contents))); -} - -void MessageHandler::OnPostMessage(int port_id, - const std::string& message) { - Profile* profile = Profile::FromBrowserContext( - render_view_host()->GetProcess()->GetBrowserContext()); - MessageService* message_service = MessageService::Get(profile); - if (message_service) { - message_service->PostMessage(port_id, message); - } -} - -} // namespace extensions diff --git a/chrome/browser/extensions/message_handler.h b/chrome/browser/extensions/message_handler.h deleted file mode 100644 index 140105d22f..0000000000 --- a/chrome/browser/extensions/message_handler.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2012 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_EXTENSIONS_MESSAGE_HANDLER_H_ -#define CHROME_BROWSER_EXTENSIONS_MESSAGE_HANDLER_H_ - -#include <string> - -#include "content/public/browser/render_view_host_observer.h" - -namespace extensions { - -// Filters and dispatches extension-related IPC messages that arrive from -// renderers. There is one of these objects for each RenderViewHost in Chrome. -// Contrast this with extensions::TabHelper, which is only created for -// WebContents. -// -// TODO(aa): Handling of content script messaging should be able to move to EFD -// once there is an EFD for every RVHD where extension code can run. Then we -// could eliminate this class. Right now, we don't end up with an EFD for tab -// contents unless that tab contents is hosting chrome-extension:// URLs. That -// still leaves content scripts. See also: crbug.com/80307. -class MessageHandler : public content::RenderViewHostObserver { - public: - // |sender| is guaranteed to outlive this object. - explicit MessageHandler(content::RenderViewHost* render_view_host); - virtual ~MessageHandler(); - - // RenderViewHostObserver overrides. - virtual void RenderViewHostInitialized() OVERRIDE; - virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; - - private: - // Message handlers. - void OnPostMessage(int port_id, const std::string& message); - - DISALLOW_COPY_AND_ASSIGN(MessageHandler); -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_MESSAGE_HANDLER_H_ diff --git a/chrome/browser/extensions/notifications_apitest.cc b/chrome/browser/extensions/notifications_apitest.cc index 0fd58123d4..b1299cf4a8 100644 --- a/chrome/browser/extensions/notifications_apitest.cc +++ b/chrome/browser/extensions/notifications_apitest.cc @@ -74,5 +74,5 @@ IN_PROC_BROWSER_TEST_F(NotificationIdleTest, MAYBE_NotificationsAllowUnload) { // Lazy Background Page has been shut down. ExtensionProcessManager* pm = extensions::ExtensionSystem::Get(profile())->process_manager(); - EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); + EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id())); } diff --git a/chrome/browser/extensions/page_action_browsertest.cc b/chrome/browser/extensions/page_action_browsertest.cc index 52a0dd4fce..921b333d75 100644 --- a/chrome/browser/extensions/page_action_browsertest.cc +++ b/chrome/browser/extensions/page_action_browsertest.cc @@ -108,7 +108,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, UnloadPageAction) { ui_test_utils::NavigateToURL(browser(), feed_url); ASSERT_TRUE(WaitForPageActionCountChangeTo(1)); - UnloadExtension(last_loaded_extension_id_); + UnloadExtension(last_loaded_extension_id()); // Make sure the page action goes away when it's unloaded. ASSERT_TRUE(WaitForPageActionCountChangeTo(0)); diff --git a/chrome/browser/extensions/policy_handlers.cc b/chrome/browser/extensions/policy_handlers.cc new file mode 100644 index 0000000000..2637726236 --- /dev/null +++ b/chrome/browser/extensions/policy_handlers.cc @@ -0,0 +1,247 @@ +// Copyright 2013 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/extensions/policy_handlers.h" + +#include "base/logging.h" +#include "base/prefs/pref_value_map.h" +#include "chrome/browser/extensions/external_policy_loader.h" +#include "chrome/browser/policy/policy_error_map.h" +#include "chrome/browser/policy/policy_map.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/pref_names.h" +#include "grit/generated_resources.h" +#include "policy/policy_constants.h" + +namespace extensions { + +// ExtensionListPolicyHandler implementation ----------------------------------- + +ExtensionListPolicyHandler::ExtensionListPolicyHandler(const char* policy_name, + const char* pref_path, + bool allow_wildcards) + : policy::TypeCheckingPolicyHandler(policy_name, base::Value::TYPE_LIST), + pref_path_(pref_path), + allow_wildcards_(allow_wildcards) {} + +ExtensionListPolicyHandler::~ExtensionListPolicyHandler() {} + +bool ExtensionListPolicyHandler::CheckPolicySettings( + const policy::PolicyMap& policies, + policy::PolicyErrorMap* errors) { + return CheckAndGetList(policies, errors, NULL); +} + +void ExtensionListPolicyHandler::ApplyPolicySettings( + const policy::PolicyMap& policies, + PrefValueMap* prefs) { + scoped_ptr<base::ListValue> list; + policy::PolicyErrorMap errors; + if (CheckAndGetList(policies, &errors, &list) && list) + prefs->SetValue(pref_path(), list.release()); +} + +const char* ExtensionListPolicyHandler::pref_path() const { + return pref_path_; +} + +bool ExtensionListPolicyHandler::CheckAndGetList( + const policy::PolicyMap& policies, + policy::PolicyErrorMap* errors, + scoped_ptr<base::ListValue>* extension_ids) { + if (extension_ids) + extension_ids->reset(); + + const base::Value* value = NULL; + if (!CheckAndGetValue(policies, errors, &value)) + return false; + + if (!value) + return true; + + const base::ListValue* list_value = NULL; + if (!value->GetAsList(&list_value)) { + NOTREACHED(); + return false; + } + + // Filter the list, rejecting any invalid extension IDs. + scoped_ptr<base::ListValue> filtered_list(new base::ListValue()); + for (base::ListValue::const_iterator entry(list_value->begin()); + entry != list_value->end(); ++entry) { + std::string id; + if (!(*entry)->GetAsString(&id)) { + errors->AddError(policy_name(), + entry - list_value->begin(), + IDS_POLICY_TYPE_ERROR, + ValueTypeToString(base::Value::TYPE_STRING)); + continue; + } + if (!(allow_wildcards_ && id == "*") && + !extensions::Extension::IdIsValid(id)) { + errors->AddError(policy_name(), + entry - list_value->begin(), + IDS_POLICY_VALUE_FORMAT_ERROR); + continue; + } + filtered_list->Append(base::Value::CreateStringValue(id)); + } + + if (extension_ids) + *extension_ids = filtered_list.Pass(); + + return true; +} + +// ExtensionInstallForcelistPolicyHandler implementation ----------------------- + +ExtensionInstallForcelistPolicyHandler::ExtensionInstallForcelistPolicyHandler() + : policy::TypeCheckingPolicyHandler(policy::key::kExtensionInstallForcelist, + base::Value::TYPE_LIST) {} + +ExtensionInstallForcelistPolicyHandler:: + ~ExtensionInstallForcelistPolicyHandler() {} + +bool ExtensionInstallForcelistPolicyHandler::CheckPolicySettings( + const policy::PolicyMap& policies, + policy::PolicyErrorMap* errors) { + const base::Value* value; + return CheckAndGetValue(policies, errors, &value) && + ParseList(value, NULL, errors); +} + +void ExtensionInstallForcelistPolicyHandler::ApplyPolicySettings( + const policy::PolicyMap& policies, + PrefValueMap* prefs) { + const base::Value* value = NULL; + scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); + if (CheckAndGetValue(policies, NULL, &value) && + value && + ParseList(value, dict.get(), NULL)) { + prefs->SetValue(prefs::kExtensionInstallForceList, dict.release()); + } +} + +bool ExtensionInstallForcelistPolicyHandler::ParseList( + const base::Value* policy_value, + base::DictionaryValue* extension_dict, + policy::PolicyErrorMap* errors) { + if (!policy_value) + return true; + + const base::ListValue* policy_list_value = NULL; + if (!policy_value->GetAsList(&policy_list_value)) { + // This should have been caught in CheckPolicySettings. + NOTREACHED(); + return false; + } + + for (base::ListValue::const_iterator entry(policy_list_value->begin()); + entry != policy_list_value->end(); ++entry) { + std::string entry_string; + if (!(*entry)->GetAsString(&entry_string)) { + if (errors) { + errors->AddError(policy_name(), + entry - policy_list_value->begin(), + IDS_POLICY_TYPE_ERROR, + ValueTypeToString(base::Value::TYPE_STRING)); + } + continue; + } + + // Each string item of the list has the following form: + // <extension_id>;<update_url> + // Note: The update URL might also contain semicolons. + size_t pos = entry_string.find(';'); + if (pos == std::string::npos) { + if (errors) { + errors->AddError(policy_name(), + entry - policy_list_value->begin(), + IDS_POLICY_VALUE_FORMAT_ERROR); + } + continue; + } + + std::string extension_id = entry_string.substr(0, pos); + std::string update_url = entry_string.substr(pos+1); + if (!extensions::Extension::IdIsValid(extension_id) || + !GURL(update_url).is_valid()) { + if (errors) { + errors->AddError(policy_name(), + entry - policy_list_value->begin(), + IDS_POLICY_VALUE_FORMAT_ERROR); + } + continue; + } + + if (extension_dict) { + extensions::ExternalPolicyLoader::AddExtension( + extension_dict, extension_id, update_url); + } + } + + return true; +} + +// ExtensionURLPatternListPolicyHandler implementation ------------------------- + +ExtensionURLPatternListPolicyHandler::ExtensionURLPatternListPolicyHandler( + const char* policy_name, + const char* pref_path) + : policy::TypeCheckingPolicyHandler(policy_name, base::Value::TYPE_LIST), + pref_path_(pref_path) {} + +ExtensionURLPatternListPolicyHandler::~ExtensionURLPatternListPolicyHandler() {} + +bool ExtensionURLPatternListPolicyHandler::CheckPolicySettings( + const policy::PolicyMap& policies, + policy::PolicyErrorMap* errors) { + const base::Value* value = NULL; + if (!CheckAndGetValue(policies, errors, &value)) + return false; + + if (!value) + return true; + + const base::ListValue* list_value = NULL; + if (!value->GetAsList(&list_value)) { + NOTREACHED(); + return false; + } + + // Check that the list contains valid URLPattern strings only. + for (base::ListValue::const_iterator entry(list_value->begin()); + entry != list_value->end(); ++entry) { + std::string url_pattern_string; + if (!(*entry)->GetAsString(&url_pattern_string)) { + errors->AddError(policy_name(), + entry - list_value->begin(), + IDS_POLICY_TYPE_ERROR, + ValueTypeToString(base::Value::TYPE_STRING)); + return false; + } + + URLPattern pattern(URLPattern::SCHEME_ALL); + if (pattern.Parse(url_pattern_string) != URLPattern::PARSE_SUCCESS) { + errors->AddError(policy_name(), + entry - list_value->begin(), + IDS_POLICY_VALUE_FORMAT_ERROR); + return false; + } + } + + return true; +} + +void ExtensionURLPatternListPolicyHandler::ApplyPolicySettings( + const policy::PolicyMap& policies, + PrefValueMap* prefs) { + if (!pref_path_) + return; + const Value* value = policies.GetValue(policy_name()); + if (value) + prefs->SetValue(pref_path_, value->DeepCopy()); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/policy_handlers.h b/chrome/browser/extensions/policy_handlers.h new file mode 100644 index 0000000000..4446238e70 --- /dev/null +++ b/chrome/browser/extensions/policy_handlers.h @@ -0,0 +1,93 @@ +// Copyright 2013 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_EXTENSIONS_POLICY_HANDLERS_H_ +#define CHROME_BROWSER_EXTENSIONS_POLICY_HANDLERS_H_ + +#include "base/memory/scoped_ptr.h" +#include "base/values.h" +#include "chrome/browser/policy/configuration_policy_handler.h" + +namespace policy { +class PolicyMap; +class PolicyErrorMap; +} // namespace policy + +namespace extensions { + +// Implements additional checks for policies that are lists of extension IDs. +class ExtensionListPolicyHandler + : public policy::TypeCheckingPolicyHandler { + public: + ExtensionListPolicyHandler(const char* policy_name, + const char* pref_path, + bool allow_wildcards); + virtual ~ExtensionListPolicyHandler(); + + // ConfigurationPolicyHandler methods: + virtual bool CheckPolicySettings(const policy::PolicyMap& policies, + policy::PolicyErrorMap* errors) OVERRIDE; + virtual void ApplyPolicySettings(const policy::PolicyMap& policies, + PrefValueMap* prefs) OVERRIDE; + + protected: + const char* pref_path() const; + + // Runs sanity checks on the policy value and returns it in |extension_ids|. + bool CheckAndGetList(const policy::PolicyMap& policies, + policy::PolicyErrorMap* errors, + scoped_ptr<base::ListValue>* extension_ids); + + private: + const char* pref_path_; + bool allow_wildcards_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionListPolicyHandler); +}; + +class ExtensionInstallForcelistPolicyHandler + : public policy::TypeCheckingPolicyHandler { + public: + ExtensionInstallForcelistPolicyHandler(); + virtual ~ExtensionInstallForcelistPolicyHandler(); + + // ConfigurationPolicyHandler methods: + virtual bool CheckPolicySettings(const policy::PolicyMap& policies, + policy::PolicyErrorMap* errors) OVERRIDE; + virtual void ApplyPolicySettings(const policy::PolicyMap& policies, + PrefValueMap* prefs) OVERRIDE; + + private: + // Parses the data in |policy_value| and writes them to |extension_dict|. + bool ParseList(const base::Value* policy_value, + base::DictionaryValue* extension_dict, + policy::PolicyErrorMap* errors); + + DISALLOW_COPY_AND_ASSIGN(ExtensionInstallForcelistPolicyHandler); +}; + +// Implements additional checks for policies that are lists of extension +// URLPatterns. +class ExtensionURLPatternListPolicyHandler + : public policy::TypeCheckingPolicyHandler { + public: + ExtensionURLPatternListPolicyHandler(const char* policy_name, + const char* pref_path); + virtual ~ExtensionURLPatternListPolicyHandler(); + + // ConfigurationPolicyHandler methods: + virtual bool CheckPolicySettings(const policy::PolicyMap& policies, + policy::PolicyErrorMap* errors) OVERRIDE; + virtual void ApplyPolicySettings(const policy::PolicyMap& policies, + PrefValueMap* prefs) OVERRIDE; + + private: + const char* pref_path_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionURLPatternListPolicyHandler); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_POLICY_HANDLERS_H_ diff --git a/chrome/browser/extensions/policy_handlers_unittest.cc b/chrome/browser/extensions/policy_handlers_unittest.cc new file mode 100644 index 0000000000..14b8409ba5 --- /dev/null +++ b/chrome/browser/extensions/policy_handlers_unittest.cc @@ -0,0 +1,283 @@ +// Copyright 2013 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/prefs/pref_value_map.h" +#include "chrome/browser/extensions/external_policy_loader.h" +#include "chrome/browser/extensions/policy_handlers.h" +#include "chrome/browser/policy/policy_error_map.h" +#include "chrome/browser/policy/policy_map.h" +#include "chrome/common/pref_names.h" +#include "policy/policy_constants.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace extensions { + +const char kTestPref[] = "unit_test.test_pref"; + +TEST(ExtensionListPolicyHandlerTest, CheckPolicySettings) { + base::ListValue list; + policy::PolicyMap policy_map; + policy::PolicyErrorMap errors; + ExtensionListPolicyHandler handler( + policy::key::kExtensionInstallBlacklist, kTestPref, true); + + policy_map.Set(policy::key::kExtensionInstallBlacklist, + policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, + list.DeepCopy(), + NULL); + errors.Clear(); + EXPECT_TRUE(handler.CheckPolicySettings(policy_map, &errors)); + EXPECT_TRUE(errors.empty()); + + list.Append(Value::CreateStringValue("abcdefghijklmnopabcdefghijklmnop")); + policy_map.Set(policy::key::kExtensionInstallBlacklist, + policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, + list.DeepCopy(), + NULL); + errors.Clear(); + EXPECT_TRUE(handler.CheckPolicySettings(policy_map, &errors)); + EXPECT_TRUE(errors.empty()); + + list.Append(Value::CreateStringValue("*")); + policy_map.Set(policy::key::kExtensionInstallBlacklist, + policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, + list.DeepCopy(), + NULL); + errors.Clear(); + EXPECT_TRUE(handler.CheckPolicySettings(policy_map, &errors)); + EXPECT_TRUE(errors.empty()); + + list.Append(Value::CreateStringValue("invalid")); + policy_map.Set(policy::key::kExtensionInstallBlacklist, + policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, + list.DeepCopy(), + NULL); + errors.Clear(); + EXPECT_TRUE(handler.CheckPolicySettings(policy_map, &errors)); + EXPECT_FALSE(errors.empty()); + EXPECT_FALSE( + errors.GetErrors(policy::key::kExtensionInstallBlacklist).empty()); +} + +TEST(ExtensionListPolicyHandlerTest, ApplyPolicySettings) { + base::ListValue policy; + base::ListValue expected; + policy::PolicyMap policy_map; + PrefValueMap prefs; + base::Value* value = NULL; + ExtensionListPolicyHandler handler( + policy::key::kExtensionInstallBlacklist, kTestPref, false); + + policy.Append(Value::CreateStringValue("abcdefghijklmnopabcdefghijklmnop")); + expected.Append(Value::CreateStringValue("abcdefghijklmnopabcdefghijklmnop")); + + policy_map.Set(policy::key::kExtensionInstallBlacklist, + policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, + policy.DeepCopy(), + NULL); + handler.ApplyPolicySettings(policy_map, &prefs); + EXPECT_TRUE(prefs.GetValue(kTestPref, &value)); + EXPECT_TRUE(base::Value::Equals(&expected, value)); + + policy.Append(Value::CreateStringValue("invalid")); + policy_map.Set(policy::key::kExtensionInstallBlacklist, + policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, + policy.DeepCopy(), + NULL); + handler.ApplyPolicySettings(policy_map, &prefs); + EXPECT_TRUE(prefs.GetValue(kTestPref, &value)); + EXPECT_TRUE(base::Value::Equals(&expected, value)); +} + +TEST(ExtensionInstallForcelistPolicyHandlerTest, CheckPolicySettings) { + base::ListValue list; + policy::PolicyMap policy_map; + policy::PolicyErrorMap errors; + ExtensionInstallForcelistPolicyHandler handler; + + policy_map.Set(policy::key::kExtensionInstallForcelist, + policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, + list.DeepCopy(), + NULL); + errors.Clear(); + EXPECT_TRUE(handler.CheckPolicySettings(policy_map, &errors)); + EXPECT_TRUE(errors.empty()); + + list.AppendString("abcdefghijklmnopabcdefghijklmnop;http://example.com"); + policy_map.Set(policy::key::kExtensionInstallForcelist, + policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, + list.DeepCopy(), + NULL); + errors.Clear(); + EXPECT_TRUE(handler.CheckPolicySettings(policy_map, &errors)); + EXPECT_TRUE(errors.empty()); + + // Add an erroneous entry. This should generate an error, but the good + // entry should still be translated successfully. + list.AppendString("adfasdf;http://example.com"); + policy_map.Set(policy::key::kExtensionInstallForcelist, + policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, + list.DeepCopy(), + NULL); + errors.Clear(); + EXPECT_TRUE(handler.CheckPolicySettings(policy_map, &errors)); + EXPECT_EQ(1U, errors.size()); + + // Add an entry with bad URL, which should generate another error. + list.AppendString("abcdefghijklmnopabcdefghijklmnop;nourl"); + policy_map.Set(policy::key::kExtensionInstallForcelist, + policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, + list.DeepCopy(), + NULL); + errors.Clear(); + EXPECT_TRUE(handler.CheckPolicySettings(policy_map, &errors)); + EXPECT_EQ(2U, errors.size()); + + // Just an extension ID should also generate an error. + list.AppendString("abcdefghijklmnopabcdefghijklmnop"); + policy_map.Set(policy::key::kExtensionInstallForcelist, + policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, + list.DeepCopy(), + NULL); + errors.Clear(); + EXPECT_TRUE(handler.CheckPolicySettings(policy_map, &errors)); + EXPECT_EQ(3U, errors.size()); +} + +TEST(ExtensionInstallForcelistPolicyHandlerTest, ApplyPolicySettings) { + base::ListValue policy; + base::DictionaryValue expected; + policy::PolicyMap policy_map; + PrefValueMap prefs; + base::Value* value = NULL; + ExtensionInstallForcelistPolicyHandler handler; + + handler.ApplyPolicySettings(policy_map, &prefs); + EXPECT_FALSE(prefs.GetValue(prefs::kExtensionInstallForceList, &value)); + EXPECT_FALSE(value); + + policy_map.Set(policy::key::kExtensionInstallForcelist, + policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, + policy.DeepCopy(), + NULL); + handler.ApplyPolicySettings(policy_map, &prefs); + EXPECT_TRUE(prefs.GetValue(prefs::kExtensionInstallForceList, &value)); + EXPECT_TRUE(base::Value::Equals(&expected, value)); + + policy.AppendString("abcdefghijklmnopabcdefghijklmnop;http://example.com"); + extensions::ExternalPolicyLoader::AddExtension( + &expected, "abcdefghijklmnopabcdefghijklmnop", "http://example.com"); + policy_map.Set(policy::key::kExtensionInstallForcelist, + policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, + policy.DeepCopy(), + NULL); + handler.ApplyPolicySettings(policy_map, &prefs); + EXPECT_TRUE(prefs.GetValue(prefs::kExtensionInstallForceList, &value)); + EXPECT_TRUE(base::Value::Equals(&expected, value)); + + policy.AppendString("invalid"); + policy_map.Set(policy::key::kExtensionInstallForcelist, + policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, + policy.DeepCopy(), + NULL); + handler.ApplyPolicySettings(policy_map, &prefs); + EXPECT_TRUE(prefs.GetValue(prefs::kExtensionInstallForceList, &value)); + EXPECT_TRUE(base::Value::Equals(&expected, value)); +} + +TEST(ExtensionURLPatternListPolicyHandlerTest, CheckPolicySettings) { + base::ListValue list; + policy::PolicyMap policy_map; + policy::PolicyErrorMap errors; + ExtensionURLPatternListPolicyHandler handler( + policy::key::kExtensionInstallSources, kTestPref); + + policy_map.Set(policy::key::kExtensionInstallSources, + policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, + list.DeepCopy(), + NULL); + errors.Clear(); + EXPECT_TRUE(handler.CheckPolicySettings(policy_map, &errors)); + EXPECT_TRUE(errors.empty()); + + list.Append(Value::CreateStringValue("http://*.google.com/*")); + policy_map.Set(policy::key::kExtensionInstallSources, + policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, + list.DeepCopy(), + NULL); + errors.Clear(); + EXPECT_TRUE(handler.CheckPolicySettings(policy_map, &errors)); + EXPECT_TRUE(errors.empty()); + + list.Append(Value::CreateStringValue("<all_urls>")); + policy_map.Set(policy::key::kExtensionInstallSources, + policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, + list.DeepCopy(), + NULL); + errors.Clear(); + EXPECT_TRUE(handler.CheckPolicySettings(policy_map, &errors)); + EXPECT_TRUE(errors.empty()); + + list.Append(Value::CreateStringValue("invalid")); + policy_map.Set(policy::key::kExtensionInstallSources, + policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, + list.DeepCopy(), + NULL); + errors.Clear(); + EXPECT_FALSE(handler.CheckPolicySettings(policy_map, &errors)); + EXPECT_FALSE(errors.empty()); + EXPECT_FALSE(errors.GetErrors(policy::key::kExtensionInstallSources).empty()); + + // URLPattern syntax has a different way to express 'all urls'. Though '*' + // would be compatible today, it would be brittle, so we disallow. + list.Append(Value::CreateStringValue("*")); + policy_map.Set(policy::key::kExtensionInstallSources, + policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, + list.DeepCopy(), + NULL); + errors.Clear(); + EXPECT_FALSE(handler.CheckPolicySettings(policy_map, &errors)); + EXPECT_FALSE(errors.empty()); + EXPECT_FALSE(errors.GetErrors(policy::key::kExtensionInstallSources).empty()); +} + +TEST(ExtensionURLPatternListPolicyHandlerTest, ApplyPolicySettings) { + base::ListValue list; + policy::PolicyMap policy_map; + PrefValueMap prefs; + base::Value* value = NULL; + ExtensionURLPatternListPolicyHandler handler( + policy::key::kExtensionInstallSources, kTestPref); + + list.Append(Value::CreateStringValue("https://corp.monkey.net/*")); + policy_map.Set(policy::key::kExtensionInstallSources, + policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, + list.DeepCopy(), + NULL); + handler.ApplyPolicySettings(policy_map, &prefs); + ASSERT_TRUE(prefs.GetValue(kTestPref, &value)); + EXPECT_TRUE(base::Value::Equals(&list, value)); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/user_script_master.cc b/chrome/browser/extensions/user_script_master.cc index 95b8f6aa33..4a9ff1ca01 100644 --- a/chrome/browser/extensions/user_script_master.cc +++ b/chrome/browser/extensions/user_script_master.cc @@ -19,6 +19,7 @@ #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/image_loader.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/extensions/api/i18n/default_locale_handler.h" @@ -373,8 +374,9 @@ void UserScriptMaster::Observe(int type, extensions_info_[extension->id()] = ExtensionSet::ExtensionPathAndDefaultLocale( extension->path(), LocaleInfo::GetDefaultLocale(extension)); - bool incognito_enabled = extensions::ExtensionSystem::Get(profile_)-> - extension_service()->IsIncognitoEnabled(extension->id()); + bool incognito_enabled = extension_util::IsIncognitoEnabled( + extension->id(), + extensions::ExtensionSystem::Get(profile_)->extension_service()); const UserScriptList& scripts = ContentScriptsInfo::GetContentScripts(extension); for (UserScriptList::const_iterator iter = scripts.begin(); diff --git a/chrome/browser/extensions/webstore_installer.cc b/chrome/browser/extensions/webstore_installer.cc index ab1d1c907e..5bf00a9447 100644 --- a/chrome/browser/extensions/webstore_installer.cc +++ b/chrome/browser/extensions/webstore_installer.cc @@ -4,6 +4,8 @@ #include "chrome/browser/extensions/webstore_installer.h" +#include <vector> + #include "base/basictypes.h" #include "base/bind.h" #include "base/command_line.h" @@ -19,6 +21,7 @@ #include "chrome/browser/download/download_prefs.h" #include "chrome/browser/download/download_stats.h" #include "chrome/browser/extensions/crx_installer.h" +#include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/extensions/install_tracker.h" #include "chrome/browser/extensions/install_tracker_factory.h" #include "chrome/browser/profiles/profile.h" @@ -27,6 +30,7 @@ #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_constants.h" +#include "chrome/common/extensions/manifest_handlers/shared_module_info.h" #include "chrome/common/omaha_query_params/omaha_query_params.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/download_manager.h" @@ -68,6 +72,9 @@ const char kInstallCanceledError[] = "Install canceled"; const char kDownloadInterruptedError[] = "Download interrupted"; const char kInvalidDownloadError[] = "Download was not a valid extension or user script"; +const char kDependencyNotFoundError[] = "Dependency not found"; +const char kDependencyNotSharedModuleError[] = + "Dependency is not shared module"; const char kInlineInstallSource[] = "inline"; const char kDefaultInstallSource[] = "ondemand"; const char kAppLauncherInstallSource[] = "applauncher"; @@ -124,7 +131,19 @@ namespace extensions { // static GURL WebstoreInstaller::GetWebstoreInstallURL( - const std::string& extension_id, const std::string& install_source) { + const std::string& extension_id, InstallSource source) { + std::string install_source; + switch (source) { + case INSTALL_SOURCE_INLINE: + install_source = kInlineInstallSource; + break; + case INSTALL_SOURCE_APP_LAUNCHER: + install_source = kAppLauncherInstallSource; + break; + case INSTALL_SOURCE_OTHER: + install_source = kDefaultInstallSource; + } + CommandLine* cmd_line = CommandLine::ForCurrentProcess(); if (cmd_line->HasSwitch(switches::kAppsGalleryDownloadURL)) { std::string download_url = @@ -164,7 +183,7 @@ WebstoreInstaller::Approval::Approval() skip_post_install_ui(false), skip_install_dialog(false), enable_launcher(false), - strict_manifest_check(true) { + manifest_check_level(MANIFEST_CHECK_LEVEL_STRICT) { } scoped_ptr<WebstoreInstaller::Approval> @@ -175,6 +194,15 @@ WebstoreInstaller::Approval::CreateWithInstallPrompt(Profile* profile) { } scoped_ptr<WebstoreInstaller::Approval> +WebstoreInstaller::Approval::CreateForSharedModule(Profile* profile) { + scoped_ptr<Approval> result(new Approval()); + result->profile = profile; + result->skip_install_dialog = true; + result->manifest_check_level = MANIFEST_CHECK_LEVEL_NONE; + return result.Pass(); +} + +scoped_ptr<WebstoreInstaller::Approval> WebstoreInstaller::Approval::CreateWithNoInstallPrompt( Profile* profile, const std::string& extension_id, @@ -187,7 +215,8 @@ WebstoreInstaller::Approval::CreateWithNoInstallPrompt( new Manifest(Manifest::INVALID_LOCATION, scoped_ptr<DictionaryValue>(parsed_manifest->DeepCopy()))); result->skip_install_dialog = true; - result->strict_manifest_check = strict_manifest_check; + result->manifest_check_level = strict_manifest_check ? + MANIFEST_CHECK_LEVEL_STRICT : MANIFEST_CHECK_LEVEL_LOOSE; return result.Pass(); } @@ -208,25 +237,14 @@ WebstoreInstaller::WebstoreInstaller(Profile* profile, delegate_(delegate), controller_(controller), id_(id), + install_source_(source), download_item_(NULL), - approval_(approval.release()) { + approval_(approval.release()), + total_modules_(0), + download_started_(false) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(controller_); - const char* install_source = ""; - switch (source) { - case INSTALL_SOURCE_INLINE: - install_source = kInlineInstallSource; - break; - case INSTALL_SOURCE_APP_LAUNCHER: - install_source = kAppLauncherInstallSource; - break; - case INSTALL_SOURCE_OTHER: - install_source = kDefaultInstallSource; - } - - download_url_ = GetWebstoreInstallURL(id, install_source); - registrar_.Add(this, chrome::NOTIFICATION_CRX_INSTALLER_DONE, content::NotificationService::AllSources()); registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED, @@ -244,12 +262,30 @@ void WebstoreInstaller::Start() { return; } - base::FilePath download_path = DownloadPrefs::FromDownloadManager( - BrowserContext::GetDownloadManager(profile_))->DownloadPath(); - BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, - base::Bind(&GetDownloadFilePath, download_path, id_, - base::Bind(&WebstoreInstaller::StartDownload, this))); + ExtensionService* extension_service = + ExtensionSystem::Get(profile_)->extension_service(); + if (approval_.get() && approval_->dummy_extension) { + ExtensionService::ImportStatus status = + extension_service->CheckImports(approval_->dummy_extension, + &pending_modules_, &pending_modules_); + // For this case, it is because some imports are not shared modules. + if (status == ExtensionService::IMPORT_STATUS_UNRECOVERABLE) { + ReportFailure(kDependencyNotSharedModuleError, + FAILURE_REASON_DEPENDENCY_NOT_SHARED_MODULE); + return; + } + } + + // Add the extension main module into the list. + SharedModuleInfo::ImportInfo info; + info.extension_id = id_; + pending_modules_.push_back(info); + + total_modules_ = pending_modules_.size(); + + // TODO(crbug.com/305343): Query manifest of dependencises before + // downloading & installing those dependencies. + DownloadNextPendingModule(); std::string name; if (!approval_->manifest->value()->GetString(manifest_keys::kName, &name)) { @@ -282,8 +318,31 @@ void WebstoreInstaller::Observe(int type, CHECK(profile_->IsSameProfile(content::Source<Profile>(source).ptr())); const Extension* extension = content::Details<const InstalledExtensionInfo>(details)->extension; - if (id_ == extension->id()) + CHECK(!pending_modules_.empty()); + SharedModuleInfo::ImportInfo info = pending_modules_.front(); + pending_modules_.pop_front(); + CHECK_EQ(extension->id(), info.extension_id); + + if (pending_modules_.empty()) { + CHECK_EQ(extension->id(), id_); ReportSuccess(); + } else { + const Version version_required(info.minimum_version); + if (version_required.IsValid() && + extension->version()->CompareTo(version_required) < 0) { + // It should not happen, CrxInstaller will make sure the version is + // equal or newer than version_required. + ReportFailure(kDependencyNotFoundError, + FAILURE_REASON_DEPENDENCY_NOT_FOUND); + } else if (!SharedModuleInfo::IsSharedModule(extension)) { + // It should not happen, CrxInstaller will make sure it is a shared + // module. + ReportFailure(kDependencyNotSharedModuleError, + FAILURE_REASON_DEPENDENCY_NOT_SHARED_MODULE); + } else { + DownloadNextPendingModule(); + } + } break; } @@ -333,12 +392,33 @@ void WebstoreInstaller::OnDownloadStarted( } DCHECK_EQ(net::OK, error); + DCHECK(!pending_modules_.empty()); download_item_ = item; download_item_->AddObserver(this); - if (approval_) - download_item_->SetUserData(kApprovalKey, approval_.release()); - if (delegate_) - delegate_->OnExtensionDownloadStarted(id_, download_item_); + if (pending_modules_.size() > 1) { + // We are downloading a shared module. We need create an approval for it. + scoped_ptr<Approval> approval = Approval::CreateForSharedModule(profile_); + const SharedModuleInfo::ImportInfo& info = pending_modules_.front(); + approval->extension_id = info.extension_id; + const Version version_required(info.minimum_version); + + if (version_required.IsValid()) { + approval->minimum_version.reset( + new Version(version_required)); + } + download_item_->SetUserData(kApprovalKey, approval.release()); + } else { + // It is for the main module of the extension. We should use the provided + // |approval_|. + if (approval_) + download_item_->SetUserData(kApprovalKey, approval_.release()); + } + + if (!download_started_) { + if (delegate_) + delegate_->OnExtensionDownloadStarted(id_, download_item_); + download_started_ = true; + } } void WebstoreInstaller::OnDownloadUpdated(DownloadItem* download) { @@ -355,23 +435,29 @@ void WebstoreInstaller::OnDownloadUpdated(DownloadItem* download) { // Wait for other notifications if the download is really an extension. if (!download_crx_util::IsExtensionDownload(*download)) { ReportFailure(kInvalidDownloadError, FAILURE_REASON_OTHER); - } else { + } else if (pending_modules_.empty()) { + // The download is the last module - the extension main module. if (delegate_) delegate_->OnExtensionDownloadProgress(id_, download); - extensions::InstallTracker* tracker = extensions::InstallTrackerFactory::GetForProfile(profile_); tracker->OnDownloadProgress(id_, 100); } break; case DownloadItem::IN_PROGRESS: { - if (delegate_) + if (delegate_ && pending_modules_.size() == 1) { + // Only report download progress for the main module to |delegrate_|. delegate_->OnExtensionDownloadProgress(id_, download); - - extensions::InstallTracker* tracker = + } + int percent = download->PercentComplete(); + // Only report progress if precent is more than 0 + if (percent >= 0) { + int finished_modules = total_modules_ - pending_modules_.size(); + percent = (percent + finished_modules * 100) / total_modules_; + extensions::InstallTracker* tracker = extensions::InstallTrackerFactory::GetForProfile(profile_); - tracker->OnDownloadProgress(id_, download->PercentComplete()); - + tracker->OnDownloadProgress(id_, percent); + } break; } default: @@ -386,6 +472,27 @@ void WebstoreInstaller::OnDownloadDestroyed(DownloadItem* download) { download_item_ = NULL; } +void WebstoreInstaller::DownloadNextPendingModule() { + CHECK(!pending_modules_.empty()); + if (pending_modules_.size() == 1) { + DCHECK_EQ(id_, pending_modules_.front().extension_id); + DownloadCrx(id_, install_source_); + } else { + DownloadCrx(pending_modules_.front().extension_id, INSTALL_SOURCE_OTHER); + } +} + +void WebstoreInstaller::DownloadCrx( + const std::string& extension_id, InstallSource source) { + download_url_ = GetWebstoreInstallURL(extension_id, source); + base::FilePath download_path = DownloadPrefs::FromDownloadManager( + BrowserContext::GetDownloadManager(profile_))->DownloadPath(); + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + base::Bind(&GetDownloadFilePath, download_path, id_, + base::Bind(&WebstoreInstaller::StartDownload, this))); +} + // http://crbug.com/165634 // http://crbug.com/126013 // The current working theory is that one of the many pointers dereferenced in diff --git a/chrome/browser/extensions/webstore_installer.h b/chrome/browser/extensions/webstore_installer.h index 629788101c..56458e2ac1 100644 --- a/chrome/browser/extensions/webstore_installer.h +++ b/chrome/browser/extensions/webstore_installer.h @@ -5,15 +5,17 @@ #ifndef CHROME_BROWSER_EXTENSIONS_WEBSTORE_INSTALLER_H_ #define CHROME_BROWSER_EXTENSIONS_WEBSTORE_INSTALLER_H_ +#include <list> #include <string> -#include <vector> #include "base/compiler_specific.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/supports_user_data.h" #include "base/values.h" +#include "base/version.h" #include "chrome/browser/extensions/extension_install_prompt.h" +#include "chrome/common/extensions/manifest_handlers/shared_module_info.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/download_item.h" #include "content/public/browser/notification_observer.h" @@ -34,6 +36,7 @@ class NavigationController; namespace extensions { +class Extension; class Manifest; // Downloads and installs extensions from the web store. @@ -52,9 +55,23 @@ class WebstoreInstaller :public content::NotificationObserver, enum FailureReason { FAILURE_REASON_CANCELLED, + FAILURE_REASON_DEPENDENCY_NOT_FOUND, + FAILURE_REASON_DEPENDENCY_NOT_SHARED_MODULE, FAILURE_REASON_OTHER }; + enum ManifestCheckLevel { + // Do not check for any manifest equality. + MANIFEST_CHECK_LEVEL_NONE, + + // Only check that the expected and actual permissions have the same + // effective permissions. + MANIFEST_CHECK_LEVEL_LOOSE, + + // All data in the expected and actual manifests must match. + MANIFEST_CHECK_LEVEL_STRICT, + }; + class Delegate { public: virtual void OnExtensionDownloadStarted(const std::string& id, @@ -77,6 +94,9 @@ class WebstoreInstaller :public content::NotificationObserver, struct Approval : public base::SupportsUserData::Data { static scoped_ptr<Approval> CreateWithInstallPrompt(Profile* profile); + // Creates an Approval for installing a shared module. + static scoped_ptr<Approval> CreateForSharedModule(Profile* profile); + // Creates an Approval that will skip putting up an install confirmation // prompt if the actual manifest from the extension to be installed matches // |parsed_manifest|. The |strict_manifest_check| controls whether we want @@ -115,9 +135,9 @@ class WebstoreInstaller :public content::NotificationObserver, // Whether we should enable the launcher before installing the app. bool enable_launcher; - // Whether we want a strict manifest equality check, or just want a match - // for effective permissions. - bool strict_manifest_check; + // Manifest check level for checking actual manifest against expected + // manifest. + ManifestCheckLevel manifest_check_level; // Used to show the install dialog. ExtensionInstallPrompt::ShowDialogCallback show_dialog_callback; @@ -125,6 +145,12 @@ class WebstoreInstaller :public content::NotificationObserver, // The icon to use to display the extension while it is installing. gfx::ImageSkia installing_icon; + // A dummy extension created from |manifest|; + scoped_refptr<Extension> dummy_extension; + + // Required minimum version. + scoped_ptr<Version> minimum_version; + private: Approval(); }; @@ -173,7 +199,7 @@ class WebstoreInstaller :public content::NotificationObserver, // Helper to get install URL. static GURL GetWebstoreInstallURL(const std::string& extension_id, - const std::string& install_source); + InstallSource source); // DownloadManager::DownloadUrl callback. void OnDownloadStarted(content::DownloadItem* item, net::Error error); @@ -182,6 +208,13 @@ class WebstoreInstaller :public content::NotificationObserver, virtual void OnDownloadUpdated(content::DownloadItem* download) OVERRIDE; virtual void OnDownloadDestroyed(content::DownloadItem* download) OVERRIDE; + // Downloads next pending module in |pending_modules_|. + void DownloadNextPendingModule(); + + // Downloads and installs a single Crx with the given |extension_id|. + // This function is used for both the extension Crx and dependences. + void DownloadCrx(const std::string& extension_id, InstallSource source); + // Starts downloading the extension to |file_path|. void StartDownload(const base::FilePath& file_path); @@ -199,11 +232,19 @@ class WebstoreInstaller :public content::NotificationObserver, Delegate* delegate_; content::NavigationController* controller_; std::string id_; + InstallSource install_source_; // The DownloadItem is owned by the DownloadManager and is valid from when // OnDownloadStarted is called (with no error) until OnDownloadDestroyed(). content::DownloadItem* download_item_; scoped_ptr<Approval> approval_; GURL download_url_; + + // Pending modules. + std::list<SharedModuleInfo::ImportInfo> pending_modules_; + // Total extension modules we need download and install (the main module and + // depedences). + int total_modules_; + bool download_started_; }; } // namespace extensions diff --git a/chrome/browser/extensions/webstore_installer_unittest.cc b/chrome/browser/extensions/webstore_installer_unittest.cc index e46dad1ec3..734013afcd 100644 --- a/chrome/browser/extensions/webstore_installer_unittest.cc +++ b/chrome/browser/extensions/webstore_installer_unittest.cc @@ -24,7 +24,8 @@ bool Contains(const std::string& source, const std::string& target) { TEST(WebstoreInstallerTest, PlatformParams) { std::string id = extensions::id_util::GenerateId("some random string"); std::string source = "inline"; - GURL url = WebstoreInstaller::GetWebstoreInstallURL(id, source); + GURL url = WebstoreInstaller::GetWebstoreInstallURL(id, + WebstoreInstaller::INSTALL_SOURCE_INLINE); std::string query = url.query(); EXPECT_TRUE(Contains(query,StringPrintf("os=%s", OmahaQueryParams::getOS()))); EXPECT_TRUE(Contains(query,StringPrintf("arch=%s", diff --git a/chrome/browser/extensions/webstore_startup_installer_browsertest.cc b/chrome/browser/extensions/webstore_startup_installer_browsertest.cc index 24de760250..0451bbc9d9 100644 --- a/chrome/browser/extensions/webstore_startup_installer_browsertest.cc +++ b/chrome/browser/extensions/webstore_startup_installer_browsertest.cc @@ -171,8 +171,18 @@ IN_PROC_BROWSER_TEST_F(WebstoreStartupInstallerTest, InstallFromHostedApp) { EXPECT_TRUE(extension_service->extensions()->Contains(kTestExtensionId)); } -IN_PROC_BROWSER_TEST_F(WebstoreStartupInstallerTest, - InstallProhibitedForManagedUsers) { +class WebstoreStartupInstallerManagedUsersTest + : public WebstoreStartupInstallerTest { + public: + // InProcessBrowserTest overrides: + virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { + WebstoreStartupInstallerTest::SetUpCommandLine(command_line); + command_line->AppendSwitch(switches::kNewProfileIsSupervised); + } +}; + +IN_PROC_BROWSER_TEST_F(WebstoreStartupInstallerManagedUsersTest, + InstallProhibited) { #if defined(OS_WIN) && defined(USE_ASH) // Disable this test in Metro+Ash for now (http://crbug.com/262796). if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests)) @@ -182,11 +192,6 @@ IN_PROC_BROWSER_TEST_F(WebstoreStartupInstallerTest, CommandLine::ForCurrentProcess()->AppendSwitchASCII( switches::kAppsGalleryInstallAutoConfirmForTests, "accept"); - // Make the profile managed such that no extension installs are allowed. - ManagedUserService* service = - ManagedUserServiceFactory::GetForProfile(browser()->profile()); - service->InitForTesting(); - ui_test_utils::NavigateToURL( browser(), GenerateTestServerUrl(kAppDomain, "install_prohibited.html")); diff --git a/chrome/browser/extensions/window_open_apitest.cc b/chrome/browser/extensions/window_open_apitest.cc index 1a2c61979e..5cee30edcc 100644 --- a/chrome/browser/extensions/window_open_apitest.cc +++ b/chrome/browser/extensions/window_open_apitest.cc @@ -442,7 +442,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, WindowOpenExtension) { test_data_dir_.AppendASCII("uitest").AppendASCII("window_open"))); GURL start_url(std::string("chrome-extension://") + - last_loaded_extension_id_ + "/test.html"); + last_loaded_extension_id() + "/test.html"); ui_test_utils::NavigateToURL(browser(), start_url); WebContents* newtab = NULL; ASSERT_NO_FATAL_FAILURE( @@ -462,7 +462,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, WindowOpenInvalidExtension) { test_data_dir_.AppendASCII("uitest").AppendASCII("window_open"))); GURL start_url(std::string("chrome-extension://") + - last_loaded_extension_id_ + "/test.html"); + last_loaded_extension_id() + "/test.html"); ui_test_utils::NavigateToURL(browser(), start_url); ASSERT_NO_FATAL_FAILURE( OpenWindow(browser()->tab_strip_model()->GetActiveWebContents(), @@ -484,7 +484,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, WindowOpenNoPrivileges) { WebContents* newtab = NULL; ASSERT_NO_FATAL_FAILURE( OpenWindow(browser()->tab_strip_model()->GetActiveWebContents(), - GURL(std::string("chrome-extension://") + last_loaded_extension_id_ + + GURL(std::string("chrome-extension://") + last_loaded_extension_id() + "/newtab.html"), false, &newtab)); // Extension API should succeed. diff --git a/chrome/browser/favicon/favicon_service.cc b/chrome/browser/favicon/favicon_service.cc index fd0f9eb557..9f0ff332cd 100644 --- a/chrome/browser/favicon/favicon_service.cc +++ b/chrome/browser/favicon/favicon_service.cc @@ -45,6 +45,22 @@ CancelableTaskTracker::TaskId RunWithEmptyResultAsync( Bind(callback, std::vector<chrome::FaviconBitmapResult>())); } +// Return the TaskId to retreive the favicon from chrome specific URL. +CancelableTaskTracker::TaskId GetFaviconForChromeURL( + Profile* profile, + const GURL& page_url, + const std::vector<ui::ScaleFactor>& desired_scale_factors, + const FaviconService::FaviconResultsCallback& callback, + CancelableTaskTracker* tracker) { + CancelableTaskTracker::IsCanceledCallback is_canceled_cb; + CancelableTaskTracker::TaskId id = tracker->NewTrackedTaskId(&is_canceled_cb); + FaviconService::FaviconResultsCallback cancelable_cb = + Bind(&CancelOrRunFaviconResultsCallback, is_canceled_cb, callback); + ChromeWebUIControllerFactory::GetInstance()->GetFaviconForURL(profile, + page_url, desired_scale_factors, cancelable_cb); + return id; +} + } // namespace FaviconService::FaviconService(HistoryService* history_service) @@ -169,6 +185,29 @@ CancelableTaskTracker::TaskId FaviconService::GetRawFaviconForURL( tracker); } +CancelableTaskTracker::TaskId FaviconService::GetLargestRawFaviconForURL( + Profile* profile, + const GURL& page_url, + const std::vector<int>& icon_types, + int minimum_size_in_pixels, + const FaviconRawCallback& callback, + CancelableTaskTracker* tracker) { + FaviconResultsCallback favicon_results_callback = + Bind(&FaviconService::RunFaviconRawCallbackWithBitmapResults, + base::Unretained(this), callback, 0, ui::ScaleFactor()); + if (page_url.SchemeIs(chrome::kChromeUIScheme) || + page_url.SchemeIs(extensions::kExtensionScheme)) { + std::vector<ui::ScaleFactor> scale_factor; + scale_factor.push_back(ui::SCALE_FACTOR_100P); + return GetFaviconForChromeURL(profile, page_url, scale_factor, + favicon_results_callback, tracker); + } else if (history_service_) { + return history_service_->GetLargestFaviconForURL(page_url, icon_types, + minimum_size_in_pixels, callback, tracker); + } + return RunWithEmptyResultAsync(favicon_results_callback, tracker); +} + CancelableTaskTracker::TaskId FaviconService::GetFaviconForURL( const FaviconForURLParams& params, const FaviconResultsCallback& callback, @@ -284,15 +323,8 @@ CancelableTaskTracker::TaskId FaviconService::GetFaviconForURLImpl( CancelableTaskTracker* tracker) { if (params.page_url.SchemeIs(chrome::kChromeUIScheme) || params.page_url.SchemeIs(extensions::kExtensionScheme)) { - CancelableTaskTracker::IsCanceledCallback is_canceled_cb; - CancelableTaskTracker::TaskId id = - tracker->NewTrackedTaskId(&is_canceled_cb); - - FaviconResultsCallback cancelable_cb = - Bind(&CancelOrRunFaviconResultsCallback, is_canceled_cb, callback); - ChromeWebUIControllerFactory::GetInstance()->GetFaviconForURL( - params.profile, params.page_url, desired_scale_factors, cancelable_cb); - return id; + return GetFaviconForChromeURL(params.profile, params.page_url, + desired_scale_factors, callback, tracker); } else if (history_service_) { return history_service_->GetFaviconsForURL(params.page_url, params.icon_types, @@ -300,9 +332,8 @@ CancelableTaskTracker::TaskId FaviconService::GetFaviconForURLImpl( desired_scale_factors, callback, tracker); - } else { - return RunWithEmptyResultAsync(callback, tracker); } + return RunWithEmptyResultAsync(callback, tracker); } void FaviconService::RunFaviconImageCallbackWithBitmapResults( diff --git a/chrome/browser/favicon/favicon_service.h b/chrome/browser/favicon/favicon_service.h index 692a6ebe74..ed1bc3d249 100644 --- a/chrome/browser/favicon/favicon_service.h +++ b/chrome/browser/favicon/favicon_service.h @@ -67,11 +67,9 @@ class FaviconService : public CancelableRequestProvider, typedef base::Callback<void(const chrome::FaviconImageResult&)> FaviconImageCallback; - // Callback for GetRawFavicon() and GetRawFaviconForURL(). - // FaviconBitmapResult::bitmap_data is the bitmap in the thumbnail database - // for the passed in URL and icon types whose pixel size best matches the - // passed in |desired_size_in_dip| and |desired_scale_factor|. Returns an - // invalid chrome::FaviconBitmapResult if there are no matches. + // Callback for GetRawFavicon(), GetRawFaviconForURL() and + // GetLargestRawFavicon(). + // See function for details on value. typedef base::Callback<void(const chrome::FaviconBitmapResult&)> FaviconRawCallback; @@ -172,6 +170,15 @@ class FaviconService : public CancelableRequestProvider, const FaviconRawCallback& callback, CancelableTaskTracker* tracker); + // See HistoryService::GetLargestFaviconForURL(). + CancelableTaskTracker::TaskId GetLargestRawFaviconForURL( + Profile* profile, + const GURL& page_url, + const std::vector<int>& icon_types, + int minimum_size_in_pixels, + const FaviconRawCallback& callback, + CancelableTaskTracker* tracker); + CancelableTaskTracker::TaskId GetFaviconForURL( const FaviconForURLParams& params, const FaviconResultsCallback& callback, diff --git a/chrome/browser/geolocation/geolocation_browsertest.cc b/chrome/browser/geolocation/geolocation_browsertest.cc index a1bec46ca9..d26eabf0a3 100644 --- a/chrome/browser/geolocation/geolocation_browsertest.cc +++ b/chrome/browser/geolocation/geolocation_browsertest.cc @@ -477,7 +477,8 @@ IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, CheckStringValueFromJavascript("1", "geoGetLastError()"); } -IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, NoInfobarForSecondTab) { +// See http://crbug.com/308358 +IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, DISABLED_NoInfobarForSecondTab) { ASSERT_TRUE(Initialize(INITIALIZATION_NONE)); AddGeolocationWatch(true); SetInfoBarResponse(current_url(), true); diff --git a/chrome/browser/geolocation/geolocation_infobar_delegate.cc b/chrome/browser/geolocation/geolocation_infobar_delegate.cc index 65e645ebf9..fba08a89ea 100644 --- a/chrome/browser/geolocation/geolocation_infobar_delegate.cc +++ b/chrome/browser/geolocation/geolocation_infobar_delegate.cc @@ -4,6 +4,7 @@ #include "chrome/browser/geolocation/geolocation_infobar_delegate.h" +#include "base/metrics/histogram.h" #include "chrome/browser/content_settings/permission_queue_controller.h" #include "chrome/browser/google/google_util.h" #include "chrome/browser/infobars/infobar_service.h" @@ -23,6 +24,41 @@ typedef GeolocationInfoBarDelegateAndroid DelegateType; typedef GeolocationInfoBarDelegate DelegateType; #endif +namespace { + +enum GeolocationInfoBarDelegateEvent { + // NOTE: Do not renumber these as that would confuse interpretation of + // previously logged data. When making changes, also update the enum list + // in tools/metrics/histograms/histograms.xml to keep it in sync. + + // The bar was created. + GEOLOCATION_INFO_BAR_DELEGATE_EVENT_CREATE = 0, + + // User allowed use of geolocation. + GEOLOCATION_INFO_BAR_DELEGATE_EVENT_ALLOW = 1, + + // User denied use of geolocation. + GEOLOCATION_INFO_BAR_DELEGATE_EVENT_DENY = 2, + + // User dismissed the bar. + GEOLOCATION_INFO_BAR_DELEGATE_EVENT_DISMISS = 3, + + // User clicked on link. + GEOLOCATION_INFO_BAR_DELEGATE_EVENT_LINK_CLICK = 4, + + // User ignored the bar. + GEOLOCATION_INFO_BAR_DELEGATE_EVENT_IGNORED = 5, + + // NOTE: Add entries only immediately above this line. + GEOLOCATION_INFO_BAR_DELEGATE_EVENT_COUNT = 6 +}; + +void RecordUmaEvent(GeolocationInfoBarDelegateEvent event) { + UMA_HISTOGRAM_ENUMERATION("Geolocation.InfoBarDelegate.Event", + event, GEOLOCATION_INFO_BAR_DELEGATE_EVENT_COUNT); +} + +} // namespace // static InfoBarDelegate* GeolocationInfoBarDelegate::Create( @@ -31,6 +67,7 @@ InfoBarDelegate* GeolocationInfoBarDelegate::Create( const PermissionRequestID& id, const GURL& requesting_frame, const std::string& display_languages) { + RecordUmaEvent(GEOLOCATION_INFO_BAR_DELEGATE_EVENT_CREATE); const content::NavigationEntry* committed_entry = infobar_service->web_contents()->GetController().GetLastCommittedEntry(); return infobar_service->AddInfoBar(scoped_ptr<InfoBarDelegate>( @@ -51,13 +88,18 @@ GeolocationInfoBarDelegate::GeolocationInfoBarDelegate( id_(id), requesting_frame_(requesting_frame.GetOrigin()), contents_unique_id_(contents_unique_id), - display_languages_(display_languages) { + display_languages_(display_languages), + user_has_interacted_(false) { } GeolocationInfoBarDelegate::~GeolocationInfoBarDelegate() { + if (!user_has_interacted_) + RecordUmaEvent(GEOLOCATION_INFO_BAR_DELEGATE_EVENT_IGNORED); } bool GeolocationInfoBarDelegate::Accept() { + RecordUmaEvent(GEOLOCATION_INFO_BAR_DELEGATE_EVENT_ALLOW); + set_user_has_interacted(); SetPermission(true, true); return true; } @@ -72,6 +114,8 @@ void GeolocationInfoBarDelegate::SetPermission(bool update_content_setting, } void GeolocationInfoBarDelegate::InfoBarDismissed() { + RecordUmaEvent(GEOLOCATION_INFO_BAR_DELEGATE_EVENT_DISMISS); + set_user_has_interacted(); SetPermission(false, false); } @@ -106,6 +150,8 @@ string16 GeolocationInfoBarDelegate::GetButtonLabel( } bool GeolocationInfoBarDelegate::Cancel() { + RecordUmaEvent(GEOLOCATION_INFO_BAR_DELEGATE_EVENT_DENY); + set_user_has_interacted(); SetPermission(true, false); return true; } @@ -116,6 +162,7 @@ string16 GeolocationInfoBarDelegate::GetLinkText() const { bool GeolocationInfoBarDelegate::LinkClicked( WindowOpenDisposition disposition) { + RecordUmaEvent(GEOLOCATION_INFO_BAR_DELEGATE_EVENT_LINK_CLICK); const char kGeolocationLearnMoreUrl[] = #if defined(OS_CHROMEOS) "https://www.google.com/support/chromeos/bin/answer.py?answer=142065"; diff --git a/chrome/browser/geolocation/geolocation_infobar_delegate.h b/chrome/browser/geolocation/geolocation_infobar_delegate.h index 6dc4b45e62..7444a67890 100644 --- a/chrome/browser/geolocation/geolocation_infobar_delegate.h +++ b/chrome/browser/geolocation/geolocation_infobar_delegate.h @@ -42,6 +42,13 @@ class GeolocationInfoBarDelegate : public ConfirmInfoBarDelegate { // Call back to the controller, to inform of the user's decision. void SetPermission(bool update_content_setting, bool allowed); + // Marks a flag internally to indicate that the user has interacted with the + // bar. This makes it possible to log from the destructor when the bar has not + // been used, i.e. it has been ignored by the user. + void set_user_has_interacted() { + user_has_interacted_ = true; + } + private: // ConfirmInfoBarDelegate: virtual void InfoBarDismissed() OVERRIDE; @@ -61,6 +68,9 @@ class GeolocationInfoBarDelegate : public ConfirmInfoBarDelegate { int contents_unique_id_; std::string display_languages_; + // Whether the user has interacted with the geolocation infobar. + bool user_has_interacted_; + DISALLOW_COPY_AND_ASSIGN(GeolocationInfoBarDelegate); }; diff --git a/chrome/browser/geolocation/geolocation_infobar_delegate_android.cc b/chrome/browser/geolocation/geolocation_infobar_delegate_android.cc index 52c2b5b1be..4193181ccb 100644 --- a/chrome/browser/geolocation/geolocation_infobar_delegate_android.cc +++ b/chrome/browser/geolocation/geolocation_infobar_delegate_android.cc @@ -4,6 +4,7 @@ #include "chrome/browser/geolocation/geolocation_infobar_delegate_android.h" +#include "base/metrics/histogram.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/android/google_location_settings_helper.h" #include "grit/generated_resources.h" @@ -11,6 +12,28 @@ #include "grit/theme_resources.h" #include "ui/base/l10n/l10n_util.h" +namespace { +enum GeolocationInfoBarDelegateAndroidEvent { + // NOTE: Do not renumber these as that would confuse interpretation of + // previously logged data. When making changes, also update the enum list + // in tools/metrics/histograms/histograms.xml to keep it in sync. + + // User allowed the page to use geolocation. + GEOLOCATION_INFO_BAR_DELEGATE_ANDROID_EVENT_ALLOW = 0, + + // User opened geolocation settings. + GEOLOCATION_INFO_BAR_DELEGATE_ANDROID_EVENT_SETTINGS = 1, + + // NOTE: Add entries only immediately above this line. + GEOLOCATION_INFO_BAR_DELEGATE_ANDROID_EVENT_COUNT = 2 +}; + +void RecordUmaEvent(GeolocationInfoBarDelegateAndroidEvent event) { + UMA_HISTOGRAM_ENUMERATION("Geolocation.InfoBarDelegateAndroid.Event", + event, GEOLOCATION_INFO_BAR_DELEGATE_ANDROID_EVENT_COUNT); +} +} // namespace + GeolocationInfoBarDelegateAndroid::GeolocationInfoBarDelegateAndroid( InfoBarService* infobar_service, PermissionQueueController* controller, @@ -29,13 +52,18 @@ GeolocationInfoBarDelegateAndroid::~GeolocationInfoBarDelegateAndroid() { } bool GeolocationInfoBarDelegateAndroid::Accept() { + set_user_has_interacted(); + // Accept button text could be either 'Allow' or 'Google Location Settings'. // If 'Allow' we follow the regular flow. - if (google_location_settings_helper_->IsGoogleAppsLocationSettingEnabled()) + if (google_location_settings_helper_->IsGoogleAppsLocationSettingEnabled()) { + RecordUmaEvent(GEOLOCATION_INFO_BAR_DELEGATE_ANDROID_EVENT_ALLOW); return GeolocationInfoBarDelegate::Accept(); + } // If 'Google Location Settings', we need to open the system Google Location // Settings activity. + RecordUmaEvent(GEOLOCATION_INFO_BAR_DELEGATE_ANDROID_EVENT_SETTINGS); google_location_settings_helper_->ShowGoogleLocationSettings(); SetPermission(false, false); return true; diff --git a/chrome/browser/google_apis/auth_service.cc b/chrome/browser/google_apis/auth_service.cc index bd9a6b646f..e9dca03192 100644 --- a/chrome/browser/google_apis/auth_service.cc +++ b/chrome/browser/google_apis/auth_service.cc @@ -208,7 +208,6 @@ void AuthService::OnAuthCompleted(const AuthStatusCallback& callback, ClearRefreshToken(); } - // TODO(zelidrag): Add retry, back-off logic when things go wrong here. callback.Run(error, access_token); } diff --git a/chrome/browser/google_apis/drive_api_requests_unittest.cc b/chrome/browser/google_apis/drive_api_requests_unittest.cc index 30c0a2af76..99878d3476 100644 --- a/chrome/browser/google_apis/drive_api_requests_unittest.cc +++ b/chrome/browser/google_apis/drive_api_requests_unittest.cc @@ -41,6 +41,14 @@ const char kTestUploadExistingFilePath[] = "/upload/existingfile/path"; const char kTestUploadNewFilePath[] = "/upload/newfile/path"; const char kTestDownloadPathPrefix[] = "/download/"; +// Used as a GetContentCallback. +void AppendContent(std::string* out, + GDataErrorCode error, + scoped_ptr<std::string> content) { + EXPECT_EQ(HTTP_SUCCESS, error); + out->append(*content); +} + } // namespace class DriveApiRequestsTest : public testing::Test { @@ -1555,4 +1563,39 @@ TEST_F(DriveApiRequestsTest, DownloadFileRequest) { EXPECT_EQ(expected_contents, contents); } +TEST_F(DriveApiRequestsTest, DownloadFileRequest_GetContentCallback) { + const base::FilePath kDownloadedFilePath = + temp_dir_.path().AppendASCII("cache_file"); + const std::string kTestId("dummyId"); + + GDataErrorCode result_code = GDATA_OTHER_ERROR; + base::FilePath temp_file; + std::string contents; + { + base::RunLoop run_loop; + drive::DownloadFileRequest* request = new drive::DownloadFileRequest( + request_sender_.get(), + *url_generator_, + kTestId, + kDownloadedFilePath, + test_util::CreateQuitCallback( + &run_loop, + test_util::CreateCopyResultCallback(&result_code, &temp_file)), + base::Bind(&AppendContent, &contents), + ProgressCallback()); + request_sender_->StartRequestWithRetry(request); + run_loop.Run(); + } + + base::DeleteFile(temp_file, false); + + EXPECT_EQ(HTTP_SUCCESS, result_code); + EXPECT_EQ(net::test_server::METHOD_GET, http_request_.method); + EXPECT_EQ(kTestDownloadPathPrefix + kTestId, http_request_.relative_url); + EXPECT_EQ(kDownloadedFilePath, temp_file); + + const std::string expected_contents = kTestId + kTestId + kTestId; + EXPECT_EQ(expected_contents, contents); +} + } // namespace google_apis diff --git a/chrome/browser/google_apis/drive_entry_kinds.h b/chrome/browser/google_apis/drive_entry_kinds.h index 06f1e69314..c64c4e4c4a 100644 --- a/chrome/browser/google_apis/drive_entry_kinds.h +++ b/chrome/browser/google_apis/drive_entry_kinds.h @@ -22,6 +22,7 @@ enum DriveEntryKind { ENTRY_KIND_PRESENTATION, ENTRY_KIND_DRAWING, ENTRY_KIND_TABLE, + ENTRY_KIND_FORM, // Hosted external application document. ENTRY_KIND_EXTERNAL_APP, // Folders; collections. diff --git a/chrome/browser/google_apis/gdata_wapi_parser.cc b/chrome/browser/google_apis/gdata_wapi_parser.cc index af90dd3c5f..d1d7b2fded 100644 --- a/chrome/browser/google_apis/gdata_wapi_parser.cc +++ b/chrome/browser/google_apis/gdata_wapi_parser.cc @@ -106,6 +106,7 @@ const EntryKindMap kEntryKindMap[] = { { ENTRY_KIND_PRESENTATION, "presentation", ".gslides" }, { ENTRY_KIND_DRAWING, "drawing", ".gdraw"}, { ENTRY_KIND_TABLE, "table", ".gtable"}, + { ENTRY_KIND_FORM, "form", ".gform"}, { ENTRY_KIND_EXTERNAL_APP, "externalapp", ".glink"}, { ENTRY_KIND_SITE, "site", NULL}, { ENTRY_KIND_FOLDER, "folder", NULL}, @@ -586,6 +587,7 @@ int ResourceEntry::ClassifyEntryKind(DriveEntryKind kind) { case ENTRY_KIND_PRESENTATION: case ENTRY_KIND_DRAWING: case ENTRY_KIND_TABLE: + case ENTRY_KIND_FORM: classes = KIND_OF_GOOGLE_DOCUMENT | KIND_OF_HOSTED_DOCUMENT; break; diff --git a/chrome/browser/google_apis/gdata_wapi_requests.cc b/chrome/browser/google_apis/gdata_wapi_requests.cc index 1f99108084..0857a42792 100644 --- a/chrome/browser/google_apis/gdata_wapi_requests.cc +++ b/chrome/browser/google_apis/gdata_wapi_requests.cc @@ -5,17 +5,12 @@ #include "chrome/browser/google_apis/gdata_wapi_requests.h" #include "base/location.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/stringprintf.h" #include "base/task_runner_util.h" #include "base/values.h" #include "chrome/browser/google_apis/gdata_wapi_parser.h" #include "chrome/browser/google_apis/gdata_wapi_url_generator.h" #include "chrome/browser/google_apis/request_sender.h" #include "chrome/browser/google_apis/request_util.h" -#include "chrome/browser/google_apis/time_util.h" -#include "net/base/escape.h" -#include "net/base/url_util.h" #include "third_party/libxml/chromium/libxml_utils.h" using net::URLFetcher; @@ -565,10 +560,7 @@ InitiateUploadNewFileRequest::InitiateUploadNewFileRequest( int64 content_length, const std::string& parent_resource_id, const std::string& title) - : InitiateUploadRequestBase(sender, - callback, - content_type, - content_length), + : InitiateUploadRequestBase(sender, callback, content_type, content_length), url_generator_(url_generator), parent_resource_id_(parent_resource_id), title_(title) { @@ -614,10 +606,7 @@ InitiateUploadExistingFileRequest::InitiateUploadExistingFileRequest( int64 content_length, const std::string& resource_id, const std::string& etag) - : InitiateUploadRequestBase(sender, - callback, - content_type, - content_length), + : InitiateUploadRequestBase(sender, callback, content_type, content_length), url_generator_(url_generator), resource_id_(resource_id), etag_(etag) { @@ -710,7 +699,6 @@ void GetUploadStatusRequest::OnRangeRequestComplete( callback_.Run(response, ParseResourceEntry(value.Pass())); } - //========================== DownloadFileRequest ========================== DownloadFileRequest::DownloadFileRequest( diff --git a/chrome/browser/google_apis/gdata_wapi_requests_unittest.cc b/chrome/browser/google_apis/gdata_wapi_requests_unittest.cc index 08ca8e7b3c..399925b45e 100644 --- a/chrome/browser/google_apis/gdata_wapi_requests_unittest.cc +++ b/chrome/browser/google_apis/gdata_wapi_requests_unittest.cc @@ -494,9 +494,11 @@ TEST_F(GDataWapiRequestsTest, GetResourceEntryRequest_ValidResourceId) { EXPECT_EQ("/feeds/default/private/full/file%3A2_file_resource_id" "?v=3&alt=json&showroot=true", http_request_.relative_url); - EXPECT_TRUE(test_util::VerifyJsonData( - test_util::GetTestFilePath("gdata/file_entry.json"), - result_data.get())); + scoped_ptr<base::Value> expected_json = + test_util::LoadJSONFile("gdata/file_entry.json"); + ASSERT_TRUE(expected_json); + EXPECT_TRUE(result_data); + EXPECT_TRUE(base::Value::Equals(expected_json.get(), result_data.get())); } TEST_F(GDataWapiRequestsTest, GetResourceEntryRequest_InvalidResourceId) { diff --git a/chrome/browser/google_apis/request_sender.h b/chrome/browser/google_apis/request_sender.h index 550a696009..f37754c673 100644 --- a/chrome/browser/google_apis/request_sender.h +++ b/chrome/browser/google_apis/request_sender.h @@ -7,7 +7,6 @@ #include <set> #include <string> -#include <vector> #include "base/basictypes.h" #include "base/callback_forward.h" diff --git a/chrome/browser/google_apis/test_util.cc b/chrome/browser/google_apis/test_util.cc index 934042e4f7..e1d18fcdd9 100644 --- a/chrome/browser/google_apis/test_util.cc +++ b/chrome/browser/google_apis/test_util.cc @@ -122,30 +122,6 @@ scoped_ptr<net::test_server::HttpResponse> HandleDownloadFileRequest( GetTestFilePath(remaining_path)).PassAs<net::test_server::HttpResponse>(); } -bool VerifyJsonData(const base::FilePath& expected_json_file_path, - const base::Value* json_data) { - if (!json_data) { - LOG(ERROR) << "json_data is NULL"; - return false; - } - - std::string expected_content; - if (!base::ReadFileToString(expected_json_file_path, &expected_content)) { - LOG(ERROR) << "Failed to read file: " << expected_json_file_path.value(); - return false; - } - - scoped_ptr<base::Value> expected_json_data( - base::JSONReader::Read(expected_content)); - if (!base::Value::Equals(expected_json_data.get(), json_data)) { - LOG(ERROR) - << "The value of json_data is different from the file's content."; - return false; - } - - return true; -} - bool ParseContentRangeHeader(const std::string& value, int64* start_position, int64* end_position, diff --git a/chrome/browser/google_apis/test_util.h b/chrome/browser/google_apis/test_util.h index 42facc4df2..2976937c97 100644 --- a/chrome/browser/google_apis/test_util.h +++ b/chrome/browser/google_apis/test_util.h @@ -93,12 +93,6 @@ scoped_ptr<net::test_server::HttpResponse> HandleDownloadFileRequest( net::test_server::HttpRequest* out_request, const net::test_server::HttpRequest& request); -// Returns true if |json_data| is not NULL and equals to the content in -// |expected_json_file_path|. The failure reason will be logged into LOG(ERROR) -// if necessary. -bool VerifyJsonData(const base::FilePath& expected_json_file_path, - const base::Value* json_data); - // Parses a value of Content-Range header, which looks like // "bytes <start_position>-<end_position>/<length>". // Returns true on success. @@ -129,8 +123,6 @@ bool ParseContentRangeHeader(const std::string& value, // : // // Note: The max arity of the supported function is 4 based on the usage. -// TODO(hidehiko): Use replace CopyResultFromXxxCallback method defined above -// by this one. (crbug.com/180569). namespace internal { // Following helper templates are to support Chrome's move semantics. // Their goal is defining helper methods which are similar to: diff --git a/chrome/browser/guestview/adview/adview_guest.cc b/chrome/browser/guestview/adview/adview_guest.cc index e62bb4e2d0..0c887d4eaf 100644 --- a/chrome/browser/guestview/adview/adview_guest.cc +++ b/chrome/browser/guestview/adview/adview_guest.cc @@ -43,6 +43,7 @@ AdViewGuest::~AdViewGuest() { void AdViewGuest::DidCommitProvisionalLoadForFrame( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& url, content::PageTransition transition_type, @@ -55,6 +56,7 @@ void AdViewGuest::DidCommitProvisionalLoadForFrame( void AdViewGuest::DidFailProvisionalLoad( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& validated_url, int error_code, diff --git a/chrome/browser/guestview/adview/adview_guest.h b/chrome/browser/guestview/adview/adview_guest.h index 9d4385cb13..932736342c 100644 --- a/chrome/browser/guestview/adview/adview_guest.h +++ b/chrome/browser/guestview/adview/adview_guest.h @@ -33,12 +33,14 @@ class AdViewGuest : public GuestView, virtual void DidCommitProvisionalLoadForFrame( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& url, content::PageTransition transition_type, content::RenderViewHost* render_view_host) OVERRIDE; virtual void DidFailProvisionalLoad( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& validated_url, int error_code, diff --git a/chrome/browser/guestview/webview/webview_guest.cc b/chrome/browser/guestview/webview/webview_guest.cc index 3230f6af73..aa23b9e1be 100644 --- a/chrome/browser/guestview/webview/webview_guest.cc +++ b/chrome/browser/guestview/webview/webview_guest.cc @@ -450,6 +450,7 @@ WebViewGuest::~WebViewGuest() { void WebViewGuest::DidCommitProvisionalLoadForFrame( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& url, content::PageTransition transition_type, @@ -468,6 +469,7 @@ void WebViewGuest::DidCommitProvisionalLoadForFrame( void WebViewGuest::DidFailProvisionalLoad( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& validated_url, int error_code, diff --git a/chrome/browser/guestview/webview/webview_guest.h b/chrome/browser/guestview/webview/webview_guest.h index 784e58f9b5..48e0ea4f75 100644 --- a/chrome/browser/guestview/webview/webview_guest.h +++ b/chrome/browser/guestview/webview/webview_guest.h @@ -124,12 +124,14 @@ class WebViewGuest : public GuestView, // WebContentsObserver implementation. virtual void DidCommitProvisionalLoadForFrame( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& url, content::PageTransition transition_type, content::RenderViewHost* render_view_host) OVERRIDE; virtual void DidFailProvisionalLoad( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& validated_url, int error_code, diff --git a/chrome/browser/history/history_backend.cc b/chrome/browser/history/history_backend.cc index ba569f7a9c..e899fb5378 100644 --- a/chrome/browser/history/history_backend.cc +++ b/chrome/browser/history/history_backend.cc @@ -1696,6 +1696,100 @@ void HistoryBackend::GetFavicons( bitmap_results); } +void HistoryBackend::GetLargestFaviconForURL( + const GURL& page_url, + const std::vector<int>& icon_types, + int minimum_size_in_pixels, + chrome::FaviconBitmapResult* favicon_bitmap_result) { + DCHECK(favicon_bitmap_result); + + if (!db_ || !thumbnail_db_) + return; + + TimeTicks beginning_time = TimeTicks::Now(); + + std::vector<IconMapping> icon_mappings; + if (!thumbnail_db_->GetIconMappingsForPageURL(page_url, &icon_mappings) || + icon_mappings.empty()) + return; + + int required_icon_types = 0; + for (std::vector<int>::const_iterator i = icon_types.begin(); + i != icon_types.end(); ++i) { + required_icon_types |= *i; + } + + // Find the largest bitmap for each IconType placing in + // |largest_favicon_bitmaps|. + std::map<chrome::IconType, FaviconBitmap> largest_favicon_bitmaps; + for (std::vector<IconMapping>::const_iterator i = icon_mappings.begin(); + i != icon_mappings.end(); ++i) { + if (!(i->icon_type & required_icon_types)) + continue; + std::vector<FaviconBitmapIDSize> bitmap_id_sizes; + thumbnail_db_->GetFaviconBitmapIDSizes(i->icon_id, &bitmap_id_sizes); + FaviconBitmap& largest = largest_favicon_bitmaps[i->icon_type]; + for (std::vector<FaviconBitmapIDSize>::const_iterator j = + bitmap_id_sizes.begin(); j != bitmap_id_sizes.end(); ++j) { + if (largest.bitmap_id == 0 || + (largest.pixel_size.width() < j->pixel_size.width() && + largest.pixel_size.height() < j->pixel_size.height())) { + largest.icon_id = i->icon_id; + largest.bitmap_id = j->bitmap_id; + largest.pixel_size = j->pixel_size; + } + } + } + if (largest_favicon_bitmaps.empty()) + return; + + // Find an icon which is larger than minimum_size_in_pixels in the order of + // icon_types. + FaviconBitmap largest_icon; + for (std::vector<int>::const_iterator t = icon_types.begin(); + t != icon_types.end(); ++t) { + for (std::map<chrome::IconType, FaviconBitmap>::const_iterator f = + largest_favicon_bitmaps.begin(); f != largest_favicon_bitmaps.end(); + ++f) { + if (f->first & *t && + (largest_icon.bitmap_id == 0 || + (largest_icon.pixel_size.height() < f->second.pixel_size.height() && + largest_icon.pixel_size.width() < f->second.pixel_size.width()))) { + largest_icon = f->second; + } + } + if (largest_icon.pixel_size.width() > minimum_size_in_pixels && + largest_icon.pixel_size.height() > minimum_size_in_pixels) + break; + } + + GURL icon_url; + chrome::IconType icon_type; + if (!thumbnail_db_->GetFaviconHeader(largest_icon.icon_id, &icon_url, + &icon_type)) { + return; + } + + base::Time last_updated; + chrome::FaviconBitmapResult bitmap_result; + bitmap_result.icon_url = icon_url; + bitmap_result.icon_type = icon_type; + if (!thumbnail_db_->GetFaviconBitmap(largest_icon.bitmap_id, + &last_updated, + &bitmap_result.bitmap_data, + &bitmap_result.pixel_size)) { + return; + } + + bitmap_result.expired = (Time::Now() - last_updated) > + TimeDelta::FromDays(kFaviconRefetchDays); + if (bitmap_result.is_valid()) + *favicon_bitmap_result = bitmap_result; + + HISTOGRAM_TIMES("History.GetLargestFaviconForURL", + TimeTicks::Now() - beginning_time); +} + void HistoryBackend::GetFaviconsForURL( const GURL& page_url, int icon_types, diff --git a/chrome/browser/history/history_backend.h b/chrome/browser/history/history_backend.h index 41765c7575..f85f6e0a34 100644 --- a/chrome/browser/history/history_backend.h +++ b/chrome/browser/history/history_backend.h @@ -242,6 +242,12 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>, const std::vector<ui::ScaleFactor>& desired_scale_factors, std::vector<chrome::FaviconBitmapResult>* bitmap_results); + void GetLargestFaviconForURL( + const GURL& page_url, + const std::vector<int>& icon_types, + int minimum_size_in_pixels, + chrome::FaviconBitmapResult* bitmap_result); + void GetFaviconsForURL( const GURL& page_url, int icon_types, diff --git a/chrome/browser/history/history_backend_unittest.cc b/chrome/browser/history/history_backend_unittest.cc index 04af2b41ff..a09b57bc01 100644 --- a/chrome/browser/history/history_backend_unittest.cc +++ b/chrome/browser/history/history_backend_unittest.cc @@ -1922,6 +1922,115 @@ TEST_F(HistoryBackendTest, MergeFaviconShowsUpInGetFaviconsForURLResult) { EXPECT_TRUE(BitmapDataEqual('c', result.bitmap_data)); } +// Tests GetFaviconsForURL with icon_types priority, +TEST_F(HistoryBackendTest, TestGetFaviconsForURLWithIconTypesPriority) { + GURL page_url("http://www.google.com"); + GURL icon_url("http://www.google.com/favicon.ico"); + GURL touch_icon_url("http://wwww.google.com/touch_icon.ico"); + + std::vector<chrome::FaviconBitmapData> favicon_bitmap_data; + std::vector<gfx::Size> favicon_size; + favicon_size.push_back(gfx::Size(16, 16)); + favicon_size.push_back(gfx::Size(32, 32)); + GenerateFaviconBitmapData(icon_url, favicon_size, &favicon_bitmap_data); + ASSERT_EQ(2u, favicon_bitmap_data.size()); + + std::vector<chrome::FaviconBitmapData> touch_icon_bitmap_data; + std::vector<gfx::Size> touch_icon_size; + touch_icon_size.push_back(gfx::Size(64, 64)); + GenerateFaviconBitmapData(icon_url, touch_icon_size, &touch_icon_bitmap_data); + ASSERT_EQ(1u, touch_icon_bitmap_data.size()); + + // Set some preexisting favicons for |page_url|. + backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data); + backend_->SetFavicons(page_url, chrome::TOUCH_ICON, touch_icon_bitmap_data); + + chrome::FaviconBitmapResult result; + std::vector<int> icon_types; + icon_types.push_back(chrome::FAVICON); + icon_types.push_back(chrome::TOUCH_ICON); + + backend_->GetLargestFaviconForURL(page_url, icon_types, 16, &result); + + // Verify the result icon is 32x32 favicon. + EXPECT_EQ(gfx::Size(32, 32), result.pixel_size); + EXPECT_EQ(chrome::FAVICON, result.icon_type); + + // Change Minimal size to 32x32 and verify the 64x64 touch icon returned. + backend_->GetLargestFaviconForURL(page_url, icon_types, 32, &result); + EXPECT_EQ(gfx::Size(64, 64), result.pixel_size); + EXPECT_EQ(chrome::TOUCH_ICON, result.icon_type); +} + +// Test the the first types of icon is returned if its size equal to the +// second types icon. +TEST_F(HistoryBackendTest, TestGetFaviconsForURLReturnFavicon) { + GURL page_url("http://www.google.com"); + GURL icon_url("http://www.google.com/favicon.ico"); + GURL touch_icon_url("http://wwww.google.com/touch_icon.ico"); + + std::vector<chrome::FaviconBitmapData> favicon_bitmap_data; + std::vector<gfx::Size> favicon_size; + favicon_size.push_back(gfx::Size(16, 16)); + favicon_size.push_back(gfx::Size(32, 32)); + GenerateFaviconBitmapData(icon_url, favicon_size, &favicon_bitmap_data); + ASSERT_EQ(2u, favicon_bitmap_data.size()); + + std::vector<chrome::FaviconBitmapData> touch_icon_bitmap_data; + std::vector<gfx::Size> touch_icon_size; + touch_icon_size.push_back(gfx::Size(32, 32)); + GenerateFaviconBitmapData(icon_url, touch_icon_size, &touch_icon_bitmap_data); + ASSERT_EQ(1u, touch_icon_bitmap_data.size()); + + // Set some preexisting favicons for |page_url|. + backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data); + backend_->SetFavicons(page_url, chrome::TOUCH_ICON, touch_icon_bitmap_data); + + chrome::FaviconBitmapResult result; + std::vector<int> icon_types; + icon_types.push_back(chrome::FAVICON); + icon_types.push_back(chrome::TOUCH_ICON); + + backend_->GetLargestFaviconForURL(page_url, icon_types, 16, &result); + + // Verify the result icon is 32x32 favicon. + EXPECT_EQ(gfx::Size(32, 32), result.pixel_size); + EXPECT_EQ(chrome::FAVICON, result.icon_type); + + // Change minimal size to 32x32 and verify the 32x32 favicon returned. + chrome::FaviconBitmapResult result1; + backend_->GetLargestFaviconForURL(page_url, icon_types, 32, &result1); + EXPECT_EQ(gfx::Size(32, 32), result1.pixel_size); + EXPECT_EQ(chrome::FAVICON, result1.icon_type); +} + +// Test the favicon is returned if its size is smaller than minimal size, +// because it is only one available. +TEST_F(HistoryBackendTest, TestGetFaviconsForURLReturnFaviconEvenItSmaller) { + GURL page_url("http://www.google.com"); + GURL icon_url("http://www.google.com/favicon.ico"); + + std::vector<chrome::FaviconBitmapData> favicon_bitmap_data; + std::vector<gfx::Size> favicon_size; + favicon_size.push_back(gfx::Size(16, 16)); + GenerateFaviconBitmapData(icon_url, favicon_size, &favicon_bitmap_data); + ASSERT_EQ(1u, favicon_bitmap_data.size()); + + // Set preexisting favicons for |page_url|. + backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data); + + chrome::FaviconBitmapResult result; + std::vector<int> icon_types; + icon_types.push_back(chrome::FAVICON); + icon_types.push_back(chrome::TOUCH_ICON); + + backend_->GetLargestFaviconForURL(page_url, icon_types, 32, &result); + + // Verify 16x16 icon is returned, even it small than minimal_size. + EXPECT_EQ(gfx::Size(16, 16), result.pixel_size); + EXPECT_EQ(chrome::FAVICON, result.icon_type); +} + // Test UpdateFaviconMapingsAndFetch() when multiple icon types are passed in. TEST_F(HistoryBackendTest, UpdateFaviconMappingsAndFetchMultipleIconTypes) { GURL page_url1("http://www.google.com"); diff --git a/chrome/browser/history/history_service.cc b/chrome/browser/history/history_service.cc index aee38c899a..52088ff33a 100644 --- a/chrome/browser/history/history_service.cc +++ b/chrome/browser/history/history_service.cc @@ -84,6 +84,12 @@ void RunWithFaviconResults( callback.Run(*bitmap_results); } +void RunWithFaviconResult( + const FaviconService::FaviconRawCallback& callback, + chrome::FaviconBitmapResult* bitmap_result) { + callback.Run(*bitmap_result); +} + // Extract history::URLRows into GURLs for VisitedLinkMaster. class URLIteratorFromURLRows : public visitedlink::VisitedLinkMaster::URLIterator { @@ -644,6 +650,28 @@ CancelableTaskTracker::TaskId HistoryService::GetFaviconsForURL( base::Bind(&RunWithFaviconResults, callback, base::Owned(results))); } +CancelableTaskTracker::TaskId HistoryService::GetLargestFaviconForURL( + const GURL& page_url, + const std::vector<int>& icon_types, + int minimum_size_in_pixels, + const FaviconService::FaviconRawCallback& callback, + CancelableTaskTracker* tracker) { + DCHECK(thread_checker_.CalledOnValidThread()); + LoadBackendIfNecessary(); + + chrome::FaviconBitmapResult* result = new chrome::FaviconBitmapResult(); + return tracker->PostTaskAndReply( + thread_->message_loop_proxy().get(), + FROM_HERE, + base::Bind(&HistoryBackend::GetLargestFaviconForURL, + history_backend_.get(), + page_url, + icon_types, + minimum_size_in_pixels, + result), + base::Bind(&RunWithFaviconResult, callback, base::Owned(result))); +} + CancelableTaskTracker::TaskId HistoryService::GetFaviconForID( chrome::FaviconID favicon_id, int desired_size_in_dip, diff --git a/chrome/browser/history/history_service.h b/chrome/browser/history/history_service.h index 6891f38e71..fca534ae22 100644 --- a/chrome/browser/history/history_service.h +++ b/chrome/browser/history/history_service.h @@ -707,6 +707,24 @@ class HistoryService : public CancelableRequestProvider, const FaviconService::FaviconResultsCallback& callback, CancelableTaskTracker* tracker); + // Used by FaviconService to find the first favicon bitmap whose width and + // height are greater than that of |minimum_size_in_pixels|. This searches + // for icons by IconType. Each element of |icon_types| is a bitmask of + // IconTypes indicating the types to search for. + // If the largest icon of |icon_types[0]| is not larger than + // |minimum_size_in_pixel|, the next icon types of + // |icon_types| will be searched and so on. + // If no icon is larger than |minimum_size_in_pixel|, the largest one of all + // icon types in |icon_types| is returned. + // This feature is especially useful when some types of icon is perfered as + // long as its size is larger than a specific value. + CancelableTaskTracker::TaskId GetLargestFaviconForURL( + const GURL& page_url, + const std::vector<int>& icon_types, + int minimum_size_in_pixels, + const FaviconService::FaviconRawCallback& callback, + CancelableTaskTracker* tracker); + // Used by the FaviconService to get the favicon bitmap which most closely // matches |desired_size_in_dip| and |desired_scale_factor| from the favicon // with |favicon_id| from the history backend. If |desired_size_in_dip| is 0, diff --git a/chrome/browser/icon_loader_chromeos.cc b/chrome/browser/icon_loader_chromeos.cc index c55d7c8731..9c55d31e03 100644 --- a/chrome/browser/icon_loader_chromeos.cc +++ b/chrome/browser/icon_loader_chromeos.cc @@ -68,11 +68,13 @@ const IdrBySize kImageIdrs = { IDR_FILE_MANAGER_IMG_FILETYPE_IMAGE, IDR_FILE_MANAGER_IMG_FILETYPE_IMAGE }; +#if defined(USE_PROPRIETARY_CODECS) const IdrBySize kPdfIdrs = { IDR_FILE_MANAGER_IMG_FILETYPE_PDF, IDR_FILE_MANAGER_IMG_FILETYPE_PDF, IDR_FILE_MANAGER_IMG_FILETYPE_PDF }; +#endif const IdrBySize kVideoIdrs = { IDR_FILE_MANAGER_IMG_FILETYPE_VIDEO, IDR_FILE_MANAGER_IMG_FILETYPE_LARGE_VIDEO, diff --git a/chrome/browser/lifetime/application_lifetime.cc b/chrome/browser/lifetime/application_lifetime.cc index 0693c58f08..da12a425f7 100644 --- a/chrome/browser/lifetime/application_lifetime.cc +++ b/chrome/browser/lifetime/application_lifetime.cc @@ -60,7 +60,8 @@ bool AreAllBrowsersCloseable() { return true; // If there are any downloads active, all browsers are not closeable. - if (DownloadService::DownloadCountAllProfiles() > 0) + // However, this does not block for malicious downloads. + if (DownloadService::NonMaliciousDownloadCountAllProfiles() > 0) return false; // Check TabsNeedBeforeUnloadFired(). diff --git a/chrome/browser/lifetime/browser_close_manager.cc b/chrome/browser/lifetime/browser_close_manager.cc index 49d65313be..e26b73f7eb 100644 --- a/chrome/browser/lifetime/browser_close_manager.cc +++ b/chrome/browser/lifetime/browser_close_manager.cc @@ -74,7 +74,7 @@ void BrowserCloseManager::OnBrowserReportCloseable(bool proceed) { } void BrowserCloseManager::CheckForDownloadsInProgress() { - int download_count = DownloadService::DownloadCountAllProfiles(); + int download_count = DownloadService::NonMaliciousDownloadCountAllProfiles(); if (download_count == 0) { CloseBrowsers(); return; @@ -113,7 +113,7 @@ void BrowserCloseManager::OnReportDownloadsCancellable(bool proceed) { ++it) { DownloadService* download_service = DownloadServiceFactory::GetForBrowserContext(*it); - if (download_service->DownloadCount() > 0) { + if (download_service->NonMaliciousDownloadCount() > 0) { Browser* browser = chrome::FindOrCreateTabbedBrowser(*it, chrome::GetActiveDesktop()); DCHECK(browser); diff --git a/chrome/browser/lifetime/browser_close_manager_browsertest.cc b/chrome/browser/lifetime/browser_close_manager_browsertest.cc index ce61b24402..3023e55115 100644 --- a/chrome/browser/lifetime/browser_close_manager_browsertest.cc +++ b/chrome/browser/lifetime/browser_close_manager_browsertest.cc @@ -8,6 +8,9 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_shutdown.h" #include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/download/chrome_download_manager_delegate.h" +#include "chrome/browser/download/download_service.h" +#include "chrome/browser/download/download_service_factory.h" #include "chrome/browser/lifetime/application_lifetime.h" #include "chrome/browser/lifetime/browser_close_manager.h" #include "chrome/browser/net/url_request_mock_util.h" @@ -31,6 +34,7 @@ #include "content/public/browser/web_contents.h" #include "content/public/test/download_test_observer.h" #include "content/public/test/test_navigation_observer.h" +#include "content/test/net/url_request_mock_http_job.h" #include "content/test/net/url_request_slow_download_job.h" #include "net/test/embedded_test_server/embedded_test_server.h" @@ -121,6 +125,7 @@ class TestBrowserCloseManager : public BrowserCloseManager { enum UserChoice { USER_CHOICE_USER_CANCELS_CLOSE, USER_CHOICE_USER_ALLOWS_CLOSE, + NO_USER_CHOICE }; static void AttemptClose(UserChoice user_choice) { @@ -135,7 +140,9 @@ class TestBrowserCloseManager : public BrowserCloseManager { virtual void ConfirmCloseWithPendingDownloads( int download_count, const base::Callback<void(bool)>& callback) OVERRIDE { + EXPECT_NE(NO_USER_CHOICE, user_choice_); switch (user_choice_) { + case NO_USER_CHOICE: case USER_CHOICE_USER_CANCELS_CLOSE: { callback.Run(false); break; @@ -156,6 +163,38 @@ class TestBrowserCloseManager : public BrowserCloseManager { DISALLOW_COPY_AND_ASSIGN(TestBrowserCloseManager); }; +class TestDownloadManagerDelegate : public ChromeDownloadManagerDelegate { + public: + explicit TestDownloadManagerDelegate(Profile* profile) + : ChromeDownloadManagerDelegate(profile) { + SetNextId(content::DownloadItem::kInvalidId + 1); + } + + virtual bool DetermineDownloadTarget( + content::DownloadItem* item, + const content::DownloadTargetCallback& callback) OVERRIDE { + content::DownloadTargetCallback dangerous_callback = + base::Bind(&TestDownloadManagerDelegate::SetDangerous, this, callback); + return ChromeDownloadManagerDelegate::DetermineDownloadTarget( + item, dangerous_callback); + } + + void SetDangerous( + const content::DownloadTargetCallback& callback, + const base::FilePath& target_path, + content::DownloadItem::TargetDisposition disp, + content::DownloadDangerType danger_type, + const base::FilePath& intermediate_path) { + callback.Run(target_path, + disp, + content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL, + intermediate_path); + } + + private: + virtual ~TestDownloadManagerDelegate() {} +}; + } // namespace class BrowserCloseManagerBrowserTest @@ -619,6 +658,49 @@ IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest, EXPECT_TRUE(chrome::BrowserIterator().done()); } +// Test shutdown with a DANGEROUS_URL download undecided. +IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest, + TestWithDangerousUrlDownload) { + ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); + + // Set up the fake delegate that forces the download to be malicious. + scoped_refptr<TestDownloadManagerDelegate> test_delegate( + new TestDownloadManagerDelegate(browser()->profile())); + DownloadServiceFactory::GetForBrowserContext(browser()->profile())-> + SetDownloadManagerDelegateForTesting(test_delegate.get()); + + // Run a dangerous download, but the user doesn't make a decision. + // This .swf normally would be categorized as DANGEROUS_FILE, but + // TestDownloadManagerDelegate turns it into DANGEROUS_URL. + base::FilePath file(FILE_PATH_LITERAL("downloads/dangerous/dangerous.swf")); + GURL download_url(content::URLRequestMockHTTPJob::GetMockUrl(file)); + content::DownloadTestObserverInterrupted observer( + content::BrowserContext::GetDownloadManager(browser()->profile()), + 1, + content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_QUIT); + ui_test_utils::NavigateToURLWithDisposition( + browser(), + GURL(download_url), + NEW_BACKGROUND_TAB, + ui_test_utils::BROWSER_TEST_NONE); + observer.WaitForFinished(); + + // Check that the download manager has the expected state. + EXPECT_EQ(1, content::BrowserContext::GetDownloadManager( + browser()->profile())->InProgressCount()); + EXPECT_EQ(0, content::BrowserContext::GetDownloadManager( + browser()->profile())->NonMaliciousInProgressCount()); + + // Close the browser with no user action. + RepeatedNotificationObserver close_observer( + chrome::NOTIFICATION_BROWSER_CLOSED, 1); + TestBrowserCloseManager::AttemptClose( + TestBrowserCloseManager::NO_USER_CHOICE); + close_observer.Wait(); + EXPECT_TRUE(browser_shutdown::IsTryingToQuit()); + EXPECT_TRUE(chrome::BrowserIterator().done()); +} + // Test shutdown with a download in progress. IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest, TestWithDownloads) { ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); diff --git a/chrome/browser/local_discovery/privet_device_lister_impl.cc b/chrome/browser/local_discovery/privet_device_lister_impl.cc index cf153a41bf..406bfae799 100644 --- a/chrome/browser/local_discovery/privet_device_lister_impl.cc +++ b/chrome/browser/local_discovery/privet_device_lister_impl.cc @@ -4,109 +4,42 @@ #include "chrome/browser/local_discovery/privet_device_lister_impl.h" -#include <string> #include <utility> #include <vector> -#include "base/bind.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "chrome/browser/local_discovery/privet_constants.h" namespace local_discovery { -PrivetDeviceListerImpl::PrivetDeviceListerImpl( - ServiceDiscoveryClient* service_discovery_client, - PrivetDeviceLister::Delegate* delegate) - : delegate_(delegate), - service_discovery_client_(service_discovery_client), - service_type_(kPrivetDefaultDeviceType) { -} +namespace { -PrivetDeviceListerImpl::PrivetDeviceListerImpl( - ServiceDiscoveryClient* service_discovery_client, - PrivetDeviceLister::Delegate* delegate, - std::string subtype) : delegate_(delegate), - service_discovery_client_(service_discovery_client), - service_type_( - base::StringPrintf(kPrivetSubtypeTemplate, - subtype.c_str())) { -} -PrivetDeviceListerImpl::~PrivetDeviceListerImpl() { -} - -void PrivetDeviceListerImpl::Start() { - CreateServiceWatcher(); -} - -void PrivetDeviceListerImpl::DiscoverNewDevices(bool force_update) { - service_watcher_->DiscoverNewServices(force_update); -} - -void PrivetDeviceListerImpl::OnServiceUpdated( - ServiceWatcher::UpdateType update, - const std::string& service_name) { - if (update == ServiceWatcher::UPDATE_INVALIDATED) { - resolvers_.clear(); - CreateServiceWatcher(); - - delegate_->DeviceCacheFlushed(); - return; - } - - if (update != ServiceWatcher::UPDATE_REMOVED) { - bool added = (update == ServiceWatcher::UPDATE_ADDED); - std::pair<ServiceResolverMap::iterator, bool> insert_result = - resolvers_.insert(make_pair(service_name, - linked_ptr<ServiceResolver>(NULL))); - - // If there is already a resolver working on this service, don't add one. - if (insert_result.second) { - scoped_ptr<ServiceResolver> resolver = - service_discovery_client_->CreateServiceResolver( - service_name, base::Bind( - &PrivetDeviceListerImpl::OnResolveComplete, - base::Unretained(this), - added)); - - insert_result.first->second.reset(resolver.release()); - insert_result.first->second->StartResolving(); - } - } else { - delegate_->DeviceRemoved(service_name); - } -} - -void PrivetDeviceListerImpl::OnResolveComplete( - bool added, - ServiceResolver::RequestStatus status, - const ServiceDescription& service_description) { - if (status != ServiceResolver::STATUS_SUCCESS) { - resolvers_.erase(service_description.service_name); - - // TODO(noamsml): Add retry logic. - return; +DeviceDescription::ConnectionState +ConnectionStateFromString(const std::string& str) { + if (LowerCaseEqualsASCII(str, kPrivetConnectionStatusOnline)) { + return DeviceDescription::ONLINE; + } else if (LowerCaseEqualsASCII(str, kPrivetConnectionStatusOffline)) { + return DeviceDescription::OFFLINE; + } else if (LowerCaseEqualsASCII(str, kPrivetConnectionStatusConnecting)) { + return DeviceDescription::CONNECTING; + } else if (LowerCaseEqualsASCII(str, kPrivetConnectionStatusNotConfigured)) { + return DeviceDescription::NOT_CONFIGURED; } - DeviceDescription device_description; - FillDeviceDescription(service_description, &device_description); - - std::string service_name = service_description.service_name; - resolvers_.erase(service_name); - delegate_->DeviceChanged(added, service_name, device_description); + return DeviceDescription::UNKNOWN; } -void PrivetDeviceListerImpl::FillDeviceDescription( - const ServiceDescription& service_description, - DeviceDescription* device_description) { +void FillDeviceDescription(const ServiceDescription& service_description, + DeviceDescription* device_description) { device_description->address = service_description.address; device_description->ip_address = service_description.ip_address; device_description->last_seen = service_description.last_seen; for (std::vector<std::string>::const_iterator i = service_description.metadata.begin(); - i < service_description.metadata.end(); + i != service_description.metadata.end(); i++) { size_t equals_pos = i->find_first_of('='); if (equals_pos == std::string::npos) @@ -131,29 +64,57 @@ void PrivetDeviceListerImpl::FillDeviceDescription( } } -DeviceDescription::ConnectionState -PrivetDeviceListerImpl::ConnectionStateFromString(const std::string& str) { - if (LowerCaseEqualsASCII(str, kPrivetConnectionStatusOnline)) { - return DeviceDescription::ONLINE; - } else if (LowerCaseEqualsASCII(str, kPrivetConnectionStatusOffline)) { - return DeviceDescription::OFFLINE; - } else if (LowerCaseEqualsASCII(str, kPrivetConnectionStatusConnecting)) { - return DeviceDescription::CONNECTING; - } else if (LowerCaseEqualsASCII(str, kPrivetConnectionStatusNotConfigured)) { - return DeviceDescription::NOT_CONFIGURED; - } +} // namespace - return DeviceDescription::UNKNOWN; +PrivetDeviceListerImpl::PrivetDeviceListerImpl( + ServiceDiscoveryClient* service_discovery_client, + PrivetDeviceLister::Delegate* delegate) + : delegate_(delegate), + device_lister_(this, service_discovery_client, kPrivetDefaultDeviceType) { } -void PrivetDeviceListerImpl::CreateServiceWatcher() { - service_watcher_ = - service_discovery_client_->CreateServiceWatcher( - service_type_, - base::Bind(&PrivetDeviceListerImpl::OnServiceUpdated, - base::Unretained(this))); - service_watcher_->Start(); +PrivetDeviceListerImpl::PrivetDeviceListerImpl( + ServiceDiscoveryClient* service_discovery_client, + PrivetDeviceLister::Delegate* delegate, + const std::string& subtype) + : delegate_(delegate), + device_lister_( + this, + service_discovery_client, + base::StringPrintf(kPrivetSubtypeTemplate, subtype.c_str())) { +} +PrivetDeviceListerImpl::~PrivetDeviceListerImpl() { +} + +void PrivetDeviceListerImpl::Start() { + device_lister_.Start(); +} + +void PrivetDeviceListerImpl::DiscoverNewDevices(bool force_update) { + device_lister_.DiscoverNewDevices(force_update); +} + +void PrivetDeviceListerImpl::OnDeviceChanged( + bool added, const ServiceDescription& service_description) { + if (!delegate_) + return; + + DeviceDescription device_description; + FillDeviceDescription(service_description, &device_description); + + delegate_->DeviceChanged( + added, service_description.service_name, device_description); +} + +void PrivetDeviceListerImpl::OnDeviceRemoved(const std::string& service_name) { + if (delegate_) + delegate_->DeviceRemoved(service_name); +} + +void PrivetDeviceListerImpl::OnDeviceCacheFlushed() { + if (delegate_) + delegate_->DeviceCacheFlushed(); } } // namespace local_discovery diff --git a/chrome/browser/local_discovery/privet_device_lister_impl.h b/chrome/browser/local_discovery/privet_device_lister_impl.h index 64cfba1841..a1888aea00 100644 --- a/chrome/browser/local_discovery/privet_device_lister_impl.h +++ b/chrome/browser/local_discovery/privet_device_lister_impl.h @@ -5,23 +5,22 @@ #ifndef CHROME_BROWSER_LOCAL_DISCOVERY_PRIVET_DEVICE_LISTER_IMPL_H_ #define CHROME_BROWSER_LOCAL_DISCOVERY_PRIVET_DEVICE_LISTER_IMPL_H_ -#include <map> #include <string> -#include "base/callback.h" -#include "base/memory/linked_ptr.h" -#include "base/memory/scoped_ptr.h" #include "chrome/browser/local_discovery/privet_device_lister.h" -#include "chrome/common/local_discovery/service_discovery_client.h" +#include "chrome/browser/local_discovery/service_discovery_device_lister.h" namespace local_discovery { -class PrivetDeviceListerImpl : public PrivetDeviceLister { +class ServiceDiscoveryClient; + +class PrivetDeviceListerImpl : public PrivetDeviceLister, + public ServiceDiscoveryDeviceLister::Delegate { public: PrivetDeviceListerImpl( ServiceDiscoveryClient* service_discovery_client, PrivetDeviceLister::Delegate* delegate, - std::string subtype); + const std::string& subtype); PrivetDeviceListerImpl( ServiceDiscoveryClient* service_discovery_client, @@ -30,36 +29,18 @@ class PrivetDeviceListerImpl : public PrivetDeviceLister { virtual ~PrivetDeviceListerImpl(); virtual void Start() OVERRIDE; - virtual void DiscoverNewDevices(bool force_update) OVERRIDE; - private: - typedef std::map<std::string, linked_ptr<ServiceResolver> > - ServiceResolverMap; - - void OnServiceUpdated(ServiceWatcher::UpdateType update, - const std::string& service_name); - - void OnResolveComplete( + protected: + virtual void OnDeviceChanged( bool added, - ServiceResolver::RequestStatus status, - const ServiceDescription& description); - - void FillDeviceDescription(const ServiceDescription& service_description, - DeviceDescription* device_description); - - DeviceDescription::ConnectionState ConnectionStateFromString( - const std::string& str); - - // Create or recreate the service watcher - void CreateServiceWatcher(); + const ServiceDescription& service_description) OVERRIDE; + virtual void OnDeviceRemoved(const std::string& service_name) OVERRIDE; + virtual void OnDeviceCacheFlushed() OVERRIDE; + private: PrivetDeviceLister::Delegate* delegate_; - - ServiceDiscoveryClient* service_discovery_client_; - scoped_ptr<ServiceWatcher> service_watcher_; - ServiceResolverMap resolvers_; - std::string service_type_; + ServiceDiscoveryDeviceLister device_lister_; }; } // namespace local_discovery diff --git a/chrome/browser/local_discovery/privet_url_fetcher.cc b/chrome/browser/local_discovery/privet_url_fetcher.cc index 52da7872ad..f45d4772b9 100644 --- a/chrome/browser/local_discovery/privet_url_fetcher.cc +++ b/chrome/browser/local_discovery/privet_url_fetcher.cc @@ -14,6 +14,7 @@ namespace local_discovery { namespace { const char kXPrivetTokenHeaderPrefix[] = "X-Privet-Token: "; +const char kXPrivetEmptyToken[] = "\"\""; } PrivetURLFetcher::PrivetURLFetcher( @@ -22,11 +23,15 @@ PrivetURLFetcher::PrivetURLFetcher( net::URLFetcher::RequestType request_type, net::URLRequestContextGetter* request_context, PrivetURLFetcher::Delegate* delegate) - : privet_access_token_(token), delegate_(delegate) { + : delegate_(delegate) { + std::string sent_token = token; + if (sent_token.empty()) + sent_token = kXPrivetEmptyToken; + url_fetcher_.reset(net::URLFetcher::Create(url, request_type, this)); url_fetcher_->SetRequestContext(request_context); url_fetcher_->AddExtraRequestHeader(std::string(kXPrivetTokenHeaderPrefix) + - token); + sent_token); // URLFetcher requires us to set upload data for POST requests. if (request_type == net::URLFetcher::POST) diff --git a/chrome/browser/local_discovery/privet_url_fetcher.h b/chrome/browser/local_discovery/privet_url_fetcher.h index c96b894c28..a99ea482e8 100644 --- a/chrome/browser/local_discovery/privet_url_fetcher.h +++ b/chrome/browser/local_discovery/privet_url_fetcher.h @@ -54,7 +54,6 @@ class PrivetURLFetcher : public net::URLFetcherDelegate { private: scoped_ptr<net::URLFetcher> url_fetcher_; - std::string privet_access_token_; Delegate* delegate_; DISALLOW_COPY_AND_ASSIGN(PrivetURLFetcher); diff --git a/chrome/browser/local_discovery/privet_url_fetcher_unittest.cc b/chrome/browser/local_discovery/privet_url_fetcher_unittest.cc index da9b743985..b7fa5f353c 100644 --- a/chrome/browser/local_discovery/privet_url_fetcher_unittest.cc +++ b/chrome/browser/local_discovery/privet_url_fetcher_unittest.cc @@ -17,6 +17,7 @@ namespace { const char kSamplePrivetURL[] = "http://10.0.0.8:7676/privet/register?action=start"; const char kSamplePrivetToken[] = "MyToken"; +const char kEmptyPrivetToken[] = "\"\""; const char kSampleParsableJSON[] = "{ \"hello\" : 2 }"; const char kSampleUnparsableJSON[] = "{ \"hello\" : }"; @@ -141,6 +142,25 @@ TEST_F(PrivetURLFetcherTest, Header) { EXPECT_EQ(kSamplePrivetToken, header_token); } +TEST_F(PrivetURLFetcherTest, Header2) { + privet_urlfetcher_.reset(new PrivetURLFetcher( + "", + GURL(kSamplePrivetURL), + net::URLFetcher::POST, + request_context_.get(), + &delegate_)); + + privet_urlfetcher_->Start(); + net::TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID(0); + ASSERT_TRUE(fetcher != NULL); + net::HttpRequestHeaders headers; + fetcher->GetExtraRequestHeaders(&headers); + + std::string header_token; + ASSERT_TRUE(headers.GetHeader("X-Privet-Token", &header_token)); + EXPECT_EQ(kEmptyPrivetToken, header_token); +} + TEST_F(PrivetURLFetcherTest, FetchHasError) { privet_urlfetcher_->Start(); net::TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID(0); diff --git a/chrome/browser/local_discovery/service_discovery_device_lister.cc b/chrome/browser/local_discovery/service_discovery_device_lister.cc new file mode 100644 index 0000000000..a6c025ac5e --- /dev/null +++ b/chrome/browser/local_discovery/service_discovery_device_lister.cc @@ -0,0 +1,92 @@ +// Copyright 2013 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/local_discovery/service_discovery_device_lister.h" + +#include <utility> +#include <vector> + +#include "base/bind.h" + +namespace local_discovery { + +ServiceDiscoveryDeviceLister::ServiceDiscoveryDeviceLister( + Delegate* delegate, + ServiceDiscoveryClient* service_discovery_client, + const std::string& service_type) + : delegate_(delegate), + service_discovery_client_(service_discovery_client), + service_type_(service_type) { +} + +ServiceDiscoveryDeviceLister::~ServiceDiscoveryDeviceLister() { +} + +void ServiceDiscoveryDeviceLister::Start() { + CreateServiceWatcher(); +} + +void ServiceDiscoveryDeviceLister::DiscoverNewDevices(bool force_update) { + service_watcher_->DiscoverNewServices(force_update); +} + +void ServiceDiscoveryDeviceLister::OnServiceUpdated( + ServiceWatcher::UpdateType update, + const std::string& service_name) { + if (update == ServiceWatcher::UPDATE_INVALIDATED) { + resolvers_.clear(); + CreateServiceWatcher(); + + delegate_->OnDeviceCacheFlushed(); + return; + } + + if (update != ServiceWatcher::UPDATE_REMOVED) { + bool added = (update == ServiceWatcher::UPDATE_ADDED); + std::pair<ServiceResolverMap::iterator, bool> insert_result = + resolvers_.insert(make_pair(service_name, + linked_ptr<ServiceResolver>(NULL))); + + // If there is already a resolver working on this service, don't add one. + if (insert_result.second) { + scoped_ptr<ServiceResolver> resolver = + service_discovery_client_->CreateServiceResolver( + service_name, base::Bind( + &ServiceDiscoveryDeviceLister::OnResolveComplete, + base::Unretained(this), + added)); + + insert_result.first->second.reset(resolver.release()); + insert_result.first->second->StartResolving(); + } + } else { + delegate_->OnDeviceRemoved(service_name); + } +} + +void ServiceDiscoveryDeviceLister::OnResolveComplete( + bool added, + ServiceResolver::RequestStatus status, + const ServiceDescription& service_description) { + if (status != ServiceResolver::STATUS_SUCCESS) { + resolvers_.erase(service_description.service_name); + + // TODO(noamsml): Add retry logic. + return; + } + + delegate_->OnDeviceChanged(added, service_description); + resolvers_.erase(service_description.service_name); +} + +void ServiceDiscoveryDeviceLister::CreateServiceWatcher() { + service_watcher_ = + service_discovery_client_->CreateServiceWatcher( + service_type_, + base::Bind(&ServiceDiscoveryDeviceLister::OnServiceUpdated, + base::Unretained(this))); + service_watcher_->Start(); +} + +} // namespace local_discovery diff --git a/chrome/browser/local_discovery/service_discovery_device_lister.h b/chrome/browser/local_discovery/service_discovery_device_lister.h new file mode 100644 index 0000000000..2baab45d50 --- /dev/null +++ b/chrome/browser/local_discovery/service_discovery_device_lister.h @@ -0,0 +1,63 @@ +// Copyright 2013 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_LOCAL_DISCOVERY_SERVICE_DISCOVERY_DEVICE_LISTER_H_ +#define CHROME_BROWSER_LOCAL_DISCOVERY_SERVICE_DISCOVERY_DEVICE_LISTER_H_ + +#include <map> +#include <string> + +#include "base/memory/linked_ptr.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/common/local_discovery/service_discovery_client.h" + +namespace local_discovery { + +class ServiceDiscoveryDeviceLister { + public: + class Delegate { + public: + virtual void OnDeviceChanged( + bool added, + const ServiceDescription& service_description) = 0; + virtual void OnDeviceRemoved(const std::string& service_name) = 0; + virtual void OnDeviceCacheFlushed() = 0; + }; + + ServiceDiscoveryDeviceLister(Delegate* delegate, + ServiceDiscoveryClient* service_discovery_client, + const std::string& service_type); + virtual ~ServiceDiscoveryDeviceLister(); + + void Start(); + void DiscoverNewDevices(bool force_update); + + std::string service_type() { return service_type_; } + + private: + typedef std::map<std::string, linked_ptr<ServiceResolver> > + ServiceResolverMap; + + void OnServiceUpdated(ServiceWatcher::UpdateType update, + const std::string& service_name); + + void OnResolveComplete( + bool added, + ServiceResolver::RequestStatus status, + const ServiceDescription& description); + + // Create or recreate the service watcher + void CreateServiceWatcher(); + + Delegate* const delegate_; + ServiceDiscoveryClient* const service_discovery_client_; + const std::string service_type_; + + scoped_ptr<ServiceWatcher> service_watcher_; + ServiceResolverMap resolvers_; +}; + +} // namespace local_discovery + +#endif // CHROME_BROWSER_LOCAL_DISCOVERY_SERVICE_DISCOVERY_DEVICE_LISTER_H_ diff --git a/chrome/browser/managed_mode/managed_mode_browsertest.cc b/chrome/browser/managed_mode/managed_mode_browsertest.cc index 67e54ed72a..86e3817d71 100644 --- a/chrome/browser/managed_mode/managed_mode_browsertest.cc +++ b/chrome/browser/managed_mode/managed_mode_browsertest.cc @@ -107,7 +107,6 @@ class ManagedModeBlockModeTest : public InProcessBrowserTest { Profile* profile = browser()->profile(); managed_user_service_ = ManagedUserServiceFactory::GetForProfile(profile); - managed_user_service_->InitForTesting(); ManagedUserSettingsService* managed_user_settings_service = ManagedUserSettingsServiceFactory::GetForProfile(profile); managed_user_settings_service->SetLocalSettingForTesting( @@ -124,6 +123,8 @@ class ManagedModeBlockModeTest : public InProcessBrowserTest { "MAP *.example.com " + host_port + "," + "MAP *.new-example.com " + host_port + "," + "MAP *.a.com " + host_port); + + command_line->AppendSwitch(switches::kNewProfileIsSupervised); } // Acts like a synchronous call to history's QueryHistory. Modified from diff --git a/chrome/browser/managed_mode/managed_mode_navigation_observer.cc b/chrome/browser/managed_mode/managed_mode_navigation_observer.cc index 3b0548c1ce..78e6011ca3 100644 --- a/chrome/browser/managed_mode/managed_mode_navigation_observer.cc +++ b/chrome/browser/managed_mode/managed_mode_navigation_observer.cc @@ -181,6 +181,7 @@ void ManagedModeNavigationObserver::ProvisionalChangeToMainFrameUrl( void ManagedModeNavigationObserver::DidCommitProvisionalLoadForFrame( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& url, content::PageTransition transition_type, diff --git a/chrome/browser/managed_mode/managed_mode_navigation_observer.h b/chrome/browser/managed_mode/managed_mode_navigation_observer.h index 03863f3151..ac19d81aca 100644 --- a/chrome/browser/managed_mode/managed_mode_navigation_observer.h +++ b/chrome/browser/managed_mode/managed_mode_navigation_observer.h @@ -53,6 +53,7 @@ class ManagedModeNavigationObserver content::RenderViewHost* render_view_host) OVERRIDE; virtual void DidCommitProvisionalLoadForFrame( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& url, content::PageTransition transition_type, diff --git a/chrome/browser/managed_mode/managed_mode_resource_throttle_browsertest.cc b/chrome/browser/managed_mode/managed_mode_resource_throttle_browsertest.cc index 9fdf8a64b0..9bfb4608aa 100644 --- a/chrome/browser/managed_mode/managed_mode_resource_throttle_browsertest.cc +++ b/chrome/browser/managed_mode/managed_mode_resource_throttle_browsertest.cc @@ -4,6 +4,7 @@ #include "chrome/browser/managed_mode/managed_mode_resource_throttle.h" +#include "base/command_line.h" #include "base/prefs/pref_service.h" #include "base/values.h" #include "chrome/browser/managed_mode/managed_user_constants.h" @@ -13,6 +14,7 @@ #include "chrome/browser/managed_mode/managed_user_settings_service_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" +#include "chrome/common/chrome_switches.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" #include "content/public/browser/navigation_entry.h" @@ -33,6 +35,7 @@ class ManagedModeResourceThrottleTest : public InProcessBrowserTest { private: virtual void SetUpOnMainThread() OVERRIDE; + virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE; ManagedUserService* managed_user_service_; }; @@ -40,7 +43,11 @@ class ManagedModeResourceThrottleTest : public InProcessBrowserTest { void ManagedModeResourceThrottleTest::SetUpOnMainThread() { managed_user_service_ = ManagedUserServiceFactory::GetForProfile(browser()->profile()); - managed_user_service_->InitForTesting(); +} + +void ManagedModeResourceThrottleTest::SetUpCommandLine( + CommandLine* command_line) { + command_line->AppendSwitch(switches::kNewProfileIsSupervised); } // Tests that showing the blocking interstitial for a WebContents without a diff --git a/chrome/browser/managed_mode/managed_user_service.cc b/chrome/browser/managed_mode/managed_user_service.cc index 96bb3a1d2f..fe204fab9f 100644 --- a/chrome/browser/managed_mode/managed_user_service.cc +++ b/chrome/browser/managed_mode/managed_user_service.cc @@ -51,6 +51,7 @@ #include "ui/base/l10n/l10n_util.h" #if defined(OS_CHROMEOS) +#include "chrome/browser/chromeos/login/supervised_user_manager.h" #include "chrome/browser/chromeos/login/user_manager.h" #endif @@ -259,8 +260,8 @@ void ManagedUserService::GetCategoryNames(CategoryList* list) { std::string ManagedUserService::GetCustodianEmailAddress() const { #if defined(OS_CHROMEOS) - return chromeos::UserManager::Get()-> - GetManagerDisplayEmailForManagedUser( + return chromeos::UserManager::Get()->GetSupervisedUserManager()-> + GetManagerDisplayEmail( chromeos::UserManager::Get()->GetActiveUser()->email()); #else return profile_->GetPrefs()->GetString(prefs::kManagedUserCustodianEmail); @@ -269,8 +270,8 @@ std::string ManagedUserService::GetCustodianEmailAddress() const { std::string ManagedUserService::GetCustodianName() const { #if defined(OS_CHROMEOS) - return UTF16ToUTF8(chromeos::UserManager::Get()-> - GetManagerDisplayNameForManagedUser( + return UTF16ToUTF8(chromeos::UserManager::Get()->GetSupervisedUserManager()-> + GetManagerDisplayName( chromeos::UserManager::Get()->GetActiveUser()->email())); #else std::string name = profile_->GetPrefs()->GetString( @@ -293,11 +294,6 @@ void ManagedUserService::DidBlockNavigation( } } -void ManagedUserService::AddInitCallback( - const base::Closure& callback) { - init_callbacks_.push_back(callback); -} - std::string ManagedUserService::GetDebugPolicyProviderName() const { // Save the string space in official builds. #ifdef NDEBUG @@ -528,12 +524,6 @@ void ManagedUserService::GetManualExceptionsForHost(const std::string& host, } } -void ManagedUserService::InitForTesting() { - DCHECK(!profile_->IsManaged()); - profile_->GetPrefs()->SetString(prefs::kManagedUserId, "Test ID"); - Init(); -} - void ManagedUserService::InitSync(const std::string& refresh_token) { ProfileSyncService* service = ProfileSyncServiceFactory::GetForProfile(profile_); @@ -611,14 +601,6 @@ void ManagedUserService::Init() { UpdateSiteLists(); UpdateManualHosts(); UpdateManualURLs(); - - // Call the callbacks to notify that the ManagedUserService has been - // initialized. - for (std::vector<base::Closure>::iterator it = init_callbacks_.begin(); - it != init_callbacks_.end(); - ++it) { - it->Run(); - } } void ManagedUserService::RegisterAndInitSync( diff --git a/chrome/browser/managed_mode/managed_user_service.h b/chrome/browser/managed_mode/managed_user_service.h index b2d05f6655..baf790cb80 100644 --- a/chrome/browser/managed_mode/managed_user_service.h +++ b/chrome/browser/managed_mode/managed_user_service.h @@ -116,9 +116,6 @@ class ManagedUserService : public BrowserContextKeyedService, // managed. void Init(); - // Marks the profile as managed and initializes it. - void InitForTesting(); - // Initializes this profile for syncing, using the provided |refresh_token| to // mint access tokens for Sync. void InitSync(const std::string& refresh_token); @@ -144,8 +141,6 @@ class ManagedUserService : public BrowserContextKeyedService, void AddNavigationBlockedCallback(const NavigationBlockedCallback& callback); void DidBlockNavigation(content::WebContents* web_contents); - void AddInitCallback(const base::Closure& callback); - // extensions::ManagementPolicy::Provider implementation: virtual std::string GetDebugPolicyProviderName() const OVERRIDE; virtual bool UserMayLoad(const extensions::Extension* extension, @@ -165,7 +160,7 @@ class ManagedUserService : public BrowserContextKeyedService, virtual void OnBrowserSetLastActive(Browser* browser) OVERRIDE; private: - friend class ManagedUserServiceExtensionTest; + friend class ManagedUserServiceExtensionTestBase; friend class ManagedUserServiceFactory; FRIEND_TEST_ALL_PREFIXES(ManagedUserServiceTest, ExtensionManagementPolicyProviderUnmanaged); @@ -259,8 +254,6 @@ class ManagedUserService : public BrowserContextKeyedService, bool waiting_for_sync_initialization_; bool is_profile_active_; - std::vector<base::Closure> init_callbacks_; - std::vector<NavigationBlockedCallback> navigation_blocked_callbacks_; // Sets a profile in elevated state for testing if set to true. diff --git a/chrome/browser/managed_mode/managed_user_service_browsertest.cc b/chrome/browser/managed_mode/managed_user_service_browsertest.cc index 9af82f642f..571f413a21 100644 --- a/chrome/browser/managed_mode/managed_user_service_browsertest.cc +++ b/chrome/browser/managed_mode/managed_user_service_browsertest.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/command_line.h" #include "base/prefs/pref_service.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/browser_process.h" @@ -14,24 +15,26 @@ #include "chrome/browser/profiles/profile_info_cache.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/browser.h" +#include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/in_process_browser_test.h" #include "content/public/test/test_utils.h" typedef InProcessBrowserTest ManagedUserServiceTest; +class ManagedUserServiceTestManaged : public InProcessBrowserTest { + public: + // content::BrowserTestBase: + virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { + command_line->AppendSwitch(switches::kNewProfileIsSupervised); + } +}; + IN_PROC_BROWSER_TEST_F(ManagedUserServiceTest, LocalPolicies) { Profile* profile = browser()->profile(); PrefService* prefs = profile->GetPrefs(); EXPECT_FALSE(prefs->GetBoolean(prefs::kForceSafeSearch)); EXPECT_TRUE(prefs->IsUserModifiablePreference(prefs::kForceSafeSearch)); - - ManagedUserService* managed_user_service = - ManagedUserServiceFactory::GetForProfile(profile); - managed_user_service->InitForTesting(); - - EXPECT_TRUE(prefs->GetBoolean(prefs::kForceSafeSearch)); - EXPECT_FALSE(prefs->IsUserModifiablePreference(prefs::kForceSafeSearch)); } IN_PROC_BROWSER_TEST_F(ManagedUserServiceTest, ProfileName) { @@ -45,11 +48,22 @@ IN_PROC_BROWSER_TEST_F(ManagedUserServiceTest, ProfileName) { size_t profile_index = cache.GetIndexOfProfileWithPath(profile->GetPath()); EXPECT_EQ(original_name, UTF16ToUTF8(cache.GetNameOfProfileAtIndex(profile_index))); +} + +IN_PROC_BROWSER_TEST_F(ManagedUserServiceTestManaged, LocalPolicies) { + Profile* profile = browser()->profile(); + PrefService* prefs = profile->GetPrefs(); + EXPECT_TRUE(prefs->GetBoolean(prefs::kForceSafeSearch)); + EXPECT_FALSE(prefs->IsUserModifiablePreference(prefs::kForceSafeSearch)); +} + +IN_PROC_BROWSER_TEST_F(ManagedUserServiceTestManaged, ProfileName) { + Profile* profile = browser()->profile(); + PrefService* prefs = profile->GetPrefs(); + std::string original_name = prefs->GetString(prefs::kProfileName); + ProfileManager* profile_manager = g_browser_process->profile_manager(); + const ProfileInfoCache& cache = profile_manager->GetProfileInfoCache(); - // Change the profile to a managed user. - ManagedUserService* managed_user_service = - ManagedUserServiceFactory::GetForProfile(profile); - managed_user_service->InitForTesting(); ManagedUserSettingsService* settings = ManagedUserSettingsServiceFactory::GetForProfile(profile); @@ -59,7 +73,7 @@ IN_PROC_BROWSER_TEST_F(ManagedUserServiceTest, ProfileName) { scoped_ptr<base::Value>(new base::StringValue(name))); EXPECT_FALSE(prefs->IsUserModifiablePreference(prefs::kProfileName)); EXPECT_EQ(name, prefs->GetString(prefs::kProfileName)); - profile_index = cache.GetIndexOfProfileWithPath(profile->GetPath()); + size_t profile_index = cache.GetIndexOfProfileWithPath(profile->GetPath()); EXPECT_EQ(name, UTF16ToUTF8(cache.GetNameOfProfileAtIndex(profile_index))); // Change the name once more. diff --git a/chrome/browser/managed_mode/managed_user_service_unittest.cc b/chrome/browser/managed_mode/managed_user_service_unittest.cc index 42a2a32f63..97183ebbed 100644 --- a/chrome/browser/managed_mode/managed_user_service_unittest.cc +++ b/chrome/browser/managed_mode/managed_user_service_unittest.cc @@ -20,7 +20,7 @@ #include "chrome/common/extensions/extension_builder.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/testing_profile.h" -#include "content/public/test/test_browser_thread.h" +#include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/test_utils.h" #include "extensions/common/manifest_constants.h" #include "testing/gtest/include/gtest/gtest.h" @@ -66,16 +66,14 @@ class ManagedModeURLFilterObserver : public ManagedModeURLFilter::Observer { class ManagedUserServiceTest : public ::testing::Test { public: - ManagedUserServiceTest() : ui_thread_(content::BrowserThread::UI, - &message_loop_) { + ManagedUserServiceTest() { managed_user_service_ = ManagedUserServiceFactory::GetForProfile(&profile_); } virtual ~ManagedUserServiceTest() {} protected: - base::MessageLoop message_loop_; - content::TestBrowserThread ui_thread_; + content::TestBrowserThreadBundle thread_bundle_; TestingProfile profile_; ManagedUserService* managed_user_service_; }; @@ -152,18 +150,19 @@ TEST_F(ManagedUserServiceTest, ShutDownCustodianProfileDownloader) { downloader_service->DownloadProfile(base::Bind(&OnProfileDownloadedFail)); } -class ManagedUserServiceExtensionTest : public ExtensionServiceTestBase { +class ManagedUserServiceExtensionTestBase : public ExtensionServiceTestBase { public: - ManagedUserServiceExtensionTest() {} - virtual ~ManagedUserServiceExtensionTest() {} + explicit ManagedUserServiceExtensionTestBase(bool is_managed) + : is_managed_(is_managed) {} + virtual ~ManagedUserServiceExtensionTestBase() {} virtual void SetUp() OVERRIDE { ExtensionServiceTestBase::SetUp(); - InitializeEmptyExtensionService(); - } - - virtual void TearDown() OVERRIDE { - ExtensionServiceTestBase::TearDown(); + ExtensionServiceTestBase::ExtensionServiceInitParams params = + CreateDefaultInitParams(); + params.profile_is_managed = is_managed_; + InitializeExtensionService(params); + ManagedUserServiceFactory::GetForProfile(profile_.get())->Init(); } protected: @@ -193,10 +192,26 @@ class ManagedUserServiceExtensionTest : public ExtensionServiceTestBase { builder.SetManifest(manifest.Pass()).Build(); return extension; } + + bool is_managed_; +}; + +class ManagedUserServiceExtensionTestUnmanaged + : public ManagedUserServiceExtensionTestBase { + public: + ManagedUserServiceExtensionTestUnmanaged() + : ManagedUserServiceExtensionTestBase(false) {} }; -TEST_F(ManagedUserServiceExtensionTest, - ExtensionManagementPolicyProviderUnmanaged) { +class ManagedUserServiceExtensionTest + : public ManagedUserServiceExtensionTestBase { + public: + ManagedUserServiceExtensionTest() + : ManagedUserServiceExtensionTestBase(true) {} +}; + +TEST_F(ManagedUserServiceExtensionTestUnmanaged, + ExtensionManagementPolicyProvider) { ManagedUserService* managed_user_service = ManagedUserServiceFactory::GetForProfile(profile_.get()); EXPECT_FALSE(profile_->IsManaged()); @@ -212,14 +227,12 @@ TEST_F(ManagedUserServiceExtensionTest, EXPECT_EQ(string16(), error_2); } -TEST_F(ManagedUserServiceExtensionTest, - ExtensionManagementPolicyProviderManaged) { +TEST_F(ManagedUserServiceExtensionTest, ExtensionManagementPolicyProvider) { ManagedUserService* managed_user_service = ManagedUserServiceFactory::GetForProfile(profile_.get()); - managed_user_service->InitForTesting(); ManagedModeURLFilterObserver observer( managed_user_service->GetURLFilterForUIThread()); - EXPECT_TRUE(profile_->IsManaged()); + ASSERT_TRUE(profile_->IsManaged()); // Wait for the initial update to finish (otherwise we'll get leaks). observer.Wait(); @@ -247,11 +260,9 @@ TEST_F(ManagedUserServiceExtensionTest, #endif } - TEST_F(ManagedUserServiceExtensionTest, NoContentPacks) { ManagedUserService* managed_user_service = ManagedUserServiceFactory::GetForProfile(profile_.get()); - managed_user_service->Init(); ManagedModeURLFilter* url_filter = managed_user_service->GetURLFilterForUIThread(); @@ -266,7 +277,6 @@ TEST_F(ManagedUserServiceExtensionTest, NoContentPacks) { TEST_F(ManagedUserServiceExtensionTest, InstallContentPacks) { ManagedUserService* managed_user_service = ManagedUserServiceFactory::GetForProfile(profile_.get()); - managed_user_service->InitForTesting(); ManagedModeURLFilter* url_filter = managed_user_service->GetURLFilterForUIThread(); ManagedModeURLFilterObserver observer(url_filter); diff --git a/chrome/browser/managed_mode/managed_user_sync_service.cc b/chrome/browser/managed_mode/managed_user_sync_service.cc index 5ce6b4d608..9b673a35c9 100644 --- a/chrome/browser/managed_mode/managed_user_sync_service.cc +++ b/chrome/browser/managed_mode/managed_user_sync_service.cc @@ -36,7 +36,11 @@ using sync_pb::ManagedUserSpecifics; namespace { +#if defined(OS_CHROMEOS) +const char kChromeOSAvatarPrefix[] = "chromeos-avatar-index:"; +#else const char kChromeAvatarPrefix[] = "chrome-avatar-index:"; +#endif SyncData CreateLocalSyncData(const std::string& id, const std::string& name, @@ -120,10 +124,14 @@ bool ManagedUserSyncService::GetAvatarIndex(const std::string& avatar_str, *avatar_index = kNoAvatar; return true; } - - size_t prefix_len = strlen(kChromeAvatarPrefix); +#if defined(OS_CHROMEOS) + const char* prefix = kChromeOSAvatarPrefix; +#else + const char* prefix = kChromeAvatarPrefix; +#endif + size_t prefix_len = strlen(prefix); if (avatar_str.size() <= prefix_len || - avatar_str.substr(0, prefix_len) != kChromeAvatarPrefix) { + avatar_str.substr(0, prefix_len) != prefix) { return false; } @@ -132,7 +140,12 @@ bool ManagedUserSyncService::GetAvatarIndex(const std::string& avatar_str, // static std::string ManagedUserSyncService::BuildAvatarString(int avatar_index) { - return base::StringPrintf("%s%d", kChromeAvatarPrefix, avatar_index); +#if defined(OS_CHROMEOS) + const char* prefix = kChromeOSAvatarPrefix; +#else + const char* prefix = kChromeAvatarPrefix; +#endif + return base::StringPrintf("%s%d", prefix, avatar_index); } void ManagedUserSyncService::AddObserver( @@ -155,19 +168,14 @@ void ManagedUserSyncService::AddManagedUser(const std::string& id, value->SetString(kName, name); value->SetString(kMasterKey, master_key); std::string chrome_avatar; -#if defined(CHROME_OS) - // This is a dummy value that is passed when a supervised user is created on - // Chrome OS. - // TODO(ibraaaa): update this to use the correct avatar index - // once avatar syncing for supervised users is implemented on Chrome OS. - DCHECK_EQ(avatar_index, -111); + std::string chromeos_avatar; +#if defined(OS_CHROMEOS) + chromeos_avatar = BuildAvatarString(avatar_index); #else chrome_avatar = BuildAvatarString(avatar_index); #endif value->SetString(kChromeAvatar, chrome_avatar); - // TODO(ibraaaa): this should be updated to allow supervised - // users avatar syncing on Chrome OS. - value->SetString(kChromeOsAvatar, std::string()); + value->SetString(kChromeOsAvatar, chromeos_avatar); DCHECK(!dict->HasKey(id)); dict->SetWithoutPathExpansion(id, value); @@ -180,7 +188,7 @@ void ManagedUserSyncService::AddManagedUser(const std::string& id, FROM_HERE, SyncChange::ACTION_ADD, CreateLocalSyncData(id, name, false, master_key, - chrome_avatar, std::string()))); + chrome_avatar, chromeos_avatar))); SyncError error = sync_processor_->ProcessSyncChanges(FROM_HERE, change_list); DCHECK(!error.IsSet()) << error.ToString(); diff --git a/chrome/browser/managed_mode/managed_user_sync_service.h b/chrome/browser/managed_mode/managed_user_sync_service.h index e6f70934eb..d802707b58 100644 --- a/chrome/browser/managed_mode/managed_user_sync_service.h +++ b/chrome/browser/managed_mode/managed_user_sync_service.h @@ -54,7 +54,8 @@ class ManagedUserSyncService : public BrowserContextKeyedService, // where INDEX is the integer to be extracted. |avatar_str| can be empty // in case there is no avatar synced for a managed user in which case // |avatar_index| is set to -1. - static bool GetAvatarIndex(const std::string& avatar_str, int* avatar_index); + static bool GetAvatarIndex(const std::string& avatar_str, + int* avatar_index); // Given an |avatar_index|, it returns a string of the form: // "chrome-avatar-index:INDEX" where INDEX = |avatar_index|. diff --git a/chrome/browser/managed_mode/managed_user_sync_service_unittest.cc b/chrome/browser/managed_mode/managed_user_sync_service_unittest.cc index 9b06cae9ec..fd0e1561a3 100644 --- a/chrome/browser/managed_mode/managed_user_sync_service_unittest.cc +++ b/chrome/browser/managed_mode/managed_user_sync_service_unittest.cc @@ -171,8 +171,13 @@ TEST_F(ManagedUserSyncServiceTest, MergeExisting) { const char kName3[] = "Crush"; const char kName4[] = "Dory"; const char kAvatar1[] = ""; +#if defined(OS_CHROMEOS) + const char kAvatar2[] = "chromeos-avatar-index:0"; + const char kAvatar3[] = "chromeos-avatar-index:20"; +#else const char kAvatar2[] = "chrome-avatar-index:0"; const char kAvatar3[] = "chrome-avatar-index:20"; +#endif const char kAvatar4[] = ""; { DictionaryPrefUpdate update(prefs(), prefs::kManagedUsers); @@ -274,18 +279,42 @@ TEST_F(ManagedUserSyncServiceTest, GetAvatarIndex) { EXPECT_EQ(ManagedUserSyncService::kNoAvatar, avatar); std::string avatar_str = ManagedUserSyncService::BuildAvatarString(24); +#if defined(OS_CHROMEOS) + EXPECT_EQ("chromeos-avatar-index:24", avatar_str); +#else EXPECT_EQ("chrome-avatar-index:24", avatar_str); +#endif EXPECT_TRUE(ManagedUserSyncService::GetAvatarIndex(avatar_str, &avatar)); EXPECT_EQ(24, avatar); avatar_str = ManagedUserSyncService::BuildAvatarString(0); +#if defined(OS_CHROMEOS) + EXPECT_EQ("chromeos-avatar-index:0", avatar_str); +#else EXPECT_EQ("chrome-avatar-index:0", avatar_str); +#endif EXPECT_TRUE(ManagedUserSyncService::GetAvatarIndex(avatar_str, &avatar)); EXPECT_EQ(0, avatar); EXPECT_FALSE(ManagedUserSyncService::GetAvatarIndex("wrong-prefix:5", &avatar)); +#if defined(OS_CHROMEOS) + EXPECT_FALSE(ManagedUserSyncService::GetAvatarIndex("chromeos-avatar-indes:2", + &avatar)); + + EXPECT_FALSE( + ManagedUserSyncService::GetAvatarIndex("chromeos-avatar-indexxx:2", + &avatar)); + EXPECT_FALSE(ManagedUserSyncService::GetAvatarIndex("chromeos-avatar-index:", + &avatar)); + + EXPECT_FALSE(ManagedUserSyncService::GetAvatarIndex("chromeos-avatar-index:x", + &avatar)); + + EXPECT_FALSE(ManagedUserSyncService::GetAvatarIndex("chrome-avatar-index:5", + &avatar)); +#else EXPECT_FALSE(ManagedUserSyncService::GetAvatarIndex("chrome-avatar-indes:2", &avatar)); @@ -297,4 +326,8 @@ TEST_F(ManagedUserSyncServiceTest, GetAvatarIndex) { EXPECT_FALSE(ManagedUserSyncService::GetAvatarIndex("chrome-avatar-index:x", &avatar)); + + EXPECT_FALSE(ManagedUserSyncService::GetAvatarIndex("chromeos-avatar-index:5", + &avatar)); +#endif } diff --git a/chrome/browser/media/chrome_media_stream_infobar_browsertest.cc b/chrome/browser/media/chrome_media_stream_infobar_browsertest.cc index 2aa9b3f921..3e54269df1 100644 --- a/chrome/browser/media/chrome_media_stream_infobar_browsertest.cc +++ b/chrome/browser/media/chrome_media_stream_infobar_browsertest.cc @@ -44,16 +44,24 @@ class MediaStreamInfoBarTest : public WebRtcTestBase { protected: content::WebContents* LoadTestPageInTab() { + return LoadTestPageInBrowser(browser()); + } + + content::WebContents* LoadTestPageInIncognitoTab() { + return LoadTestPageInBrowser(CreateIncognitoBrowser()); + } + + private: + content::WebContents* LoadTestPageInBrowser(Browser* browser) { EXPECT_TRUE(test_server()->Start()); const char kMainWebrtcTestHtmlPage[] = "files/webrtc/webrtc_jsep01_test.html"; ui_test_utils::NavigateToURL( - browser(), test_server()->GetURL(kMainWebrtcTestHtmlPage)); - return browser()->tab_strip_model()->GetActiveWebContents(); + browser, test_server()->GetURL(kMainWebrtcTestHtmlPage)); + return browser->tab_strip_model()->GetActiveWebContents(); } - private: DISALLOW_COPY_AND_ASSIGN(MediaStreamInfoBarTest); }; @@ -82,6 +90,11 @@ IN_PROC_BROWSER_TEST_F(MediaStreamInfoBarTest, TestDismissingInfobar) { GetUserMediaAndDismiss(tab_contents); } +IN_PROC_BROWSER_TEST_F(MediaStreamInfoBarTest, TestDenyingUserMediaIncognito) { + content::WebContents* tab_contents = LoadTestPageInIncognitoTab(); + GetUserMediaAndDeny(tab_contents); +} + // Failing on ChromiumOS Debug and Win Aura, so disabling on Aura. // See http://crbug.com/263333. #if defined(USE_AURA) diff --git a/chrome/browser/media/chrome_webrtc_apprtc_browsertest.cc b/chrome/browser/media/chrome_webrtc_apprtc_browsertest.cc index fa5b0e0784..d0ba4c8a4a 100644 --- a/chrome/browser/media/chrome_webrtc_apprtc_browsertest.cc +++ b/chrome/browser/media/chrome_webrtc_apprtc_browsertest.cc @@ -62,8 +62,7 @@ class WebrtcApprtcBrowserTest : public WebRtcTestBase { } base::FilePath apprtc_dir = - GetSourceDir().Append( - FILE_PATH_LITERAL("third_party/webrtc_apprtc/apprtc")); + GetSourceDir().Append(FILE_PATH_LITERAL("out/apprtc")); if (!base::PathExists(apprtc_dir)) { LOG(ERROR) << "Missing AppRTC code at " << apprtc_dir.value() << ". " << kAdviseOnGclientSolution; @@ -120,11 +119,14 @@ class WebrtcApprtcBrowserTest : public WebRtcTestBase { base::ProcessHandle dev_appserver_; }; -IN_PROC_BROWSER_TEST_F(WebrtcApprtcBrowserTest, MANUAL_WorksOnApprtc) { - if (!LaunchApprtcInstanceOnLocalhost()) { - // TODO(phoglund): assert on this once everything is in place on the bots. - return; - } +#if defined (OS_WIN) || defined(OS_MACOSX) +#define MAYBE_MANUAL_WorksOnApprtc DISABLED_MANUAL_WorksOnApprtc +#else +#define MAYBE_MANUAL_WorksOnApprtc MANUAL_WorksOnApprtc +#endif + +IN_PROC_BROWSER_TEST_F(WebrtcApprtcBrowserTest, MAYBE_MANUAL_WorksOnApprtc) { + ASSERT_TRUE(LaunchApprtcInstanceOnLocalhost()); while (!LocalApprtcInstanceIsUp()) LOG(INFO) << "Waiting for AppRTC to come up..."; diff --git a/chrome/browser/media/chrome_webrtc_audio_quality_browsertest.cc b/chrome/browser/media/chrome_webrtc_audio_quality_browsertest.cc index 73d9328ece..d7a83a5569 100644 --- a/chrome/browser/media/chrome_webrtc_audio_quality_browsertest.cc +++ b/chrome/browser/media/chrome_webrtc_audio_quality_browsertest.cc @@ -47,7 +47,7 @@ static const base::FilePath::CharType kToolsPath[] = static const char kMainWebrtcTestHtmlPage[] = "files/webrtc/webrtc_audio_quality_test.html"; -base::FilePath GetTestDataDir() { +static base::FilePath GetTestDataDir() { base::FilePath source_dir; PathService::Get(chrome::DIR_TEST_DATA, &source_dir); return source_dir; @@ -55,6 +55,9 @@ base::FilePath GetTestDataDir() { // Test we can set up a WebRTC call and play audio through it. // +// You must have the src-internal solution in your .gclient to put the required +// pyauto_private directory into chrome/test/data/. +// // This test will only work on machines that have been configured to record // their own input. // diff --git a/chrome/browser/media/chrome_webrtc_typing_detection_browsertest.cc b/chrome/browser/media/chrome_webrtc_typing_detection_browsertest.cc new file mode 100644 index 0000000000..a6aa242531 --- /dev/null +++ b/chrome/browser/media/chrome_webrtc_typing_detection_browsertest.cc @@ -0,0 +1,171 @@ +// Copyright 2013 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 <ctime> + +#include "base/path_service.h" +#include "base/strings/stringprintf.h" +#include "chrome/browser/media/webrtc_browsertest_base.h" +#include "chrome/browser/media/webrtc_browsertest_common.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_tabstrip.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/test/base/ui_test_utils.h" +#include "chrome/test/ui/ui_test.h" +#include "content/public/test/browser_test_utils.h" +#include "net/test/embedded_test_server/embedded_test_server.h" +#include "testing/perf/perf_test.h" + +static const base::FilePath::CharType kReferenceFile[] = +#if defined (OS_WIN) + FILE_PATH_LITERAL("pyauto_private/webrtc/human-voice-win.wav"); +#else + FILE_PATH_LITERAL("pyauto_private/webrtc/human-voice-linux.wav"); +#endif + +// The javascript will load the reference file relative to its location, +// which is in /webrtc on the web server. Therefore, prepend a '..' traversal. +static const char kReferenceFileRelativeUrl[] = +#if defined (OS_WIN) + "../pyauto_private/webrtc/human-voice-win.wav"; +#else + "../pyauto_private/webrtc/human-voice-linux.wav"; +#endif + +static const char kMainWebrtcTestHtmlPage[] = + "files/webrtc/webrtc_audio_quality_test.html"; + +static base::FilePath GetTestDataDir() { + base::FilePath source_dir; + PathService::Get(chrome::DIR_TEST_DATA, &source_dir); + return source_dir; +} + +// Test that the typing detection feature works. +// You must have the src-internal solution in your .gclient to put the required +// pyauto_private directory into chrome/test/data/. +class WebrtcTypingDetectionBrowserTest : public WebRtcTestBase { + public: + // TODO(phoglund): clean up duplication from audio quality browser test when + // this test is complete and is proven to work. + virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { + PeerConnectionServerRunner::KillAllPeerConnectionServersOnCurrentSystem(); + } + + bool HasAllRequiredResources() { + base::FilePath reference_file = + GetTestDataDir().Append(kReferenceFile); + if (!base::PathExists(reference_file)) { + LOG(ERROR) << "Cannot find the reference file to be used for audio " + << "quality comparison: " << reference_file.value(); + return false; + } + return true; + } + + void AddAudioFile(const std::string& input_file_relative_url, + content::WebContents* tab_contents) { + EXPECT_EQ("ok-added", ExecuteJavascript( + "addAudioFile('" + input_file_relative_url + "')", tab_contents)); + } + + void PlayAudioFile(content::WebContents* tab_contents) { + EXPECT_EQ("ok-playing", ExecuteJavascript("playAudioFile()", tab_contents)); + } + + void MixLocalStreamWithPreviouslyLoadedAudioFile( + content::WebContents* tab_contents) { + EXPECT_EQ("ok-mixed-in", ExecuteJavascript( + "mixLocalStreamWithPreviouslyLoadedAudioFile()", tab_contents)); + } + + // Ensures we didn't get any errors asynchronously (e.g. while no javascript + // call from this test was outstanding). + void AssertNoAsynchronousErrors(content::WebContents* tab_contents) { + EXPECT_EQ("ok-no-errors", + ExecuteJavascript("getAnyTestFailures()", tab_contents)); + } + + void EstablishCall(content::WebContents* from_tab, + content::WebContents* to_tab) { + EXPECT_EQ("ok-negotiating", + ExecuteJavascript("negotiateCall()", from_tab)); + + // Ensure the call gets up on both sides. + EXPECT_TRUE(PollingWaitUntil("getPeerConnectionReadyState()", + "active", from_tab)); + EXPECT_TRUE(PollingWaitUntil("getPeerConnectionReadyState()", + "active", to_tab)); + } + + void HangUp(content::WebContents* from_tab) { + EXPECT_EQ("ok-call-hung-up", ExecuteJavascript("hangUp()", from_tab)); + } + + void WaitUntilHangupVerified(content::WebContents* tab_contents) { + EXPECT_TRUE(PollingWaitUntil("getPeerConnectionReadyState()", + "no-peer-connection", tab_contents)); + } + + PeerConnectionServerRunner peerconnection_server_; +}; + +// TODO(phoglund): enable when fully implemented. +IN_PROC_BROWSER_TEST_F(WebrtcTypingDetectionBrowserTest, + DISABLED_MANUAL_TestTypingDetection) { + // TODO(phoglund): make this use embedded_test_server when that test server + // can handle files > ~400Kb. + ASSERT_TRUE(test_server()->Start()); + ASSERT_TRUE(peerconnection_server_.Start()); + + ui_test_utils::NavigateToURL( + browser(), test_server()->GetURL(kMainWebrtcTestHtmlPage)); + content::WebContents* left_tab = + browser()->tab_strip_model()->GetActiveWebContents(); + + chrome::AddBlankTabAt(browser(), -1, true); + content::WebContents* right_tab = + browser()->tab_strip_model()->GetActiveWebContents(); + ui_test_utils::NavigateToURL( + browser(), test_server()->GetURL(kMainWebrtcTestHtmlPage)); + + ConnectToPeerConnectionServer("peer 1", left_tab); + ConnectToPeerConnectionServer("peer 2", right_tab); + + GetUserMediaWithSpecificConstraintsAndAccept(left_tab, + kAudioOnlyCallConstraints); + EXPECT_EQ("ok-peerconnection-created", + ExecuteJavascript("preparePeerConnection(false, true)", left_tab)); + + AddAudioFile(kReferenceFileRelativeUrl, left_tab); + MixLocalStreamWithPreviouslyLoadedAudioFile(left_tab); + + EstablishCall(left_tab, right_tab); + + // Note: the media flow isn't necessarily established on the connection just + // because the ready state is ok on both sides. We sleep a bit between call + // establishment and playing to avoid cutting of the beginning of the audio + // file. + SleepInJavascript(left_tab, 2000); + + PlayAudioFile(left_tab); + + // TODO(phoglund): simulate key presses, look for changes in typing detection + // state. + SleepInJavascript(left_tab, 10000); + + AssertNoAsynchronousErrors(left_tab); + AssertNoAsynchronousErrors(right_tab); + + HangUp(left_tab); + WaitUntilHangupVerified(left_tab); + WaitUntilHangupVerified(right_tab); + + AssertNoAsynchronousErrors(left_tab); + AssertNoAsynchronousErrors(right_tab); + + ASSERT_TRUE(peerconnection_server_.Stop()); +} diff --git a/chrome/browser/media/encrypted_media_browsertest.cc b/chrome/browser/media/encrypted_media_browsertest.cc index e1e68aa75c..9c2484921d 100644 --- a/chrome/browser/media/encrypted_media_browsertest.cc +++ b/chrome/browser/media/encrypted_media_browsertest.cc @@ -11,6 +11,9 @@ #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/chrome_switches.h" #include "content/public/test/browser_test_utils.h" +#if defined(OS_ANDROID) +#include "base/android/build_info.h" +#endif #include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR. @@ -47,8 +50,8 @@ const char kMP4VideoOnly[] = "video/mp4; codecs=\"avc1.4D4041\""; #endif // defined(USE_PROPRIETARY_CODECS) // EME-specific test results and errors. -const char kEmeGkrException[] = "GENERATE_KEY_REQUEST_EXCEPTION"; const char kEmeKeyError[] = "KEYERROR"; +const char kEmeNotSupportedError[] = "NOTSUPPORTEDERROR"; // The type of video src used to load media. enum SrcType { @@ -56,20 +59,24 @@ enum SrcType { MSE }; -// Tests encrypted media playback with a combination of parameters: -// - char*: Key system name. -// - bool: True to load media using MSE, otherwise use src. -class EncryptedMediaTest : public MediaBrowserTest, - public testing::WithParamInterface<std::tr1::tuple<const char*, SrcType> > { - public: - // Can only be used in parameterized (*_P) tests. - const char* CurrentKeySystem() { - return std::tr1::get<0>(GetParam()); +// MSE is available on all desktop platforms and on Android 4.1 and later. +static bool IsMSESupported() { +#if defined(OS_ANDROID) + if (base::android::BuildInfo::GetInstance()->sdk_int() < 16) { + LOG(INFO) << "MSE is only supported in Android 4.1 and later."; + return false; } +#endif // defined(OS_ANDROID) + return true; +} - // Can only be used in parameterized (*_P) tests. - SrcType CurrentSourceType() { - return std::tr1::get<1>(GetParam()); +// Base class for encrypted media tests. +class EncryptedMediaTestBase : public MediaBrowserTest { + public: + EncryptedMediaTestBase() : is_pepper_cdm_registered_(false) {} + + bool IsExternalClearKey(const char* key_system) { + return (strcmp(key_system, kExternalClearKeyKeySystem) == 0); } #if defined(WIDEVINE_CDM_AVAILABLE) @@ -78,42 +85,17 @@ class EncryptedMediaTest : public MediaBrowserTest, } #endif // defined(WIDEVINE_CDM_AVAILABLE) - void TestSimplePlayback(const char* encrypted_media, const char* media_type) { - RunSimpleEncryptedMediaTest( - encrypted_media, media_type, CurrentKeySystem(), CurrentSourceType()); - } - - void TestFrameSizeChange() { -#if defined(WIDEVINE_CDM_AVAILABLE) - if (IsWidevine(CurrentKeySystem())) { - LOG(INFO) << "FrameSizeChange test cannot run with Widevine."; - return; - } -#endif // defined(WIDEVINE_CDM_AVAILABLE) - RunEncryptedMediaTest("encrypted_frame_size_change.html", - "frame_size_change-av-enc-v.webm", kWebMAudioVideo, - CurrentKeySystem(), CurrentSourceType(), kEnded); - } - - void TestConfigChange() { -#if defined(WIDEVINE_CDM_AVAILABLE) - if (IsWidevine(CurrentKeySystem())) { - LOG(INFO) << "ConfigChange test cannot run with Widevine."; - return; - } -#endif // defined(WIDEVINE_CDM_AVAILABLE) - std::vector<StringPair> query_params; - query_params.push_back(std::make_pair("keysystem", CurrentKeySystem())); - query_params.push_back(std::make_pair("runencrypted", "1")); - RunMediaTestPage("mse_config_change.html", &query_params, kEnded, true); - } - void RunEncryptedMediaTest(const char* html_page, const char* media_file, const char* media_type, const char* key_system, SrcType src_type, const char* expectation) { + if (src_type == MSE && !IsMSESupported()) { + LOG(INFO) << "Skipping test - MSE not supported."; + return; + } + std::vector<StringPair> query_params; query_params.push_back(std::make_pair("mediafile", media_file)); query_params.push_back(std::make_pair("mediatype", media_type)); @@ -165,20 +147,11 @@ class EncryptedMediaTest : public MediaBrowserTest, // We want to fail quickly when a test fails because an error is encountered. virtual void AddWaitForTitles(content::TitleWatcher* title_watcher) OVERRIDE { MediaBrowserTest::AddWaitForTitles(title_watcher); - title_watcher->AlsoWaitForTitle(ASCIIToUTF16(kEmeGkrException)); + title_watcher->AlsoWaitForTitle(ASCIIToUTF16(kEmeNotSupportedError)); title_watcher->AlsoWaitForTitle(ASCIIToUTF16(kEmeKeyError)); } virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { -#if defined(ENABLE_PEPPER_CDMS) - RegisterPepperCdm(command_line, kClearKeyCdmAdapterFileName, - kExternalClearKeyKeySystem); -#if defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT) - RegisterPepperCdm(command_line, kWidevineCdmAdapterFileName, - kWidevineKeySystem); -#endif // defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT) -#endif // defined(ENABLE_PEPPER_CDMS) } - #if defined(OS_ANDROID) command_line->AppendSwitch( switches::kDisableGestureRequirementForMediaPlayback); @@ -191,10 +164,31 @@ class EncryptedMediaTest : public MediaBrowserTest, #endif // defined(OS_CHROMEOS) } + void SetUpCommandLineForKeySystem(const char* key_system, + CommandLine* command_line) { +#if defined(ENABLE_PEPPER_CDMS) + if (IsExternalClearKey(key_system)) { + RegisterPepperCdm(command_line, kClearKeyCdmAdapterFileName, + kExternalClearKeyKeySystem); + } +#if defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT) + else if (IsWidevine(key_system)) { + RegisterPepperCdm(command_line, kWidevineCdmAdapterFileName, + kWidevineKeySystem); + } +#endif // defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT) +#endif // defined(ENABLE_PEPPER_CDMS) + } + + private: #if defined(ENABLE_PEPPER_CDMS) void RegisterPepperCdm(CommandLine* command_line, const std::string& adapter_name, const std::string& key_system) { + DCHECK(!is_pepper_cdm_registered_) + << "RegisterPepperCdm() can only be called once."; + is_pepper_cdm_registered_ = true; + // Append the switch to register the Clear Key CDM Adapter. base::FilePath plugin_dir; EXPECT_TRUE(PathService::Get(base::DIR_MODULE, &plugin_dir)); @@ -224,27 +218,117 @@ class EncryptedMediaTest : public MediaBrowserTest, return ""; } #endif // defined(ENABLE_PEPPER_CDMS) + + bool is_pepper_cdm_registered_; +}; + +#if defined(ENABLE_PEPPER_CDMS) +// Tests encrypted media playback using ExternalClearKey key system. +class ECKEncryptedMediaTest : public EncryptedMediaTestBase { + protected: + virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { + EncryptedMediaTestBase::SetUpCommandLine(command_line); + SetUpCommandLineForKeySystem(kExternalClearKeyKeySystem, command_line); + } +}; + +#if defined(WIDEVINE_CDM_AVAILABLE) +// Tests encrypted media playback using Widevine key system. +class WVEncryptedMediaTest : public EncryptedMediaTestBase { + protected: + virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { + EncryptedMediaTestBase::SetUpCommandLine(command_line); + SetUpCommandLineForKeySystem(kWidevineKeySystem, command_line); + } +}; +#endif // defined(WIDEVINE_CDM_AVAILABLE) +#endif // defined(ENABLE_PEPPER_CDMS) + +// Tests encrypted media playback with a combination of parameters: +// - char*: Key system name. +// - bool: True to load media using MSE, otherwise use src. +// +// Note: Only parameterized (*_P) tests can be used. Non-parameterized (*_F) +// tests will crash at GetParam(). To add non-parameterized tests, use +// EncryptedMediaTestBase or one of its subclasses (e.g. WVEncryptedMediaTest). +class EncryptedMediaTest : public EncryptedMediaTestBase, + public testing::WithParamInterface<std::tr1::tuple<const char*, SrcType> > { + public: + const char* CurrentKeySystem() { + return std::tr1::get<0>(GetParam()); + } + + SrcType CurrentSourceType() { + return std::tr1::get<1>(GetParam()); + } + + void TestSimplePlayback(const char* encrypted_media, const char* media_type) { + RunSimpleEncryptedMediaTest( + encrypted_media, media_type, CurrentKeySystem(), CurrentSourceType()); + } + + void TestFrameSizeChange() { +#if defined(WIDEVINE_CDM_AVAILABLE) + if (IsWidevine(CurrentKeySystem())) { + LOG(INFO) << "FrameSizeChange test cannot run with Widevine."; + return; + } +#endif // defined(WIDEVINE_CDM_AVAILABLE) + RunEncryptedMediaTest("encrypted_frame_size_change.html", + "frame_size_change-av-enc-v.webm", kWebMAudioVideo, + CurrentKeySystem(), CurrentSourceType(), kEnded); + } + + void TestConfigChange() { + if (CurrentSourceType() != MSE || !IsMSESupported()) { + LOG(INFO) << "Skipping test - config change test requires MSE."; + return; + } +#if defined(WIDEVINE_CDM_AVAILABLE) + if (IsWidevine(CurrentKeySystem())) { + LOG(INFO) << "ConfigChange test cannot run with Widevine."; + return; + } +#endif // defined(WIDEVINE_CDM_AVAILABLE) + std::vector<StringPair> query_params; + query_params.push_back(std::make_pair("keysystem", CurrentKeySystem())); + query_params.push_back(std::make_pair("runencrypted", "1")); + RunMediaTestPage("mse_config_change.html", &query_params, kEnded, true); + } + + protected: + virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { + EncryptedMediaTestBase::SetUpCommandLine(command_line); + SetUpCommandLineForKeySystem(CurrentKeySystem(), command_line); + } }; -INSTANTIATE_TEST_CASE_P(ClearKey, EncryptedMediaTest, - ::testing::Combine( - ::testing::Values(kClearKeyKeySystem), ::testing::Values(SRC, MSE))); +using ::testing::Combine; +using ::testing::Values; + +#if !defined(OS_ANDROID) +INSTANTIATE_TEST_CASE_P(SRC_ClearKey, EncryptedMediaTest, + Combine(Values(kClearKeyKeySystem), Values(SRC))); +#endif // !defined(OS_ANDROID) + +INSTANTIATE_TEST_CASE_P(MSE_ClearKey, EncryptedMediaTest, + Combine(Values(kClearKeyKeySystem), Values(MSE))); // External Clear Key is currently only used on platforms that use Pepper CDMs. #if defined(ENABLE_PEPPER_CDMS) -INSTANTIATE_TEST_CASE_P(ExternalClearKey, EncryptedMediaTest, - ::testing::Combine( - ::testing::Values(kExternalClearKeyKeySystem), - ::testing::Values(SRC, MSE))); +INSTANTIATE_TEST_CASE_P(SRC_ExternalClearKey, EncryptedMediaTest, + Combine(Values(kExternalClearKeyKeySystem), Values(SRC))); +INSTANTIATE_TEST_CASE_P(MSE_ExternalClearKey, EncryptedMediaTest, + Combine(Values(kExternalClearKeyKeySystem), Values(MSE))); +#endif // defined(ENABLE_PEPPER_CDMS) #if defined(WIDEVINE_CDM_AVAILABLE) // This test doesn't fully test playback with Widevine. So we only run Widevine -// test with MSE (no SRC) to reduce test time. -INSTANTIATE_TEST_CASE_P(Widevine, EncryptedMediaTest, - ::testing::Combine( - ::testing::Values(kWidevineKeySystem), ::testing::Values(MSE))); +// test with MSE (no SRC) to reduce test time. Also, on Android EME only works +// with MSE and we cannot run this test with SRC. +INSTANTIATE_TEST_CASE_P(MSE_Widevine, EncryptedMediaTest, + Combine(Values(kWidevineKeySystem), Values(MSE))); #endif // defined(WIDEVINE_CDM_AVAILABLE) -#endif // defined(ENABLE_PEPPER_CDMS) IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_AudioOnly_WebM) { TestSimplePlayback("bear-a-enc_a.webm", kWebMAudioOnly); @@ -301,14 +385,19 @@ IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_AudioOnly_MP4) { #if defined(WIDEVINE_CDM_AVAILABLE) // The parent key system cannot be used in generateKeyRequest. -IN_PROC_BROWSER_TEST_F(EncryptedMediaTest, WVParentThrowsException) { - RunEncryptedMediaTest("encrypted_media_player.html", "bear-a-enc_a.webm", - kWebMAudioOnly, "com.widevine", SRC, kEmeGkrException); +IN_PROC_BROWSER_TEST_F(WVEncryptedMediaTest, ParentThrowsException) { + RunEncryptedMediaTest("encrypted_media_player.html", + "bear-a-enc_a.webm", + kWebMAudioOnly, + "com.widevine", + MSE, + kEmeNotSupportedError); } #endif // defined(WIDEVINE_CDM_AVAILABLE) #if defined(ENABLE_PEPPER_CDMS) -IN_PROC_BROWSER_TEST_F(EncryptedMediaTest, ExternalClearKeyInitializeCDMFail) { +IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, + ExternalClearKeyInitializeCDMFail) { RunEncryptedMediaTest("encrypted_media_player.html", "bear-a-enc_a.webm", kWebMAudioOnly, diff --git a/chrome/browser/media/encrypted_media_istypesupported_browsertest.cc b/chrome/browser/media/encrypted_media_istypesupported_browsertest.cc index 647fdaa242..1006a2fb6e 100644 --- a/chrome/browser/media/encrypted_media_istypesupported_browsertest.cc +++ b/chrome/browser/media/encrypted_media_istypesupported_browsertest.cc @@ -112,6 +112,10 @@ class EncryptedMediaIsTypeSupportedTest : public InProcessBrowserTest { avc2_codec_.push_back("avc2"); + avc3_codec_.push_back("avc3"); + + avc3_extended_codec_.push_back("avc3.64001f"); + aac_codec_.push_back("mp4a"); avc1_and_aac_codecs_.push_back("avc1"); @@ -138,6 +142,10 @@ class EncryptedMediaIsTypeSupportedTest : public InProcessBrowserTest { } const CodecVector& avc1_dot_codec() const { return avc1_dot_codec_; } const CodecVector& avc2_codec() const { return avc2_codec_; } + const CodecVector& avc3_codec() const { return avc3_codec_; } + const CodecVector& avc3_extended_codec() const { + return avc3_extended_codec_; + } const CodecVector& aac_codec() const { return aac_codec_; } const CodecVector& avc1_and_aac_codecs() const { return avc1_and_aac_codecs_; @@ -243,6 +251,8 @@ class EncryptedMediaIsTypeSupportedTest : public InProcessBrowserTest { CodecVector avc1_extended_codec_; CodecVector avc1_dot_codec_; CodecVector avc2_codec_; + CodecVector avc3_codec_; + CodecVector avc3_extended_codec_; CodecVector aac_codec_; CodecVector avc1_and_aac_codecs_; CodecVector unknown_codec_; @@ -444,11 +454,15 @@ IN_PROC_BROWSER_TEST_F(EncryptedMediaIsTypeSupportedTest, EXPECT_PROPRIETARY(IsSupportedKeySystemWithMediaMimeType( "video/mp4", avc1_and_aac_codecs(), kPrefixedClearKey)); EXPECT_PROPRIETARY(IsSupportedKeySystemWithMediaMimeType( + "video/mp4", avc3_codec(), kPrefixedClearKey)); + EXPECT_PROPRIETARY(IsSupportedKeySystemWithMediaMimeType( "video/mp4", aac_codec(), kPrefixedClearKey)); // Extended codecs. EXPECT_PROPRIETARY(IsSupportedKeySystemWithMediaMimeType( "video/mp4", avc1_extended_codec(), kPrefixedClearKey)); + EXPECT_PROPRIETARY(IsSupportedKeySystemWithMediaMimeType( + "video/mp4", avc3_extended_codec(), kPrefixedClearKey)); // Invalid codec format, but canPlayType() strips away the period. EXPECT_PROPRIETARY(IsSupportedKeySystemWithMediaMimeType( @@ -475,6 +489,8 @@ IN_PROC_BROWSER_TEST_F(EncryptedMediaIsTypeSupportedTest, "audio/mp4", avc1_codec(), kPrefixedClearKey)); EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( "audio/mp4", avc1_and_aac_codecs(), kPrefixedClearKey)); + EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( + "audio/mp4", avc3_codec(), kPrefixedClearKey)); // Non-MP4 codec. EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( @@ -614,11 +630,15 @@ IN_PROC_BROWSER_TEST_F( EXPECT_ECKPROPRIETARY(IsSupportedKeySystemWithMediaMimeType( "video/mp4", avc1_and_aac_codecs(), kExternalClearKey)); EXPECT_ECKPROPRIETARY(IsSupportedKeySystemWithMediaMimeType( + "video/mp4", avc3_codec(), kExternalClearKey)); + EXPECT_ECKPROPRIETARY(IsSupportedKeySystemWithMediaMimeType( "video/mp4", aac_codec(), kExternalClearKey)); // Extended codecs. EXPECT_ECKPROPRIETARY(IsSupportedKeySystemWithMediaMimeType( "video/mp4", avc1_extended_codec(), kExternalClearKey)); + EXPECT_ECKPROPRIETARY(IsSupportedKeySystemWithMediaMimeType( + "video/mp4", avc3_extended_codec(), kExternalClearKey)); // Invalid codec format, but canPlayType() strips away the period. EXPECT_ECKPROPRIETARY(IsSupportedKeySystemWithMediaMimeType( @@ -645,6 +665,8 @@ IN_PROC_BROWSER_TEST_F( "audio/mp4", avc1_codec(), kExternalClearKey)); EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( "audio/mp4", avc1_and_aac_codecs(), kExternalClearKey)); + EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( + "audio/mp4", avc3_codec(), kExternalClearKey)); // Non-MP4 codec. EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( @@ -792,6 +814,8 @@ IN_PROC_BROWSER_TEST_F(EncryptedMediaIsTypeSupportedWidevineTest, "video/mp4", avc1_codec(), kWidevineAlpha)); EXPECT_WVAVC1AAC(IsSupportedKeySystemWithMediaMimeType( "video/mp4", avc1_and_aac_codecs(), kWidevineAlpha)); + EXPECT_WVAVC1(IsSupportedKeySystemWithMediaMimeType( + "video/mp4", avc3_codec(), kWidevineAlpha)); EXPECT_WVAVC1AAC(IsSupportedKeySystemWithMediaMimeType( "video/mp4", aac_codec(), kWidevineAlpha)); @@ -808,6 +832,8 @@ IN_PROC_BROWSER_TEST_F(EncryptedMediaIsTypeSupportedWidevineTest, // Extended codecs. EXPECT_WVAVC1(IsSupportedKeySystemWithMediaMimeType( "video/mp4", avc1_extended_codec(), kWidevineAlpha)); + EXPECT_WVAVC1(IsSupportedKeySystemWithMediaMimeType( + "video/mp4", avc3_extended_codec(), kWidevineAlpha)); // Invalid codec format, but canPlayType() strips away the period. EXPECT_WVAVC1(IsSupportedKeySystemWithMediaMimeType( @@ -840,6 +866,8 @@ IN_PROC_BROWSER_TEST_F(EncryptedMediaIsTypeSupportedWidevineTest, "audio/mp4", avc1_codec(), kWidevineAlpha)); EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( "audio/mp4", avc1_and_aac_codecs(), kWidevineAlpha)); + EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( + "audio/mp4", avc3_codec(), kWidevineAlpha)); // Non-MP4 codec. EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( diff --git a/chrome/browser/media/webrtc_log_upload_list.cc b/chrome/browser/media/webrtc_log_upload_list.cc index fdaaad072f..15c2963a8a 100644 --- a/chrome/browser/media/webrtc_log_upload_list.cc +++ b/chrome/browser/media/webrtc_log_upload_list.cc @@ -6,19 +6,25 @@ #include "base/files/file_path.h" #include "base/path_service.h" +#include "chrome/browser/profiles/profile.h" #include "chrome/common/chrome_paths.h" +namespace { + +const char* kWebRtcLogListFilename = "webrtc_log_uploads.log"; + +} + // static -const char* WebRtcLogUploadList::kWebRtcLogListFilename = - "webrtc_log_uploads.log"; +WebRtcLogUploadList* WebRtcLogUploadList::Create(Delegate* delegate, + Profile* profile) { + return new WebRtcLogUploadList(delegate, GetFilePathForProfile(profile)); +} // static -WebRtcLogUploadList* WebRtcLogUploadList::Create(Delegate* delegate) { - base::FilePath log_dir_path; - PathService::Get(chrome::DIR_USER_DATA, &log_dir_path); - base::FilePath upload_log_path = - log_dir_path.AppendASCII(kWebRtcLogListFilename); - return new WebRtcLogUploadList(delegate, upload_log_path); +base::FilePath WebRtcLogUploadList::GetFilePathForProfile(Profile* profile) { + base::FilePath log_dir_path = profile->GetPath(); + return log_dir_path.AppendASCII(kWebRtcLogListFilename); } WebRtcLogUploadList::WebRtcLogUploadList(Delegate* delegate, diff --git a/chrome/browser/media/webrtc_log_upload_list.h b/chrome/browser/media/webrtc_log_upload_list.h index b829d8e647..51e2c808cb 100644 --- a/chrome/browser/media/webrtc_log_upload_list.h +++ b/chrome/browser/media/webrtc_log_upload_list.h @@ -7,15 +7,17 @@ #include "chrome/browser/upload_list.h" +class Profile; + // Loads and parses a text file list of uploaded WebRTC logs. class WebRtcLogUploadList : public UploadList { public: - // Creates the WebRTC log upload list with the given callback delegate. - static WebRtcLogUploadList* Create(Delegate* delegate); + // Creates the WebRTC log upload list with the given callback delegate for + // a profile. + static WebRtcLogUploadList* Create(Delegate* delegate, Profile* profile); - // Used in this class when reading the list file and in WebRtcLogUploader when - // writing to the list file. - static const char* kWebRtcLogListFilename; + // Get the file path for the log list file for a profile. + static base::FilePath GetFilePathForProfile(Profile* profile); // Creates a new WebRTC log upload list with the given callback delegate. // |upload_log_path| is the full path to the file to read the list from. diff --git a/chrome/browser/media/webrtc_log_uploader.cc b/chrome/browser/media/webrtc_log_uploader.cc index 740e0ac776..15b2ba15c4 100644 --- a/chrome/browser/media/webrtc_log_uploader.cc +++ b/chrome/browser/media/webrtc_log_uploader.cc @@ -43,22 +43,21 @@ const char kMultipartBoundary[] = WebRtcLogUploader::WebRtcLogUploader() : log_count_(0), - post_data_(NULL) { - base::FilePath log_dir_path; - PathService::Get(chrome::DIR_USER_DATA, &log_dir_path); - upload_list_path_ = - log_dir_path.AppendASCII(WebRtcLogUploadList::kWebRtcLogListFilename); -} + post_data_(NULL) {} WebRtcLogUploader::~WebRtcLogUploader() {} void WebRtcLogUploader::OnURLFetchComplete( const net::URLFetcher* source) { + DCHECK(upload_done_data_.find(source) != upload_done_data_.end()); int response_code = source->GetResponseCode(); std::string report_id; - if (response_code == 200 && source->GetResponseAsString(&report_id)) - AddUploadedLogInfoToUploadListFile(report_id); - DCHECK(upload_done_data_.find(source) != upload_done_data_.end()); + if (response_code == 200 && source->GetResponseAsString(&report_id)) { + AddUploadedLogInfoToUploadListFile( + WebRtcLogUploadList::GetFilePathForProfile( + upload_done_data_[source].profile), + report_id); + } content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, base::Bind(&WebRtcLoggingHandlerHost::UploadLogDone, upload_done_data_[source].host)); @@ -249,11 +248,12 @@ void WebRtcLogUploader::DecreaseLogCount() { } void WebRtcLogUploader::AddUploadedLogInfoToUploadListFile( + const base::FilePath& upload_list_path, const std::string& report_id) { std::string contents; - if (base::PathExists(upload_list_path_)) { - bool read_ok = base::ReadFileToString(upload_list_path_, &contents); + if (base::PathExists(upload_list_path)) { + bool read_ok = base::ReadFileToString(upload_list_path, &contents); DPCHECK(read_ok); // Limit the number of log entries to |kLogListLimitLines| - 1, to make room @@ -277,7 +277,7 @@ void WebRtcLogUploader::AddUploadedLogInfoToUploadListFile( contents += base::DoubleToString(time_now.ToDoubleT()) + "," + report_id + '\n'; - int written = file_util::WriteFile(upload_list_path_, &contents[0], + int written = file_util::WriteFile(upload_list_path, &contents[0], contents.size()); DPCHECK(written == static_cast<int>(contents.size())); } diff --git a/chrome/browser/media/webrtc_log_uploader.h b/chrome/browser/media/webrtc_log_uploader.h index 167ccbbdab..2eca66b43d 100644 --- a/chrome/browser/media/webrtc_log_uploader.h +++ b/chrome/browser/media/webrtc_log_uploader.h @@ -15,6 +15,8 @@ #include "chrome/browser/media/webrtc_logging_handler_host.h" #include "net/url_request/url_fetcher_delegate.h" +class Profile; + namespace base { class SharedMemory; } @@ -26,8 +28,9 @@ class URLRequestContextGetter; typedef struct z_stream_s z_stream; -// Used when uploading is done to inform about that it's done. +// Used when uploading is done to perform post-upload actions. typedef struct { + Profile* profile; WebRtcLoggingHandlerHost::UploadDoneCallback callback; scoped_refptr<WebRtcLoggingHandlerHost> host; } WebRtcLogUploadDoneData; @@ -62,7 +65,7 @@ class WebRtcLogUploader : public net::URLFetcherDelegate { // Notifies that that logging has stopped and that the log should be uploaded. // Decreases log count. May only be called if permission to log has been // granted by calling ApplyForStartLogging() and getting true in return. After - // this function has been called, a new permission must be granted. Call + // this function has been called, a new permission must be granted. Call // either this function or LoggingStoppedDontUpload(). void LoggingStoppedDoUpload( net::URLRequestContextGetter* request_context, @@ -101,14 +104,11 @@ class WebRtcLogUploader : public net::URLFetcherDelegate { // time,id // etc. // where each line represents an uploaded log and "time" is Unix time. - void AddUploadedLogInfoToUploadListFile(const std::string& report_id); - - void SetUploadPathForTesting(const base::FilePath& path) { - upload_list_path_ = path; - } + void AddUploadedLogInfoToUploadListFile( + const base::FilePath& upload_list_path, + const std::string& report_id); int log_count_; - base::FilePath upload_list_path_; // For testing purposes, see OverrideUploadWithBufferForTesting. Only accessed // on the FILE thread. diff --git a/chrome/browser/media/webrtc_log_uploader_unittest.cc b/chrome/browser/media/webrtc_log_uploader_unittest.cc index 69d1b7f86b..e2fb25af91 100644 --- a/chrome/browser/media/webrtc_log_uploader_unittest.cc +++ b/chrome/browser/media/webrtc_log_uploader_unittest.cc @@ -92,22 +92,25 @@ TEST_F(WebRtcLogUploaderTest, AddUploadedLogInfoToUploadListFile) { EXPECT_TRUE(base::DeleteFile(test_list_path_, false)); scoped_ptr<WebRtcLogUploader> webrtc_log_uploader_( new WebRtcLogUploader()); - webrtc_log_uploader_->SetUploadPathForTesting(test_list_path_); - webrtc_log_uploader_->AddUploadedLogInfoToUploadListFile(kTestReportId); - webrtc_log_uploader_->AddUploadedLogInfoToUploadListFile(kTestReportId); + webrtc_log_uploader_->AddUploadedLogInfoToUploadListFile(test_list_path_, + kTestReportId); + webrtc_log_uploader_->AddUploadedLogInfoToUploadListFile(test_list_path_, + kTestReportId); ASSERT_TRUE(VerifyNumberOfLinesAndContentsOfLastLine(2)); const int expected_line_limit = 50; ASSERT_TRUE(AddLinesToTestFile(expected_line_limit - 2)); ASSERT_TRUE(VerifyNumberOfLinesAndContentsOfLastLine(expected_line_limit)); - webrtc_log_uploader_->AddUploadedLogInfoToUploadListFile(kTestReportId); + webrtc_log_uploader_->AddUploadedLogInfoToUploadListFile(test_list_path_, + kTestReportId); ASSERT_TRUE(VerifyNumberOfLinesAndContentsOfLastLine(expected_line_limit)); ASSERT_TRUE(AddLinesToTestFile(10)); ASSERT_TRUE(VerifyNumberOfLinesAndContentsOfLastLine(60)); - webrtc_log_uploader_->AddUploadedLogInfoToUploadListFile(kTestReportId); + webrtc_log_uploader_->AddUploadedLogInfoToUploadListFile(test_list_path_, + kTestReportId); ASSERT_TRUE(VerifyNumberOfLinesAndContentsOfLastLine(expected_line_limit)); } diff --git a/chrome/browser/media/webrtc_logging_handler_host.cc b/chrome/browser/media/webrtc_logging_handler_host.cc index 7f19e9c9be..aed7964856 100644 --- a/chrome/browser/media/webrtc_logging_handler_host.cc +++ b/chrome/browser/media/webrtc_logging_handler_host.cc @@ -16,6 +16,7 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/media/webrtc_log_uploader.h" +#include "chrome/browser/profiles/profile.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/media/webrtc_logging_messages.h" #include "chrome/common/partial_circular_buffer.h" @@ -92,9 +93,11 @@ std::string IPAddressToSensitiveString(const net::IPAddressNumber& address) { } // namespace -WebRtcLoggingHandlerHost::WebRtcLoggingHandlerHost() - : logging_state_(CLOSED), +WebRtcLoggingHandlerHost::WebRtcLoggingHandlerHost(Profile* profile) + : profile_(profile), + logging_state_(CLOSED), upload_log_on_render_close_(false) { + DCHECK(profile_); } WebRtcLoggingHandlerHost::~WebRtcLoggingHandlerHost() {} @@ -350,6 +353,7 @@ void WebRtcLoggingHandlerHost::TriggerUploadLog() { logging_state_ = UPLOADING; WebRtcLogUploadDoneData upload_done_data; + upload_done_data.profile = profile_; upload_done_data.callback = upload_callback_; upload_done_data.host = this; upload_callback_.Reset(); diff --git a/chrome/browser/media/webrtc_logging_handler_host.h b/chrome/browser/media/webrtc_logging_handler_host.h index 9261c5a058..9f4e39aa2f 100644 --- a/chrome/browser/media/webrtc_logging_handler_host.h +++ b/chrome/browser/media/webrtc_logging_handler_host.h @@ -13,6 +13,7 @@ namespace net { class URLRequestContextGetter; } // namespace net +class Profile; class RenderProcessHost; // WebRtcLoggingHandlerHost handles operations regarding the WebRTC logging: @@ -30,7 +31,7 @@ class WebRtcLoggingHandlerHost : public content::BrowserMessageFilter { typedef base::Callback<void(bool, const std::string&, const std::string&)> UploadDoneCallback; - WebRtcLoggingHandlerHost(); + explicit WebRtcLoggingHandlerHost(Profile* profile); // Sets meta data that will be uploaded along with the log and also written // in the beginning of the log. Must be called on the IO thread before calling @@ -111,6 +112,9 @@ class WebRtcLoggingHandlerHost : public content::BrowserMessageFilter { scoped_refptr<net::URLRequestContextGetter> system_request_context_; scoped_ptr<base::SharedMemory> shared_memory_; + // The profile associated with our renderer process. + Profile* profile_; + // These are only accessed on the IO thread, except when in STARTING state. In // this state we are protected since entering any function that alters the // state is not allowed. diff --git a/chrome/browser/media_galleries/fileapi/iapps_finder_impl.cc b/chrome/browser/media_galleries/fileapi/iapps_finder_impl.cc index 5c1b087ffd..e23a4d458f 100644 --- a/chrome/browser/media_galleries/fileapi/iapps_finder_impl.cc +++ b/chrome/browser/media_galleries/fileapi/iapps_finder_impl.cc @@ -22,7 +22,7 @@ void PostResultToUIThread(StorageInfo::Type type, device_id = StorageInfo::MakeDeviceId(type, unique_id); content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, - base::Bind(callback, unique_id)); + base::Bind(callback, device_id)); } } // namespace diff --git a/chrome/browser/media_galleries/fileapi/iapps_finder_impl.h b/chrome/browser/media_galleries/fileapi/iapps_finder_impl.h index 2b62d31227..95a24aa2d1 100644 --- a/chrome/browser/media_galleries/fileapi/iapps_finder_impl.h +++ b/chrome/browser/media_galleries/fileapi/iapps_finder_impl.h @@ -13,6 +13,7 @@ #if defined(OS_MACOSX) +class MacPreferences; #if defined(__OBJC__) @class NSString; #else // __OBJC__ @@ -28,6 +29,11 @@ namespace iapps { extern NSString* const kIPhotoRecentDatabasesKey; extern NSString* const kITunesRecentDatabasePathsKey; +// Set the mac preferences to use for testing. The caller continues to own +// |preferences| and should call this function again with NULL before freeing +// it. +void SetMacPreferencesForTesting(MacPreferences* preferences); + #endif // OS_MACOSX typedef base::Callback<void(const IAppsFinderCallback&)> IAppsFinderTask; diff --git a/chrome/browser/media_galleries/fileapi/iapps_finder_impl_mac.mm b/chrome/browser/media_galleries/fileapi/iapps_finder_impl_mac.mm index 29fecba723..bf0e5afe68 100644 --- a/chrome/browser/media_galleries/fileapi/iapps_finder_impl_mac.mm +++ b/chrome/browser/media_galleries/fileapi/iapps_finder_impl_mac.mm @@ -10,6 +10,7 @@ #import "base/mac/foundation_util.h" #import "base/mac/scoped_nsobject.h" #include "base/time/time.h" +#include "chrome/browser/policy/preferences_mac.h" #include "chrome/browser/storage_monitor/storage_info.h" #include "content/public/browser/browser_thread.h" @@ -23,16 +24,24 @@ namespace { typedef base::Callback<base::FilePath(NSString*)> PListPathExtractor; +static MacPreferences* g_test_mac_preferences = NULL; + void FindMostRecentDatabase( base::scoped_nsobject<NSString> recent_databases_key, const PListPathExtractor& path_extractor, const IAppsFinderCallback& callback) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); + scoped_ptr<MacPreferences> real_preferences; + MacPreferences* prefs = g_test_mac_preferences; + if (!prefs) { + real_preferences.reset(new MacPreferences()); + prefs = real_preferences.get(); + } + CFStringRef iapp_id = CFSTR("com.apple.iApps"); base::scoped_nsobject<NSArray> plist(CFToNSCast(CFCast<CFArrayRef>( - CFPreferencesCopyAppValue(NSToCFCast(recent_databases_key.get()), - iapp_id)))); + prefs->CopyAppValue(NSToCFCast(recent_databases_key.get()), iapp_id)))); if (!plist) { callback.Run(std::string()); return; @@ -81,14 +90,6 @@ base::FilePath ExtractITunesPath(NSString* path_ns) { NSString* const kIPhotoRecentDatabasesKey = @"iPhotoRecentDatabases"; NSString* const kITunesRecentDatabasePathsKey = @"iTunesRecentDatabasePaths"; -void TestFunc( - const PListPathExtractor& path_extractor, - const IAppsFinderCallback& callback) { -} -void TestFunc2( - const IAppsFinderCallback& callback) { -} - void FindIPhotoLibrary(const IAppsFinderCallback& callback) { FindIAppsOnFileThread( StorageInfo::IPHOTO, @@ -107,4 +108,8 @@ void FindITunesLibrary(const IAppsFinderCallback& callback) { callback); } +void SetMacPreferencesForTesting(MacPreferences* preferences) { + g_test_mac_preferences = preferences; +} + } // namespace iapps diff --git a/chrome/browser/media_galleries/fileapi/iphoto_data_provider.cc b/chrome/browser/media_galleries/fileapi/iphoto_data_provider.cc index 922007a516..64b5a43de4 100644 --- a/chrome/browser/media_galleries/fileapi/iphoto_data_provider.cc +++ b/chrome/browser/media_galleries/fileapi/iphoto_data_provider.cc @@ -12,21 +12,38 @@ #include "base/logging.h" #include "base/platform_file.h" #include "base/threading/thread_restrictions.h" -#include "chrome/browser/media_galleries/fileapi/file_path_watcher_util.h" #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h" +#include "chrome/browser/media_galleries/fileapi/safe_iapps_library_parser.h" #include "content/public/browser/browser_thread.h" namespace iphoto { IPhotoDataProvider::IPhotoDataProvider(const base::FilePath& library_path) - : iapps::IAppsDataProvider(library_path) {} + : iapps::IAppsDataProvider(library_path), + weak_factory_(this) {} IPhotoDataProvider::~IPhotoDataProvider() {} void IPhotoDataProvider::DoParseLibrary( const base::FilePath& library_path, const ReadyCallback& ready_callback) { - set_valid(true); - ready_callback.Run(true); + xml_parser_ = new iapps::SafeIAppsLibraryParser; + xml_parser_->ParseIPhotoLibrary( + library_path, + base::Bind(&IPhotoDataProvider::OnLibraryParsed, + weak_factory_.GetWeakPtr(), + ready_callback)); } + +void IPhotoDataProvider::OnLibraryParsed(const ReadyCallback& ready_callback, + bool result, + const parser::Library& library) { + DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread()); + set_valid(result); + if (valid()) { + library_ = library; + } + ready_callback.Run(valid()); +} + } // namespace iphoto diff --git a/chrome/browser/media_galleries/fileapi/iphoto_data_provider.h b/chrome/browser/media_galleries/fileapi/iphoto_data_provider.h index b8fd2d2f47..e1e62c2369 100644 --- a/chrome/browser/media_galleries/fileapi/iphoto_data_provider.h +++ b/chrome/browser/media_galleries/fileapi/iphoto_data_provider.h @@ -15,6 +15,8 @@ #include "base/files/file_path_watcher.h" #include "base/memory/weak_ptr.h" #include "chrome/browser/media_galleries/fileapi/iapps_data_provider.h" +#include "chrome/browser/media_galleries/fileapi/safe_iapps_library_parser.h" +#include "chrome/common/media_galleries/iphoto_library.h" namespace iphoto { @@ -32,6 +34,17 @@ class IPhotoDataProvider : public iapps::IAppsDataProvider { const ReadyCallback& ready_callback) OVERRIDE; private: + void OnLibraryParsed(const ReadyCallback& ready_callback, + bool result, + const parser::Library& library); + + // The parsed and uniquified data. + parser::Library library_; + + scoped_refptr<iapps::SafeIAppsLibraryParser> xml_parser_; + + base::WeakPtrFactory<IPhotoDataProvider> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(IPhotoDataProvider); }; diff --git a/chrome/browser/media_galleries/fileapi/iphoto_file_util.cc b/chrome/browser/media_galleries/fileapi/iphoto_file_util.cc index d2879a8421..47702f9a28 100644 --- a/chrome/browser/media_galleries/fileapi/iphoto_file_util.cc +++ b/chrome/browser/media_galleries/fileapi/iphoto_file_util.cc @@ -15,7 +15,6 @@ #include "chrome/browser/media_galleries/fileapi/media_path_filter.h" #include "chrome/browser/media_galleries/imported_media_gallery_registry.h" #include "content/public/browser/browser_thread.h" -#include "webkit/browser/fileapi/file_system_file_util.h" #include "webkit/browser/fileapi/file_system_operation_context.h" #include "webkit/browser/fileapi/file_system_url.h" #include "webkit/browser/fileapi/native_file_util.h" diff --git a/chrome/browser/media_galleries/fileapi/itunes_data_provider.cc b/chrome/browser/media_galleries/fileapi/itunes_data_provider.cc index 06f63c0aad..56af6d436d 100644 --- a/chrome/browser/media_galleries/fileapi/itunes_data_provider.cc +++ b/chrome/browser/media_galleries/fileapi/itunes_data_provider.cc @@ -187,12 +187,12 @@ ITunesDataProvider::~ITunesDataProvider() {} void ITunesDataProvider::DoParseLibrary( const base::FilePath& library_path, const ReadyCallback& ready_callback) { - xml_parser_ = new SafeITunesLibraryParser( + xml_parser_ = new iapps::SafeIAppsLibraryParser; + xml_parser_->ParseITunesLibrary( library_path, base::Bind(&ITunesDataProvider::OnLibraryParsed, weak_factory_.GetWeakPtr(), ready_callback)); - xml_parser_->Start(); } const base::FilePath& ITunesDataProvider::auto_add_path() const { diff --git a/chrome/browser/media_galleries/fileapi/itunes_data_provider.h b/chrome/browser/media_galleries/fileapi/itunes_data_provider.h index 485a046262..cb4316c148 100644 --- a/chrome/browser/media_galleries/fileapi/itunes_data_provider.h +++ b/chrome/browser/media_galleries/fileapi/itunes_data_provider.h @@ -14,7 +14,7 @@ #include "base/files/file_path.h" #include "base/files/file_path_watcher.h" #include "chrome/browser/media_galleries/fileapi/iapps_data_provider.h" -#include "chrome/browser/media_galleries/fileapi/safe_itunes_library_parser.h" +#include "chrome/browser/media_galleries/fileapi/safe_iapps_library_parser.h" namespace itunes { @@ -79,7 +79,7 @@ class ITunesDataProvider : public iapps::IAppsDataProvider { // The parsed and uniquified data. Library library_; - scoped_refptr<SafeITunesLibraryParser> xml_parser_; + scoped_refptr<iapps::SafeIAppsLibraryParser> xml_parser_; // Hides parent class member, but it is private, and there's no way to get a // WeakPtr<Derived> from a WeakPtr<Base> without using SupportsWeakPtr. diff --git a/chrome/browser/media_galleries/fileapi/itunes_file_util.cc b/chrome/browser/media_galleries/fileapi/itunes_file_util.cc index 1bdbdd6629..4f5e5fa34e 100644 --- a/chrome/browser/media_galleries/fileapi/itunes_file_util.cc +++ b/chrome/browser/media_galleries/fileapi/itunes_file_util.cc @@ -15,7 +15,6 @@ #include "chrome/browser/media_galleries/fileapi/media_path_filter.h" #include "chrome/browser/media_galleries/imported_media_gallery_registry.h" #include "content/public/browser/browser_thread.h" -#include "webkit/browser/fileapi/file_system_file_util.h" #include "webkit/browser/fileapi/file_system_operation_context.h" #include "webkit/browser/fileapi/file_system_url.h" #include "webkit/browser/fileapi/native_file_util.h" diff --git a/chrome/browser/media_galleries/fileapi/itunes_file_util_unittest.cc b/chrome/browser/media_galleries/fileapi/itunes_file_util_unittest.cc index a0995128eb..d15d5f6974 100644 --- a/chrome/browser/media_galleries/fileapi/itunes_file_util_unittest.cc +++ b/chrome/browser/media_galleries/fileapi/itunes_file_util_unittest.cc @@ -28,7 +28,6 @@ #include "webkit/browser/fileapi/file_system_operation_runner.h" #include "webkit/browser/quota/mock_special_storage_policy.h" -using fileapi::FileSystemFileUtil; using fileapi::FileSystemOperationContext; using fileapi::FileSystemOperation; using fileapi::FileSystemURL; diff --git a/chrome/browser/media_galleries/fileapi/media_path_filter.cc b/chrome/browser/media_galleries/fileapi/media_path_filter.cc index 823e9e1a50..922d8d3c41 100644 --- a/chrome/browser/media_galleries/fileapi/media_path_filter.cc +++ b/chrome/browser/media_galleries/fileapi/media_path_filter.cc @@ -51,6 +51,7 @@ const base::FilePath::CharType* const kExtraSupportedExtensions[] = { FILE_PATH_LITERAL("3gpp"), FILE_PATH_LITERAL("avi"), FILE_PATH_LITERAL("flv"), + FILE_PATH_LITERAL("mkv"), FILE_PATH_LITERAL("mov"), FILE_PATH_LITERAL("mpeg"), FILE_PATH_LITERAL("mpeg4"), diff --git a/chrome/browser/media_galleries/fileapi/picasa_data_provider_browsertest.cc b/chrome/browser/media_galleries/fileapi/picasa_data_provider_browsertest.cc index 8e78827d80..a4f6b616c1 100644 --- a/chrome/browser/media_galleries/fileapi/picasa_data_provider_browsertest.cc +++ b/chrome/browser/media_galleries/fileapi/picasa_data_provider_browsertest.cc @@ -552,8 +552,14 @@ class PicasaDataProviderInvalidateInflightAlbumsIndexerTest } }; +// Flaky on Mac. crbug.com/309160. +#if defined(OS_MACOSX) +#define MAYBE_InvalidateInflightAlbumsIndexerTest DISABLED_InvalidateInflightAlbumsIndexerTest +#else +#define MAYBE_InvalidateInflightAlbumsIndexerTest InvalidateInflightAlbumsIndexerTest +#endif IN_PROC_BROWSER_TEST_F(PicasaDataProviderInvalidateInflightAlbumsIndexerTest, - InvalidateInflightAlbumsIndexerTest) { + MAYBE_InvalidateInflightAlbumsIndexerTest) { RunTest(); } diff --git a/chrome/browser/media_galleries/fileapi/picasa_file_util_unittest.cc b/chrome/browser/media_galleries/fileapi/picasa_file_util_unittest.cc index f115170306..21bfe396c8 100644 --- a/chrome/browser/media_galleries/fileapi/picasa_file_util_unittest.cc +++ b/chrome/browser/media_galleries/fileapi/picasa_file_util_unittest.cc @@ -31,14 +31,12 @@ #include "webkit/browser/fileapi/async_file_util.h" #include "webkit/browser/fileapi/external_mount_points.h" #include "webkit/browser/fileapi/file_system_context.h" -#include "webkit/browser/fileapi/file_system_file_util.h" #include "webkit/browser/fileapi/file_system_operation_context.h" #include "webkit/browser/fileapi/file_system_operation_runner.h" #include "webkit/browser/fileapi/isolated_context.h" #include "webkit/browser/quota/mock_special_storage_policy.h" #include "webkit/common/blob/shareable_file_reference.h" -using fileapi::FileSystemFileUtil; using fileapi::FileSystemOperationContext; using fileapi::FileSystemOperation; using fileapi::FileSystemURL; diff --git a/chrome/browser/media_galleries/fileapi/safe_iapps_library_parser.cc b/chrome/browser/media_galleries/fileapi/safe_iapps_library_parser.cc new file mode 100644 index 0000000000..2c30f2dd4f --- /dev/null +++ b/chrome/browser/media_galleries/fileapi/safe_iapps_library_parser.cc @@ -0,0 +1,176 @@ +// Copyright 2013 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/media_galleries/fileapi/safe_iapps_library_parser.h" + +#include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h" +#include "chrome/common/chrome_utility_messages.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/child_process_data.h" +#include "ipc/ipc_platform_file.h" + +using content::BrowserThread; +using content::UtilityProcessHost; + +namespace iapps { + +SafeIAppsLibraryParser::SafeIAppsLibraryParser() + : parser_state_(INITIAL_STATE) {} + +void SafeIAppsLibraryParser::ParseIPhotoLibrary( + const base::FilePath& library_file, + const IPhotoParserCallback& callback) { + library_file_ = library_file; + iphoto_callback_ = callback; + Start(); +} + +void SafeIAppsLibraryParser::ParseITunesLibrary( + const base::FilePath& library_file, + const ITunesParserCallback& callback) { + library_file_ = library_file; + itunes_callback_ = callback; + Start(); +} + +void SafeIAppsLibraryParser::Start() { + DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread()); + + // |library_platform_file_| will be closed on the IO thread once it + // has been handed off to the child process. + library_platform_file_ = base::CreatePlatformFile( + library_file_, + base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ, + NULL, // created + NULL); // error_code + if (library_platform_file_ == base::kInvalidPlatformFileValue) { + VLOG(1) << "Could not open iApps library XML file: " + << library_file_.value(); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&SafeIAppsLibraryParser::OnOpenLibraryFileFailed, this)); + return; + } + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&SafeIAppsLibraryParser::StartProcessOnIOThread, this)); +} + +SafeIAppsLibraryParser::~SafeIAppsLibraryParser() {} + +void SafeIAppsLibraryParser::StartProcessOnIOThread() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_EQ(INITIAL_STATE, parser_state_); + + scoped_refptr<base::MessageLoopProxy> message_loop_proxy = + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); + utility_process_host_ = + UtilityProcessHost::Create(this, message_loop_proxy.get())->AsWeakPtr(); + // Wait for the startup notification before sending the main IPC to the + // utility process, so that we can dup the file handle. + utility_process_host_->Send(new ChromeUtilityMsg_StartupPing); + parser_state_ = PINGED_UTILITY_PROCESS_STATE; +} + +void SafeIAppsLibraryParser::OnUtilityProcessStarted() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (parser_state_ != PINGED_UTILITY_PROCESS_STATE) + return; + + if (utility_process_host_->GetData().handle == base::kNullProcessHandle) { + DLOG(ERROR) << "Child process handle is null"; + OnError(); + return; + } + + if (!itunes_callback_.is_null()) { + utility_process_host_->Send( + new ChromeUtilityMsg_ParseITunesLibraryXmlFile( + IPC::GetFileHandleForProcess( + library_platform_file_, + utility_process_host_->GetData().handle, + true /* close_source_handle */))); + } else if (!iphoto_callback_.is_null()) { +#if defined(OS_MACOSX) + utility_process_host_->Send( + new ChromeUtilityMsg_ParseIPhotoLibraryXmlFile( + IPC::GetFileHandleForProcess( + library_platform_file_, + utility_process_host_->GetData().handle, + true /* close_source_handle */))); +#endif + } + + parser_state_ = STARTED_PARSING_STATE; +} + +#if defined(OS_MACOSX) +void SafeIAppsLibraryParser::OnGotIPhotoLibrary( + bool result, const iphoto::parser::Library& library) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(!iphoto_callback_.is_null()); + + if (parser_state_ != STARTED_PARSING_STATE) + return; + + MediaFileSystemBackend::MediaTaskRunner()->PostTask( + FROM_HERE, + base::Bind(iphoto_callback_, result, library)); + parser_state_ = FINISHED_PARSING_STATE; +} +#endif + +void SafeIAppsLibraryParser::OnGotITunesLibrary( + bool result, const itunes::parser::Library& library) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(!itunes_callback_.is_null()); + + if (parser_state_ != STARTED_PARSING_STATE) + return; + + MediaFileSystemBackend::MediaTaskRunner()->PostTask( + FROM_HERE, + base::Bind(itunes_callback_, result, library)); + parser_state_ = FINISHED_PARSING_STATE; +} + +void SafeIAppsLibraryParser::OnOpenLibraryFileFailed() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + OnError(); +} + +void SafeIAppsLibraryParser::OnProcessCrashed(int exit_code) { + OnError(); +} + +void SafeIAppsLibraryParser::OnError() { + parser_state_ = FINISHED_PARSING_STATE; + if (!itunes_callback_.is_null()) + OnGotITunesLibrary(false /* failed */, itunes::parser::Library()); + +#if defined(OS_MACOSX) + if (!iphoto_callback_.is_null()) + OnGotIPhotoLibrary(false /* failed */, iphoto::parser::Library()); +#endif +} + +bool SafeIAppsLibraryParser::OnMessageReceived( + const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(SafeIAppsLibraryParser, message) + IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, + OnUtilityProcessStarted) +#if defined(OS_MACOSX) + IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GotIPhotoLibrary, + OnGotIPhotoLibrary) +#endif + IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GotITunesLibrary, + OnGotITunesLibrary) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +} // namespace iapps diff --git a/chrome/browser/media_galleries/fileapi/safe_itunes_library_parser.h b/chrome/browser/media_galleries/fileapi/safe_iapps_library_parser.h index f041951074..ea77a30398 100644 --- a/chrome/browser/media_galleries/fileapi/safe_itunes_library_parser.h +++ b/chrome/browser/media_galleries/fileapi/safe_iapps_library_parser.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_MEDIA_GALLERIES_FILEAPI_SAFE_ITUNES_LIBRARY_PARSER_H_ -#define CHROME_BROWSER_MEDIA_GALLERIES_FILEAPI_SAFE_ITUNES_LIBRARY_PARSER_H_ +#ifndef CHROME_BROWSER_MEDIA_GALLERIES_FILEAPI_SAFE_IAPPS_LIBRARY_PARSER_H_ +#define CHROME_BROWSER_MEDIA_GALLERIES_FILEAPI_SAFE_IAPPS_LIBRARY_PARSER_H_ #include <string> @@ -12,6 +12,7 @@ #include "base/files/file_path.h" #include "base/memory/weak_ptr.h" #include "base/platform_file.h" +#include "chrome/common/media_galleries/iphoto_library.h" #include "chrome/common/media_galleries/itunes_library.h" #include "content/public/browser/utility_process_host.h" #include "content/public/browser/utility_process_host_client.h" @@ -20,26 +21,34 @@ namespace IPC { class Message; } -namespace itunes { +namespace iapps { -// SafeITunesLibraryParser parses the given iTunes library XML file safely via -// a utility process. The SafeITunesLibraryParser object is ref-counted and +// SafeIAppsLibraryParser parses the given iTunes library XML file safely via +// a utility process. The SafeIAppsLibraryParser object is ref-counted and // kept alive after Start() is called until the ParserCallback is called. // The ParserCallback is guaranteed to be called eventually either when the // utility process replies or when it dies. -// Since iTunes library XML files can be big, SafeITunesLibraryParser passes +// Since iApps library XML files can be big, SafeIAppsLibraryParser passes // the file handle to the utility process. -// SafeITunesLibraryParser lives on the Media Task Runner unless otherwise +// SafeIAppsLibraryParser lives on the Media Task Runner unless otherwise // noted. -class SafeITunesLibraryParser : public content::UtilityProcessHostClient { +class SafeIAppsLibraryParser : public content::UtilityProcessHostClient { public: - typedef base::Callback<void(bool, const parser::Library&)> ParserCallback; + typedef base::Callback<void(bool, const iphoto::parser::Library&)> + IPhotoParserCallback; + typedef base::Callback<void(bool, const itunes::parser::Library&)> + ITunesParserCallback; - SafeITunesLibraryParser(const base::FilePath& itunes_library_file, - const ParserCallback& callback); + SafeIAppsLibraryParser(); + + // Start the parse of the iPhoto library file. + void ParseIPhotoLibrary(const base::FilePath& library_file, + const IPhotoParserCallback& callback); + + // Start the parse of the iTunes library file. + void ParseITunesLibrary(const base::FilePath& library_file, + const ITunesParserCallback& callback); - // Posts a task to start the XML parsing in the utility process. - void Start(); private: enum ParserState { @@ -50,7 +59,10 @@ class SafeITunesLibraryParser : public content::UtilityProcessHostClient { }; // content::UtilityProcessHostClient is ref-counted. - virtual ~SafeITunesLibraryParser(); + virtual ~SafeIAppsLibraryParser(); + + // Posts a task to start the XML parsing in the utility process. + void Start(); // Launches the utility process. Must run on the IO thread. void StartProcessOnIOThread(); @@ -60,38 +72,50 @@ class SafeITunesLibraryParser : public content::UtilityProcessHostClient { // Runs on the IO thread. void OnUtilityProcessStarted(); - // Notification from the utility process when it finishes parsing the XML. - // Runs on the IO thread. - void OnGotITunesLibrary(bool result, const parser::Library& library); + // Notification from the utility process when it finishes parsing the + // iPhoto XML. Runs on the IO thread. +#if defined(OS_MACOSX) + void OnGotIPhotoLibrary(bool result, const iphoto::parser::Library& library); +#endif + + // Notification from the utility process when it finishes parsing the + // iTunes XML. Runs on the IO thread. + void OnGotITunesLibrary(bool result, const itunes::parser::Library& library); // Sets |parser_state_| in case the library XML file cannot be opened. // Runs on the IO thread. void OnOpenLibraryFileFailed(); + // Communicates an error to the callback given to the constructor. + void OnError(); + // UtilityProcessHostClient implementation. // Runs on the IO thread. virtual void OnProcessCrashed(int exit_code) OVERRIDE; virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; - const base::FilePath itunes_library_file_; + base::FilePath library_file_; // Once we have opened the file, we store the handle so that we can use it // once the utility process has launched. - base::PlatformFile itunes_library_platform_file_; + base::PlatformFile library_platform_file_; // Only accessed on the IO thread. base::WeakPtr<content::UtilityProcessHost> utility_process_host_; // Only accessed on the Media Task Runner. - const ParserCallback callback_; + ITunesParserCallback itunes_callback_; + + // Only accessed on the Media Task Runner. + IPhotoParserCallback iphoto_callback_; // Verifies the messages from the utility process came at the right time. // Initialized on the Media Task Runner, but only accessed on the IO thread. ParserState parser_state_; - DISALLOW_COPY_AND_ASSIGN(SafeITunesLibraryParser); + DISALLOW_COPY_AND_ASSIGN(SafeIAppsLibraryParser); }; -} // namespace itunes +} // namespace iapps -#endif // CHROME_BROWSER_MEDIA_GALLERIES_FILEAPI_SAFE_ITUNES_LIBRARY_PARSER_H_ +#endif // CHROME_BROWSER_MEDIA_GALLERIES_FILEAPI_SAFE_IAPPS_LIBRARY_PARSER_H_ diff --git a/chrome/browser/media_galleries/fileapi/safe_itunes_library_parser.cc b/chrome/browser/media_galleries/fileapi/safe_itunes_library_parser.cc deleted file mode 100644 index 21dd102853..0000000000 --- a/chrome/browser/media_galleries/fileapi/safe_itunes_library_parser.cc +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2013 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/media_galleries/fileapi/safe_itunes_library_parser.h" - -#include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h" -#include "chrome/common/chrome_utility_messages.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/child_process_data.h" -#include "ipc/ipc_platform_file.h" - -using content::BrowserThread; -using content::UtilityProcessHost; - -namespace itunes { - -SafeITunesLibraryParser::SafeITunesLibraryParser( - const base::FilePath& itunes_library_file, - const ParserCallback& callback) - : itunes_library_file_(itunes_library_file), - callback_(callback), - parser_state_(INITIAL_STATE) {} - -void SafeITunesLibraryParser::Start() { - DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread()); - - // |itunes_library_platform_file_| will be closed on the IO thread once it - // has been handed off to the child process. - itunes_library_platform_file_ = base::CreatePlatformFile( - itunes_library_file_, - base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ, - NULL, // created - NULL); // error_code - if (itunes_library_platform_file_ == base::kInvalidPlatformFileValue) { - VLOG(1) << "Could not open iTunes library XML file: " - << itunes_library_file_.value(); - callback_.Run(false /* failed */, parser::Library()); - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&SafeITunesLibraryParser::OnOpenLibraryFileFailed, this)); - return; - } - - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&SafeITunesLibraryParser::StartProcessOnIOThread, this)); -} - -SafeITunesLibraryParser::~SafeITunesLibraryParser() {} - -void SafeITunesLibraryParser::StartProcessOnIOThread() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DCHECK_EQ(INITIAL_STATE, parser_state_); - - scoped_refptr<base::MessageLoopProxy> message_loop_proxy = - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); - utility_process_host_ = - UtilityProcessHost::Create(this, message_loop_proxy.get())->AsWeakPtr(); - // Wait for the startup notification before sending the main IPC to the - // utility process, so that we can dup the file handle. - utility_process_host_->Send(new ChromeUtilityMsg_StartupPing); - parser_state_ = PINGED_UTILITY_PROCESS_STATE; -} - -void SafeITunesLibraryParser::OnUtilityProcessStarted() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - if (parser_state_ != PINGED_UTILITY_PROCESS_STATE) - return; - - if (utility_process_host_->GetData().handle == base::kNullProcessHandle) - DLOG(ERROR) << "Child process handle is null"; - utility_process_host_->Send( - new ChromeUtilityMsg_ParseITunesLibraryXmlFile( - IPC::GetFileHandleForProcess( - itunes_library_platform_file_, - utility_process_host_->GetData().handle, - true /* close_source_handle */))); - parser_state_ = STARTED_PARSING_STATE; -} - -void SafeITunesLibraryParser::OnGotITunesLibrary( - bool result, const parser::Library& library) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - - if (parser_state_ != STARTED_PARSING_STATE) - return; - - MediaFileSystemBackend::MediaTaskRunner()->PostTask( - FROM_HERE, - base::Bind(callback_, result, library)); - parser_state_ = FINISHED_PARSING_STATE; -} - -void SafeITunesLibraryParser::OnOpenLibraryFileFailed() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - parser_state_ = FINISHED_PARSING_STATE; -} - -void SafeITunesLibraryParser::OnProcessCrashed(int exit_code) { - OnGotITunesLibrary(false /* failed */, parser::Library()); -} - -bool SafeITunesLibraryParser::OnMessageReceived( - const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(SafeITunesLibraryParser, message) - IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, - OnUtilityProcessStarted) - IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GotITunesLibrary, - OnGotITunesLibrary) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; -} - -} // namespace itunes diff --git a/chrome/browser/media_galleries/linux/mtp_device_object_enumerator.h b/chrome/browser/media_galleries/linux/mtp_device_object_enumerator.h index ef92b92154..7f8b611f52 100644 --- a/chrome/browser/media_galleries/linux/mtp_device_object_enumerator.h +++ b/chrome/browser/media_galleries/linux/mtp_device_object_enumerator.h @@ -10,21 +10,18 @@ #include "base/files/file_path.h" #include "base/time/time.h" #include "device/media_transfer_protocol/mtp_file_entry.pb.h" -#include "webkit/browser/fileapi/file_system_file_util.h" // Used to enumerate top-level files of an media file system. -class MTPDeviceObjectEnumerator - : public fileapi::FileSystemFileUtil::AbstractFileEnumerator { +class MTPDeviceObjectEnumerator { public: explicit MTPDeviceObjectEnumerator(const std::vector<MtpFileEntry>& entries); - virtual ~MTPDeviceObjectEnumerator(); + ~MTPDeviceObjectEnumerator(); - // AbstractFileEnumerator: - virtual base::FilePath Next() OVERRIDE; - virtual int64 Size() OVERRIDE; - virtual bool IsDirectory() OVERRIDE; - virtual base::Time LastModifiedTime() OVERRIDE; + base::FilePath Next(); + int64 Size(); + bool IsDirectory(); + base::Time LastModifiedTime(); // If the current file entry is valid, returns true and fills in |entry_id| // with the entry identifier else returns false and |entry_id| is not set. diff --git a/chrome/browser/media_galleries/mac/mtp_device_delegate_impl_mac_unittest.mm b/chrome/browser/media_galleries/mac/mtp_device_delegate_impl_mac_unittest.mm index bdab3237be..7dbe116371 100644 --- a/chrome/browser/media_galleries/mac/mtp_device_delegate_impl_mac_unittest.mm +++ b/chrome/browser/media_galleries/mac/mtp_device_delegate_impl_mac_unittest.mm @@ -22,7 +22,6 @@ #include "content/public/browser/browser_thread.h" #include "content/public/test/test_browser_thread.h" #include "testing/gtest/include/gtest/gtest.h" -#include "webkit/browser/fileapi/file_system_file_util.h" #if !defined(MAC_OS_X_VERSION_10_7) || \ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 @@ -161,16 +160,6 @@ const char kTestFileContents[] = "test"; @end -// Advances the enumerator. When the method returns, signals the waiting -// event. -void EnumerateAndSignal( - fileapi::FileSystemFileUtil::AbstractFileEnumerator* enumerator, - base::WaitableEvent* event, - base::FilePath* path) { - *path = enumerator->Next(); - event->Signal(); -} - class MTPDeviceDelegateImplMacTest : public testing::Test { public: MTPDeviceDelegateImplMacTest() : camera_(NULL), delegate_(NULL) {} diff --git a/chrome/browser/media_galleries/media_file_system_context.h b/chrome/browser/media_galleries/media_file_system_context.h index 2a7d145fae..54b9a8e812 100644 --- a/chrome/browser/media_galleries/media_file_system_context.h +++ b/chrome/browser/media_galleries/media_file_system_context.h @@ -31,11 +31,6 @@ class MediaFileSystemContext { // Revoke the passed |fsid|. virtual void RevokeFileSystem(const std::string& fsid) = 0; - - // Signal the registry that a particular MTP device map entry is no longer - // needed. - virtual void RemoveScopedMTPDeviceMapEntry( - const base::FilePath::StringType& device_location) = 0; }; #endif // CHROME_BROWSER_MEDIA_GALLERIES_MEDIA_FILE_SYSTEM_CONTEXT_H_ diff --git a/chrome/browser/media_galleries/media_file_system_registry.cc b/chrome/browser/media_galleries/media_file_system_registry.cc index 7d564af4b9..0eafc4c19c 100644 --- a/chrome/browser/media_galleries/media_file_system_registry.cc +++ b/chrome/browser/media_galleries/media_file_system_registry.cc @@ -23,7 +23,6 @@ #include "chrome/browser/media_galleries/media_galleries_dialog_controller.h" #include "chrome/browser/media_galleries/media_galleries_histograms.h" #include "chrome/browser/media_galleries/media_galleries_preferences_factory.h" -#include "chrome/browser/media_galleries/scoped_mtp_device_map_entry.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/storage_monitor/media_storage_util.h" #include "chrome/browser/storage_monitor/storage_monitor.h" @@ -299,8 +298,6 @@ class ExtensionGalleriesHost private: typedef std::map<MediaGalleryPrefId, MediaFileSystemInfo> PrefIdFsInfoMap; - typedef std::map<MediaGalleryPrefId, scoped_refptr<ScopedMTPDeviceMapEntry> > - MediaDeviceEntryReferencesMap; // Private destructor and friend declaration for ref counted implementation. friend class base::RefCountedThreadSafe<ExtensionGalleriesHost>; @@ -577,7 +574,7 @@ class MediaFileSystemRegistry::MediaFileSystemContextImpl IsolatedContext::GetInstance()->RegisterFileSystemForPath( fileapi::kFileSystemTypeDeviceMedia, path, &fs_name); CHECK(!fsid.empty()); - registry_->GetOrCreateScopedMTPDeviceMapEntry(path.value(), fsid); + registry_->RegisterMTPFileSystem(path.value(), fsid); return fsid; } @@ -592,11 +589,6 @@ class MediaFileSystemRegistry::MediaFileSystemContextImpl registry_->RevokeMTPFileSystem(fsid); } - virtual void RemoveScopedMTPDeviceMapEntry( - const base::FilePath::StringType& device_location) OVERRIDE { - registry_->RemoveScopedMTPDeviceMapEntry(device_location); - } - private: MediaFileSystemRegistry* registry_; @@ -615,7 +607,7 @@ MediaFileSystemRegistry::~MediaFileSystemRegistry() { // and then can remove this. if (StorageMonitor::GetInstance()) StorageMonitor::GetInstance()->RemoveObserver(this); - DCHECK(mtp_device_delegate_map_.empty()); + DCHECK(mtp_device_usage_map_.empty()); } void MediaFileSystemRegistry::OnPermissionRemoved( @@ -669,44 +661,43 @@ void MediaFileSystemRegistry::OnGalleryRemoved( } } -scoped_refptr<ScopedMTPDeviceMapEntry> -MediaFileSystemRegistry::GetOrCreateScopedMTPDeviceMapEntry( +void MediaFileSystemRegistry::RegisterMTPFileSystem( const base::FilePath::StringType& device_location, const std::string& fsid) { - MTPDeviceDelegateMap::iterator delegate_it = - mtp_device_delegate_map_.find(device_location); - if (delegate_it != mtp_device_delegate_map_.end()) - return delegate_it->second; - - scoped_refptr<ScopedMTPDeviceMapEntry> mtp_device_host = - new ScopedMTPDeviceMapEntry(device_location, file_system_context_.get()); - // Note that this initializes the delegate asynchronously, but since - // the delegate will only be used from the IO thread, it is guaranteed - // to be created before use of it expects it to be there. - InitMTPDeviceAsyncDelegate(device_location); - mtp_device_delegate_map_[device_location] = mtp_device_host.get(); - mtp_device_map_[fsid] = mtp_device_host; - return mtp_device_host; -} + MTPDeviceUsageMap::iterator delegate_it = + mtp_device_usage_map_.find(device_location); + if (delegate_it == mtp_device_usage_map_.end()) { + // Note that this initializes the delegate asynchronously, but since + // the delegate will only be used from the IO thread, it is guaranteed + // to be created before use of it expects it to be there. + InitMTPDeviceAsyncDelegate(device_location); + mtp_device_usage_map_[device_location] = 0; + } -void MediaFileSystemRegistry::RevokeMTPFileSystem(const std::string& fsid) { - // If this was an MTP device, remove reference to it. - mtp_device_map_.erase(fsid); + mtp_device_usage_map_[device_location]++; + mtp_device_map_[fsid] = device_location; } -void MediaFileSystemRegistry::RemoveScopedMTPDeviceMapEntry( - const base::FilePath::StringType& device_location) { - content::BrowserThread::PostTask( - content::BrowserThread::IO, - FROM_HERE, - base::Bind(&MTPDeviceMapService::RemoveAsyncDelegate, - base::Unretained(MTPDeviceMapService::GetInstance()), - device_location)); - - MTPDeviceDelegateMap::iterator delegate_it = - mtp_device_delegate_map_.find(device_location); - DCHECK(delegate_it != mtp_device_delegate_map_.end()); - mtp_device_delegate_map_.erase(delegate_it); +// TODO(gbillock): Move all this accounting to the MTPDeviceMapService. +void MediaFileSystemRegistry::RevokeMTPFileSystem(const std::string& fsid) { + MTPDeviceFileSystemMap::iterator i = mtp_device_map_.find(fsid); + if (i != mtp_device_map_.end()) { + base::FilePath::StringType device_location = i->second; + mtp_device_map_.erase(i); + MTPDeviceUsageMap::iterator delegate_it = + mtp_device_usage_map_.find(device_location); + DCHECK(delegate_it != mtp_device_usage_map_.end()); + mtp_device_usage_map_[device_location]--; + if (mtp_device_usage_map_[device_location] == 0) { + mtp_device_usage_map_.erase(delegate_it); + content::BrowserThread::PostTask( + content::BrowserThread::IO, + FROM_HERE, + base::Bind(&MTPDeviceMapService::RemoveAsyncDelegate, + base::Unretained(MTPDeviceMapService::GetInstance()), + device_location)); + } + } } void MediaFileSystemRegistry::OnExtensionGalleriesHostEmpty( diff --git a/chrome/browser/media_galleries/media_file_system_registry.h b/chrome/browser/media_galleries/media_file_system_registry.h index 5b8d804be2..59523ef40b 100644 --- a/chrome/browser/media_galleries/media_file_system_registry.h +++ b/chrome/browser/media_galleries/media_file_system_registry.h @@ -24,8 +24,6 @@ class ExtensionGalleriesHost; class MediaFileSystemContext; class MediaGalleriesPreferences; class Profile; -class ScopedMTPDeviceMapEntry; - namespace content { class RenderViewHost; @@ -65,6 +63,8 @@ struct MediaFileSystemInfo { typedef base::Callback<void(const std::vector<MediaFileSystemInfo>&)> MediaFileSystemsCallback; +// Tracks usage of filesystems by extensions. +// This object lives on the UI thread. class MediaFileSystemRegistry : public RemovableStorageObserver, public MediaGalleriesPreferences::GalleryChangeObserver { @@ -73,15 +73,15 @@ class MediaFileSystemRegistry virtual ~MediaFileSystemRegistry(); // Passes to |callback| the list of media filesystem IDs and paths for a - // given RVH. Called on the UI thread. + // given RVH. void GetMediaFileSystemsForExtension( const content::RenderViewHost* rvh, const extensions::Extension* extension, const MediaFileSystemsCallback& callback); // Returns the media galleries preferences for the specified |profile|. - // Called on the UI thread. Caller is responsible for ensuring that the - // preferences are initialized before use. + // Caller is responsible for ensuring that the preferences are initialized + // before use. MediaGalleriesPreferences* GetPreferences(Profile* profile); // RemovableStorageObserver implementation. @@ -100,15 +100,14 @@ class MediaFileSystemRegistry // Map a profile and extension to the ExtensionGalleriesHost. typedef std::map<Profile*, ExtensionHostMap> ExtensionGalleriesHostMap; - // Map a filesystem id (fsid) to the reference to an MTP device. - typedef std::map<std::string, scoped_refptr<ScopedMTPDeviceMapEntry> > - MTPDeviceEntryMap; + // Map a filesystem id (fsid) to an MTP device location. + typedef std::map<std::string, base::FilePath::StringType> + MTPDeviceFileSystemMap; - // Map a MTP or PTP device location to the raw pointer of - // ScopedMTPDeviceMapEntry. It is safe to store a raw pointer in this - // map. - typedef std::map<const base::FilePath::StringType, ScopedMTPDeviceMapEntry*> - MTPDeviceDelegateMap; + // Map a MTP or PTP device location to a count of current uses of that + // location. + typedef std::map<const base::FilePath::StringType, int> + MTPDeviceUsageMap; virtual void OnPermissionRemoved(MediaGalleriesPreferences* pref, const std::string& extension_id, @@ -116,30 +115,25 @@ class MediaFileSystemRegistry virtual void OnGalleryRemoved(MediaGalleriesPreferences* pref, MediaGalleryPrefId pref_id) OVERRIDE; - // Returns ScopedMTPDeviceMapEntry object for the given |device_location|. - scoped_refptr<ScopedMTPDeviceMapEntry> GetOrCreateScopedMTPDeviceMapEntry( + // Register that an MTP filesystem is in use for the given |device_location|. + void RegisterMTPFileSystem( const base::FilePath::StringType& device_location, const std::string& fsid); + // Removes the MTP entry associated with the given + // |device_location|. Signals the MTPDeviceMapService to destroy the + // delegate if there are no more uses of it. void RevokeMTPFileSystem(const std::string& fsid); void OnExtensionGalleriesHostEmpty(Profile* profile, const std::string& extension_id); - // Removes the ScopedMTPDeviceMapEntry associated with the given - // |device_location|. - void RemoveScopedMTPDeviceMapEntry( - const base::FilePath::StringType& device_location); - - // Only accessed on the UI thread. This map owns all the - // ExtensionGalleriesHost objects created. + // This map owns all the ExtensionGalleriesHost objects created. ExtensionGalleriesHostMap extension_hosts_map_; - // Contains a map of fsid to ScopedMTPDeviceMapEntry. - MTPDeviceEntryMap mtp_device_map_; + MTPDeviceFileSystemMap mtp_device_map_; - // Only accessed on the UI thread. - MTPDeviceDelegateMap mtp_device_delegate_map_; + MTPDeviceUsageMap mtp_device_usage_map_; scoped_ptr<MediaFileSystemContext> file_system_context_; diff --git a/chrome/browser/media_galleries/media_file_system_registry_unittest.cc b/chrome/browser/media_galleries/media_file_system_registry_unittest.cc index 804524bca3..d21fdeb293 100644 --- a/chrome/browser/media_galleries/media_file_system_registry_unittest.cc +++ b/chrome/browser/media_galleries/media_file_system_registry_unittest.cc @@ -29,7 +29,6 @@ #include "chrome/browser/media_galleries/media_file_system_registry.h" #include "chrome/browser/media_galleries/media_galleries_preferences_factory.h" #include "chrome/browser/media_galleries/media_galleries_test_util.h" -#include "chrome/browser/media_galleries/scoped_mtp_device_map_entry.h" #include "chrome/browser/storage_monitor/removable_device_constants.h" #include "chrome/browser/storage_monitor/storage_info.h" #include "chrome/browser/storage_monitor/storage_monitor.h" @@ -84,9 +83,6 @@ class TestMediaFileSystemContext : public MediaFileSystemContext { virtual void RevokeFileSystem(const std::string& fsid) OVERRIDE; - virtual void RemoveScopedMTPDeviceMapEntry( - const base::FilePath::StringType& device_location) OVERRIDE; - base::FilePath GetPathForId(const std::string& fsid) const; MediaFileSystemRegistry* registry() { return registry_; } @@ -137,7 +133,7 @@ std::string TestMediaFileSystemContext::RegisterFileSystemForMTPDevice( const std::string& device_id, const base::FilePath& path) { CHECK(!StorageInfo::IsMassStorageDevice(device_id)); std::string fsid = AddFSEntry(device_id, path); - registry_->GetOrCreateScopedMTPDeviceMapEntry(path.value(), fsid); + registry_->RegisterMTPFileSystem(path.value(), fsid); return fsid; } @@ -148,11 +144,6 @@ void TestMediaFileSystemContext::RevokeFileSystem(const std::string& fsid) { registry_->RevokeMTPFileSystem(fsid); } -void TestMediaFileSystemContext::RemoveScopedMTPDeviceMapEntry( - const base::FilePath::StringType& device_location) { - registry_->RemoveScopedMTPDeviceMapEntry(device_location); -} - base::FilePath TestMediaFileSystemContext::GetPathForId( const std::string& fsid) const { std::map<std::string /*fsid*/, FSInfo>::const_iterator it = diff --git a/chrome/browser/media_galleries/media_galleries_dialog_controller.cc b/chrome/browser/media_galleries/media_galleries_dialog_controller.cc index eb008f2e43..87e1ccbe46 100644 --- a/chrome/browser/media_galleries/media_galleries_dialog_controller.cc +++ b/chrome/browser/media_galleries/media_galleries_dialog_controller.cc @@ -106,8 +106,6 @@ string16 MediaGalleriesDialogController::GetSubtext() const { int id; if (has_read_permission && has_copy_to_permission) id = IDS_MEDIA_GALLERIES_DIALOG_SUBTEXT_READ_WRITE; - else if (has_copy_to_permission) - id = IDS_MEDIA_GALLERIES_DIALOG_SUBTEXT_WRITE_ONLY; else id = IDS_MEDIA_GALLERIES_DIALOG_SUBTEXT_READ_ONLY; diff --git a/chrome/browser/media_galleries/media_galleries_test_util.cc b/chrome/browser/media_galleries/media_galleries_test_util.cc index bc25749d04..8f5a661226 100644 --- a/chrome/browser/media_galleries/media_galleries_test_util.cc +++ b/chrome/browser/media_galleries/media_galleries_test_util.cc @@ -23,10 +23,17 @@ #include "extensions/common/manifest_constants.h" #include "testing/gtest/include/gtest/gtest.h" +#if defined(OS_MACOSX) +#include "base/mac/foundation_util.h" +#include "base/strings/sys_string_conversions.h" +#include "chrome/browser/media_galleries/fileapi/iapps_finder_impl.h" +#include "chrome/browser/policy/preferences_mock_mac.h" +#endif // OS_MACOSX + #if defined(OS_WIN) #include "base/test/test_reg_util_win.h" #include "base/win/registry.h" -#endif +#endif // OS_WIN scoped_refptr<extensions::Extension> AddMediaGalleriesApp( const std::string& name, @@ -102,7 +109,7 @@ void EnsureMediaDirectoriesExists::WriteCustomPicasaAppDataPathToRegistry( KEY_SET_VALUE); key.WriteValue(picasa::kPicasaRegistryAppDataKey, path.value().c_str()); } -#endif +#endif // OS_WIN #if defined(OS_WIN) || defined(OS_MACOSX) base::FilePath @@ -110,7 +117,7 @@ EnsureMediaDirectoriesExists::GetFakePicasaFoldersRootPath() const { DCHECK(fake_dir_.IsValid()); return fake_dir_.path().AppendASCII("picasa_folders"); } -#endif +#endif // OS_WIN || OS_MACOSX void EnsureMediaDirectoriesExists::Init() { #if defined(OS_CHROMEOS) || defined(OS_ANDROID) @@ -120,18 +127,36 @@ void EnsureMediaDirectoriesExists::Init() { ASSERT_TRUE(fake_dir_.CreateUniqueTempDir()); #if defined(OS_WIN) || defined(OS_MACOSX) - // This is to control whether or not tests think iTunes and Picasa are - // installed. + // This is to control whether or not tests think iTunes (on Windows) and + // Picasa are installed. app_data_override_.reset(new base::ScopedPathOverride( base::DIR_APP_DATA, GetFakeAppDataPath())); +#endif // OS_WIN || OS_MACOSX + #if defined(OS_WIN) // Picasa on Windows is by default in the DIR_LOCAL_APP_DATA directory. local_app_data_override_.reset(new base::ScopedPathOverride( base::DIR_LOCAL_APP_DATA, GetFakeLocalAppDataPath())); // Picasa also looks in the registry for an alternate path. registry_override_.OverrideRegistry(HKEY_CURRENT_USER, L"hkcu_picasa"); -#endif -#endif +#endif // OS_WIN + +#if defined(OS_MACOSX) + mac_preferences_.reset(new MockPreferences); + iapps::SetMacPreferencesForTesting(mac_preferences_.get()); + + // iTunes override. + mac_preferences_->AddTestItem( + base::mac::NSToCFCast(iapps::kITunesRecentDatabasePathsKey), + base::SysUTF8ToNSString(fake_dir_.path().AppendASCII("itunes").value()), + false); + + // iPhoto override. + mac_preferences_->AddTestItem( + base::mac::NSToCFCast(iapps::kIPhotoRecentDatabasesKey), + base::SysUTF8ToNSString(fake_dir_.path().AppendASCII("iphoto").value()), + false); +#endif // OS_MACOSX music_override_.reset(new base::ScopedPathOverride( chrome::DIR_USER_MUSIC, fake_dir_.path().AppendASCII("music"))); @@ -140,5 +165,5 @@ void EnsureMediaDirectoriesExists::Init() { video_override_.reset(new base::ScopedPathOverride( chrome::DIR_USER_VIDEOS, fake_dir_.path().AppendASCII("videos"))); num_galleries_ = 3; -#endif +#endif // OS_CHROMEOS || OS_ANDROID } diff --git a/chrome/browser/media_galleries/media_galleries_test_util.h b/chrome/browser/media_galleries/media_galleries_test_util.h index 2e7bee0221..81e7de78c4 100644 --- a/chrome/browser/media_galleries/media_galleries_test_util.h +++ b/chrome/browser/media_galleries/media_galleries_test_util.h @@ -25,6 +25,10 @@ class RegistryOverrideManager; class Profile; +#if defined(OS_MACOSX) +class MockPreferences; +#endif + scoped_refptr<extensions::Extension> AddMediaGalleriesApp( const std::string& name, const std::vector<std::string>& media_galleries_permissions, @@ -62,6 +66,9 @@ class EnsureMediaDirectoriesExists { registry_util::RegistryOverrideManager registry_override_; #endif +#if defined(OS_MACOSX) + scoped_ptr<MockPreferences> mac_preferences_; +#endif DISALLOW_COPY_AND_ASSIGN(EnsureMediaDirectoriesExists); }; diff --git a/chrome/browser/media_galleries/scoped_mtp_device_map_entry.cc b/chrome/browser/media_galleries/scoped_mtp_device_map_entry.cc deleted file mode 100644 index ae2757dab5..0000000000 --- a/chrome/browser/media_galleries/scoped_mtp_device_map_entry.cc +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2012 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/media_galleries/scoped_mtp_device_map_entry.h" - -#include "chrome/browser/media_galleries/media_file_system_context.h" - -ScopedMTPDeviceMapEntry::ScopedMTPDeviceMapEntry( - const base::FilePath::StringType& device_location, - MediaFileSystemContext* context) - : device_location_(device_location), - context_(context) { -} - -ScopedMTPDeviceMapEntry::~ScopedMTPDeviceMapEntry() { - context_->RemoveScopedMTPDeviceMapEntry(device_location_); -} diff --git a/chrome/browser/media_galleries/scoped_mtp_device_map_entry.h b/chrome/browser/media_galleries/scoped_mtp_device_map_entry.h deleted file mode 100644 index 45d12e896f..0000000000 --- a/chrome/browser/media_galleries/scoped_mtp_device_map_entry.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2012 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_MEDIA_GALLERIES_SCOPED_MTP_DEVICE_MAP_ENTRY_H_ -#define CHROME_BROWSER_MEDIA_GALLERIES_SCOPED_MTP_DEVICE_MAP_ENTRY_H_ - -#include "base/files/file_path.h" -#include "base/memory/ref_counted.h" -#include "base/sequenced_task_runner_helpers.h" -#include "content/public/browser/browser_thread.h" - -class MediaFileSystemContext; -class MTPDeviceAsyncDelegate; - -// ScopedMTPDeviceMapEntry manages the reference count on a particular -// MTP device location. These objects are held reference counted in -// the ExtensionGalleriesHost objects. When a particular location is -// destroyed, the MediaFileSystemContext is notified, and the attendant -// delegates are subsequently erased from the system-wide MTPServiceMap. -class ScopedMTPDeviceMapEntry - : public base::RefCountedThreadSafe< - ScopedMTPDeviceMapEntry, content::BrowserThread::DeleteOnUIThread> { - public: - // Created on the UI thread. - ScopedMTPDeviceMapEntry(const base::FilePath::StringType& device_location, - MediaFileSystemContext* context); - - private: - // Friend declarations for ref counted implementation. - friend struct content::BrowserThread::DeleteOnThread< - content::BrowserThread::UI>; - friend class base::DeleteHelper<ScopedMTPDeviceMapEntry>; - - // Private because this class is ref-counted. Destroyed when: - // - no extension is using the device. - // - no extension has permission to access to device. - // - the device is detached. - // - the browser shuts down. - // Destroyed on the UI thread. - ~ScopedMTPDeviceMapEntry(); - - // The MTP or PTP device location. - const base::FilePath::StringType device_location_; - - // Notified when the object is destroyed. - MediaFileSystemContext* context_; - - DISALLOW_COPY_AND_ASSIGN(ScopedMTPDeviceMapEntry); -}; - -#endif // CHROME_BROWSER_MEDIA_GALLERIES_SCOPED_MTP_DEVICE_MAP_ENTRY_H_ diff --git a/chrome/browser/media_galleries/win/mtp_device_delegate_impl_win.cc b/chrome/browser/media_galleries/win/mtp_device_delegate_impl_win.cc index bfa81daf7d..09bf2b03f6 100644 --- a/chrome/browser/media_galleries/win/mtp_device_delegate_impl_win.cc +++ b/chrome/browser/media_galleries/win/mtp_device_delegate_impl_win.cc @@ -98,34 +98,32 @@ string16 GetFileObjectIdFromPathOnBlockingPoolThread( // Returns a pointer to a new instance of AbstractFileEnumerator for the given // |root| directory. Called on a blocking pool thread. -scoped_ptr<fileapi::FileSystemFileUtil::AbstractFileEnumerator> +scoped_ptr<MTPDeviceObjectEnumerator> CreateFileEnumeratorOnBlockingPoolThread( const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info, const base::FilePath& root) { base::ThreadRestrictions::AssertIOAllowed(); DCHECK(!device_info.registered_device_path.empty()); DCHECK(!root.empty()); - scoped_ptr<fileapi::FileSystemFileUtil::AbstractFileEnumerator> - file_enumerator(new fileapi::FileSystemFileUtil::EmptyFileEnumerator()); IPortableDevice* device = PortableDeviceMapService::GetInstance()->GetPortableDevice( device_info.registered_device_path); if (!device) - return file_enumerator.Pass(); + return scoped_ptr<MTPDeviceObjectEnumerator>(); string16 object_id = GetFileObjectIdFromPathOnBlockingPoolThread(device_info, root); if (object_id.empty()) - return file_enumerator.Pass(); + return scoped_ptr<MTPDeviceObjectEnumerator>(); MTPDeviceObjectEntries entries; if (!media_transfer_protocol::GetDirectoryEntries(device, object_id, &entries) || entries.empty()) - return file_enumerator.Pass(); + return scoped_ptr<MTPDeviceObjectEnumerator>(); - file_enumerator.reset(new MTPDeviceObjectEnumerator(entries)); - return file_enumerator.Pass(); + return scoped_ptr<MTPDeviceObjectEnumerator>( + new MTPDeviceObjectEnumerator(entries)); } // Opens the device for communication on a blocking pool thread. @@ -195,8 +193,11 @@ base::PlatformFileError ReadDirectoryOnBlockingPoolThread( return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY; base::FilePath current; - scoped_ptr<fileapi::FileSystemFileUtil::AbstractFileEnumerator> file_enum = + scoped_ptr<MTPDeviceObjectEnumerator> file_enum = CreateFileEnumeratorOnBlockingPoolThread(device_info, root); + if (!file_enum) + return error; + while (!(current = file_enum->Next()).empty()) { fileapi::DirectoryEntry entry; entry.is_directory = file_enum->IsDirectory(); @@ -291,7 +292,6 @@ void DeletePortableDeviceOnBlockingPoolThread( registered_device_path); } - } // namespace // Used by CreateMTPDeviceAsyncDelegate() to create the MTP device diff --git a/chrome/browser/media_galleries/win/mtp_device_object_enumerator.h b/chrome/browser/media_galleries/win/mtp_device_object_enumerator.h index 32a50b52eb..ce2c3e9bbe 100644 --- a/chrome/browser/media_galleries/win/mtp_device_object_enumerator.h +++ b/chrome/browser/media_galleries/win/mtp_device_object_enumerator.h @@ -12,23 +12,20 @@ #include "base/threading/thread_checker.h" #include "base/time/time.h" #include "chrome/browser/media_galleries/win/mtp_device_object_entry.h" -#include "webkit/browser/fileapi/file_system_file_util.h" // MTPDeviceObjectEnumerator is used to enumerate the media transfer protocol // (MTP) device objects from a given object entry list. // MTPDeviceObjectEnumerator supports MTP device file operations. // MTPDeviceObjectEnumerator may only be used on a single thread. -class MTPDeviceObjectEnumerator - : public fileapi::FileSystemFileUtil::AbstractFileEnumerator { +class MTPDeviceObjectEnumerator { public: explicit MTPDeviceObjectEnumerator(const MTPDeviceObjectEntries& entries); - virtual ~MTPDeviceObjectEnumerator(); + ~MTPDeviceObjectEnumerator(); - // AbstractFileEnumerator: - virtual base::FilePath Next() OVERRIDE; - virtual int64 Size() OVERRIDE; - virtual bool IsDirectory() OVERRIDE; - virtual base::Time LastModifiedTime() OVERRIDE; + base::FilePath Next(); + int64 Size(); + bool IsDirectory(); + base::Time LastModifiedTime(); // If the current file object entry is valid, returns an non-empty object id. // Returns an empty string otherwise. diff --git a/chrome/browser/metrics/metrics_log_serializer.cc b/chrome/browser/metrics/metrics_log_serializer.cc index 65021307f1..6e5c1a9458 100644 --- a/chrome/browser/metrics/metrics_log_serializer.cc +++ b/chrome/browser/metrics/metrics_log_serializer.cc @@ -4,6 +4,8 @@ #include "chrome/browser/metrics/metrics_log_serializer.h" +#include <string> + #include "base/base64.h" #include "base/md5.h" #include "base/metrics/histogram.h" @@ -52,8 +54,9 @@ MetricsLogSerializer::MetricsLogSerializer() {} MetricsLogSerializer::~MetricsLogSerializer() {} -void MetricsLogSerializer::SerializeLogs(const std::vector<std::string>& logs, - MetricsLogManager::LogType log_type) { +void MetricsLogSerializer::SerializeLogs( + const std::vector<MetricsLogManager::SerializedLog>& logs, + MetricsLogManager::LogType log_type) { PrefService* local_state = g_browser_process->local_state(); DCHECK(local_state); const char* pref = NULL; @@ -77,8 +80,9 @@ void MetricsLogSerializer::SerializeLogs(const std::vector<std::string>& logs, update.Get()); } -void MetricsLogSerializer::DeserializeLogs(MetricsLogManager::LogType log_type, - std::vector<std::string>* logs) { +void MetricsLogSerializer::DeserializeLogs( + MetricsLogManager::LogType log_type, + std::vector<MetricsLogManager::SerializedLog>* logs) { DCHECK(logs); PrefService* local_state = g_browser_process->local_state(); DCHECK(local_state); @@ -95,7 +99,7 @@ void MetricsLogSerializer::DeserializeLogs(MetricsLogManager::LogType log_type, // static void MetricsLogSerializer::WriteLogsToPrefList( - const std::vector<std::string>& local_list, + const std::vector<MetricsLogManager::SerializedLog>& local_list, size_t list_length_limit, size_t byte_limit, base::ListValue* list) { @@ -112,9 +116,9 @@ void MetricsLogSerializer::WriteLogsToPrefList( if (local_list.size() > list_length_limit) { start = local_list.size(); size_t bytes_used = 0; - for (std::vector<std::string>::const_reverse_iterator + for (std::vector<MetricsLogManager::SerializedLog>::const_reverse_iterator it = local_list.rbegin(); it != local_list.rend(); ++it) { - size_t log_size = it->length(); + size_t log_size = it->log_text().length(); if (bytes_used >= byte_limit && (local_list.size() - start) >= list_length_limit) break; @@ -132,11 +136,12 @@ void MetricsLogSerializer::WriteLogsToPrefList( base::MD5Context ctx; base::MD5Init(&ctx); std::string encoded_log; - for (std::vector<std::string>::const_iterator it = local_list.begin() + start; + for (std::vector<MetricsLogManager::SerializedLog>::const_iterator it = + local_list.begin() + start; it != local_list.end(); ++it) { // We encode the compressed log as Value::CreateStringValue() expects to // take a valid UTF8 string. - if (!base::Base64Encode(*it, &encoded_log)) { + if (!base::Base64Encode(it->log_text(), &encoded_log)) { list->Clear(); return; } @@ -154,7 +159,7 @@ void MetricsLogSerializer::WriteLogsToPrefList( // static MetricsLogSerializer::LogReadStatus MetricsLogSerializer::ReadLogsFromPrefList( const ListValue& list, - std::vector<std::string>* local_list) { + std::vector<MetricsLogManager::SerializedLog>* local_list) { if (list.GetSize() == 0) return MakeRecallStatusHistogram(LIST_EMPTY); if (list.GetSize() < 3) @@ -193,12 +198,14 @@ MetricsLogSerializer::LogReadStatus MetricsLogSerializer::ReadLogsFromPrefList( base::MD5Update(&ctx, encoded_log); - DCHECK_LT(local_index, local_list->size()); - std::string& decoded_log = (*local_list)[local_index]; - if (!base::Base64Decode(encoded_log, &decoded_log)) { + std::string log_text; + if (!base::Base64Decode(encoded_log, &log_text)) { local_list->clear(); return MakeRecallStatusHistogram(DECODE_FAIL); } + + DCHECK_LT(local_index, local_list->size()); + (*local_list)[local_index].SwapLogText(&log_text); } // Verify checksum. diff --git a/chrome/browser/metrics/metrics_log_serializer.h b/chrome/browser/metrics/metrics_log_serializer.h index 853e3e3b9f..ae85b1390f 100644 --- a/chrome/browser/metrics/metrics_log_serializer.h +++ b/chrome/browser/metrics/metrics_log_serializer.h @@ -5,6 +5,8 @@ #ifndef CHROME_BROWSER_METRICS_METRICS_LOG_SERIALIZER_H_ #define CHROME_BROWSER_METRICS_METRICS_LOG_SERIALIZER_H_ +#include <vector> + #include "base/basictypes.h" #include "base/gtest_prod_util.h" #include "chrome/common/metrics/metrics_log_manager.h" @@ -38,10 +40,12 @@ class MetricsLogSerializer : public MetricsLogManager::LogSerializer { virtual ~MetricsLogSerializer(); // Implementation of MetricsLogManager::LogSerializer - virtual void SerializeLogs(const std::vector<std::string>& logs, - MetricsLogManager::LogType log_type) OVERRIDE; - virtual void DeserializeLogs(MetricsLogManager::LogType log_type, - std::vector<std::string>* logs) OVERRIDE; + virtual void SerializeLogs( + const std::vector<MetricsLogManager::SerializedLog>& logs, + MetricsLogManager::LogType log_type) OVERRIDE; + virtual void DeserializeLogs( + MetricsLogManager::LogType log_type, + std::vector<MetricsLogManager::SerializedLog>* logs) OVERRIDE; private: // Encodes the textual log data from |local_list| and writes it to the given @@ -49,16 +53,17 @@ class MetricsLogSerializer : public MetricsLogManager::LogSerializer { // with the most recent, and working backward until at least // |list_length_limit| logs and |byte_limit| bytes of logs have been // stored. At least one of those two arguments must be non-zero. - static void WriteLogsToPrefList(const std::vector<std::string>& local_list, - size_t list_length_limit, - size_t byte_limit, - base::ListValue* list); + static void WriteLogsToPrefList( + const std::vector<MetricsLogManager::SerializedLog>& local_list, + size_t list_length_limit, + size_t byte_limit, + base::ListValue* list); // Decodes and verifies the textual log data from |list|, populating // |local_list| and returning a status code. static LogReadStatus ReadLogsFromPrefList( const base::ListValue& list, - std::vector<std::string>* local_list); + std::vector<MetricsLogManager::SerializedLog>* local_list); FRIEND_TEST_ALL_PREFIXES(MetricsLogSerializerTest, EmptyLogList); FRIEND_TEST_ALL_PREFIXES(MetricsLogSerializerTest, SingleElementLogList); diff --git a/chrome/browser/metrics/metrics_log_serializer_unittest.cc b/chrome/browser/metrics/metrics_log_serializer_unittest.cc index 7e83f36099..ce5c5b2d41 100644 --- a/chrome/browser/metrics/metrics_log_serializer_unittest.cc +++ b/chrome/browser/metrics/metrics_log_serializer_unittest.cc @@ -13,15 +13,18 @@ namespace { const size_t kListLengthLimit = 3; const size_t kLogByteLimit = 1000; -} // namespace +void SetLogText(const std::string& log_text, + MetricsLogManager::SerializedLog* log) { + std::string log_text_copy = log_text; + log->SwapLogText(&log_text_copy); +} -class MetricsLogSerializerTest : public ::testing::Test { -}; +} // namespace // Store and retrieve empty list. TEST(MetricsLogSerializerTest, EmptyLogList) { ListValue list; - std::vector<std::string> local_list; + std::vector<MetricsLogManager::SerializedLog> local_list; MetricsLogSerializer::WriteLogsToPrefList(local_list, kListLengthLimit, kLogByteLimit, &list); @@ -38,8 +41,8 @@ TEST(MetricsLogSerializerTest, EmptyLogList) { TEST(MetricsLogSerializerTest, SingleElementLogList) { ListValue list; - std::vector<std::string> local_list(1); - local_list[0] = "Hello world!"; + std::vector<MetricsLogManager::SerializedLog> local_list(1); + SetLogText("Hello world!", &local_list[0]); MetricsLogSerializer::WriteLogsToPrefList(local_list, kListLengthLimit, kLogByteLimit, &list); @@ -81,20 +84,19 @@ TEST(MetricsLogSerializerTest, LongButTinyLogList) { ListValue list; size_t log_count = kListLengthLimit * 5; - std::vector<std::string> local_list(log_count); - for (size_t i = 0; i < local_list.size(); ++i) { - local_list[0] = "x"; - } + std::vector<MetricsLogManager::SerializedLog> local_list(log_count); + for (size_t i = 0; i < local_list.size(); ++i) + SetLogText("x", &local_list[i]); MetricsLogSerializer::WriteLogsToPrefList(local_list, kListLengthLimit, kLogByteLimit, &list); - std::vector<std::string> result_list; + std::vector<MetricsLogManager::SerializedLog> result_list; EXPECT_EQ( MetricsLogSerializer::RECALL_SUCCESS, MetricsLogSerializer::ReadLogsFromPrefList(list, &result_list)); EXPECT_EQ(local_list.size(), result_list.size()); - EXPECT_TRUE(result_list.front().find("x") == 0); + EXPECT_TRUE(result_list.front().log_text().find("x") == 0); } // Store a set of logs over the length limit, but that doesn't reach the minimum @@ -106,26 +108,28 @@ TEST(MetricsLogSerializerTest, LongButSmallLogList) { // Make log_count logs each slightly larger than // kLogByteLimit / (log_count - 2) // so that the minimum is reached before the oldest (first) two logs. - std::vector<std::string> local_list(log_count); + std::vector<MetricsLogManager::SerializedLog> local_list(log_count); size_t log_size = (kLogByteLimit / (log_count - 2)) + 2; - local_list[0] = "one"; - local_list[1] = "two"; - local_list[2] = "three"; - local_list[log_count - 1] = "last"; + SetLogText("one", &local_list[0]); + SetLogText("two", &local_list[1]); + SetLogText("three", &local_list[2]); + SetLogText("last", &local_list[log_count - 1]); for (size_t i = 0; i < local_list.size(); ++i) { - local_list[i].resize(log_size, ' '); + std::string log_text = local_list[i].log_text(); + log_text.resize(log_size, ' '); + local_list[i].SwapLogText(&log_text); } MetricsLogSerializer::WriteLogsToPrefList(local_list, kListLengthLimit, kLogByteLimit, &list); - std::vector<std::string> result_list; + std::vector<MetricsLogManager::SerializedLog> result_list; EXPECT_EQ( MetricsLogSerializer::RECALL_SUCCESS, MetricsLogSerializer::ReadLogsFromPrefList(list, &result_list)); EXPECT_EQ(local_list.size() - 2, result_list.size()); - EXPECT_TRUE(result_list.front().find("three") == 0); - EXPECT_TRUE(result_list.back().find("last") == 0); + EXPECT_TRUE(result_list.front().log_text().find("three") == 0); + EXPECT_TRUE(result_list.back().log_text().find("last") == 0); } // Store a set of logs within the length limit, but well over the minimum @@ -133,16 +137,18 @@ TEST(MetricsLogSerializerTest, LongButSmallLogList) { TEST(MetricsLogSerializerTest, ShortButLargeLogList) { ListValue list; - std::vector<std::string> local_list(kListLengthLimit); + std::vector<MetricsLogManager::SerializedLog> local_list(kListLengthLimit); // Make the total byte count about twice the minimum. size_t log_size = (kLogByteLimit / local_list.size()) * 2; for (size_t i = 0; i < local_list.size(); ++i) { - local_list[i].resize(log_size, ' '); + std::string log_text = local_list[i].log_text(); + log_text.resize(log_size, ' '); + local_list[i].SwapLogText(&log_text); } MetricsLogSerializer::WriteLogsToPrefList(local_list, kListLengthLimit, kLogByteLimit, &list); - std::vector<std::string> result_list; + std::vector<MetricsLogManager::SerializedLog> result_list; EXPECT_EQ( MetricsLogSerializer::RECALL_SUCCESS, MetricsLogSerializer::ReadLogsFromPrefList(list, &result_list)); @@ -155,31 +161,35 @@ TEST(MetricsLogSerializerTest, LongAndLargeLogList) { ListValue list; // Include twice the max number of logs. - std::vector<std::string> local_list(kListLengthLimit * 2); + std::vector<MetricsLogManager::SerializedLog> + local_list(kListLengthLimit * 2); // Make the total byte count about four times the minimum. size_t log_size = (kLogByteLimit / local_list.size()) * 4; - local_list[local_list.size() - kListLengthLimit] = "First to keep"; + SetLogText("First to keep", + &local_list[local_list.size() - kListLengthLimit]); for (size_t i = 0; i < local_list.size(); ++i) { - local_list[i].resize(log_size, ' '); + std::string log_text = local_list[i].log_text(); + log_text.resize(log_size, ' '); + local_list[i].SwapLogText(&log_text); } MetricsLogSerializer::WriteLogsToPrefList(local_list, kListLengthLimit, kLogByteLimit, &list); - std::vector<std::string> result_list; + std::vector<MetricsLogManager::SerializedLog> result_list; EXPECT_EQ( MetricsLogSerializer::RECALL_SUCCESS, MetricsLogSerializer::ReadLogsFromPrefList(list, &result_list)); // The max length should control the resulting size. EXPECT_EQ(kListLengthLimit, result_list.size()); - EXPECT_TRUE(result_list.front().find("First to keep") == 0); + EXPECT_TRUE(result_list.front().log_text().find("First to keep") == 0); } // Induce LIST_SIZE_TOO_SMALL corruption TEST(MetricsLogSerializerTest, SmallRecoveredListSize) { ListValue list; - std::vector<std::string> local_list(1); - local_list[0] = "Hello world!"; + std::vector<MetricsLogManager::SerializedLog> local_list(1); + SetLogText("Hello world!", &local_list[0]); MetricsLogSerializer::WriteLogsToPrefList(local_list, kListLengthLimit, kLogByteLimit, &list); @@ -199,9 +209,9 @@ TEST(MetricsLogSerializerTest, SmallRecoveredListSize) { TEST(MetricsLogSerializerTest, RemoveSizeFromLogList) { ListValue list; - std::vector<std::string> local_list(2); - local_list[0] = "one"; - local_list[1] = "two"; + std::vector<MetricsLogManager::SerializedLog> local_list(2); + SetLogText("one", &local_list[0]); + SetLogText("two", &local_list[1]); EXPECT_EQ(2U, local_list.size()); MetricsLogSerializer::WriteLogsToPrefList(local_list, kListLengthLimit, kLogByteLimit, &list); @@ -220,8 +230,8 @@ TEST(MetricsLogSerializerTest, RemoveSizeFromLogList) { TEST(MetricsLogSerializerTest, CorruptSizeOfLogList) { ListValue list; - std::vector<std::string> local_list(1); - local_list[0] = "Hello world!"; + std::vector<MetricsLogManager::SerializedLog> local_list(1); + SetLogText("Hello world!", &local_list[0]); MetricsLogSerializer::WriteLogsToPrefList(local_list, kListLengthLimit, kLogByteLimit, &list); @@ -241,8 +251,8 @@ TEST(MetricsLogSerializerTest, CorruptSizeOfLogList) { TEST(MetricsLogSerializerTest, CorruptChecksumOfLogList) { ListValue list; - std::vector<std::string> local_list(1); - local_list[0] = "Hello world!"; + std::vector<MetricsLogManager::SerializedLog> local_list(1); + SetLogText("Hello world!", &local_list[0]); MetricsLogSerializer::WriteLogsToPrefList(local_list, kListLengthLimit, kLogByteLimit, &list); diff --git a/chrome/browser/metrics/metrics_log_unittest.cc b/chrome/browser/metrics/metrics_log_unittest.cc index 2676dbfd95..0f96916325 100644 --- a/chrome/browser/metrics/metrics_log_unittest.cc +++ b/chrome/browser/metrics/metrics_log_unittest.cc @@ -38,7 +38,7 @@ #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/login/fake_user_manager.h" #include "chrome/browser/chromeos/login/user_manager.h" -#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" +#include "chromeos/dbus/fake_dbus_thread_manager.h" #endif // OS_CHROMEOS using base::TimeDelta; @@ -176,10 +176,9 @@ class MetricsLogTest : public testing::Test { virtual void SetUp() OVERRIDE { #if defined(OS_CHROMEOS) - mock_dbus_thread_manager_ = - new chromeos::MockDBusThreadManagerWithoutGMock(); + fake_dbus_thread_manager_ = new chromeos::FakeDBusThreadManager(); chromeos::DBusThreadManager::InitializeForTesting( - mock_dbus_thread_manager_); + fake_dbus_thread_manager_); // Enable multi-profiles. CommandLine::ForCurrentProcess()->AppendSwitch(switches::kMultiProfiles); @@ -208,7 +207,7 @@ class MetricsLogTest : public testing::Test { base::MessageLoop message_loop_; #if defined(OS_CHROMEOS) - chromeos::MockDBusThreadManagerWithoutGMock* mock_dbus_thread_manager_; + chromeos::FakeDBusThreadManager* fake_dbus_thread_manager_; scoped_ptr<base::FieldTrialList> field_trial_list_; #endif // OS_CHROMEOS }; diff --git a/chrome/browser/metrics/metrics_service.cc b/chrome/browser/metrics/metrics_service.cc index 66f68d5aee..98bbf813d3 100644 --- a/chrome/browser/metrics/metrics_service.cc +++ b/chrome/browser/metrics/metrics_service.cc @@ -1463,6 +1463,11 @@ void MetricsService::PrepareFetchWithStagedLog() { current_fetch_->SetUploadData(kMimeType, compressed_log_text); // Tell the server that we're uploading gzipped protobufs. current_fetch_->SetExtraRequestHeaders("content-encoding: gzip"); + const std::string hash = + base::HexEncode(log_manager_.staged_log_hash().data(), + log_manager_.staged_log_hash().size()); + DCHECK(!hash.empty()); + current_fetch_->AddExtraRequestHeader("X-Chrome-UMA-Log-SHA1: " + hash); UMA_HISTOGRAM_PERCENTAGE( "UMA.ProtoCompressionRatio", 100 * compressed_log_text.size() / log_text.size()); diff --git a/chrome/browser/metrics/perf_provider_chromeos.cc b/chrome/browser/metrics/perf_provider_chromeos.cc index f3a1625555..f997cbf289 100644 --- a/chrome/browser/metrics/perf_provider_chromeos.cc +++ b/chrome/browser/metrics/perf_provider_chromeos.cc @@ -46,8 +46,6 @@ const size_t kPerfCommandStartIntervalLowerBoundMinutes = 10; const size_t kPerfCommandStartIntervalUpperBoundMinutes = 20; -const size_t kNumberOfSecondsInAMinute = 60; - // Default time in seconds perf is run for. const size_t kPerfCommandDurationDefaultSeconds = 2; diff --git a/chrome/browser/metrics/thread_watcher_unittest.cc b/chrome/browser/metrics/thread_watcher_unittest.cc index f8cc88e16b..4b799e9c07 100644 --- a/chrome/browser/metrics/thread_watcher_unittest.cc +++ b/chrome/browser/metrics/thread_watcher_unittest.cc @@ -64,8 +64,8 @@ class CustomThreadWatcher : public ThreadWatcher { CheckResponseState check_response_state_; uint64 ping_sent_; uint64 pong_received_; - uint64 success_response_; - uint64 failed_response_; + base::subtle::Atomic32 success_response_; + base::subtle::Atomic32 failed_response_; base::TimeTicks saved_ping_time_; uint64 saved_ping_sequence_number_; @@ -147,10 +147,12 @@ class CustomThreadWatcher : public ThreadWatcher { { base::AutoLock auto_lock(custom_lock_); if (responsive_) { - ++success_response_; + base::subtle::Release_Store(&success_response_, + base::subtle::Acquire_Load(&success_response_) + 1); check_response_state_ = SUCCESSFUL; } else { - ++failed_response_; + base::subtle::Release_Store(&failed_response_, + base::subtle::Acquire_Load(&failed_response_) + 1); check_response_state_ = FAILED; } } @@ -473,8 +475,10 @@ TEST_F(ThreadWatcherTest, ThreadResponding) { // Verify watched thread is responding with ping/pong messaging. io_watcher_->WaitForCheckResponse( kUnresponsiveTime + TimeDelta::FromMinutes(1), SUCCESSFUL); - EXPECT_GT(io_watcher_->success_response_, static_cast<uint64>(0)); - EXPECT_EQ(io_watcher_->failed_response_, static_cast<uint64>(0)); + EXPECT_GT(base::subtle::NoBarrier_Load(&(io_watcher_->success_response_)), + static_cast<base::subtle::Atomic32>(0)); + EXPECT_EQ(base::subtle::NoBarrier_Load(&(io_watcher_->failed_response_)), + static_cast<base::subtle::Atomic32>(0)); // DeActivate thread watching for shutdown. WatchDogThread::PostTask( @@ -508,8 +512,10 @@ TEST_F(ThreadWatcherTest, ThreadNotResponding) { // Verify watched thread is not responding for ping messages. io_watcher_->WaitForCheckResponse( kUnresponsiveTime + TimeDelta::FromMinutes(1), FAILED); - EXPECT_EQ(io_watcher_->success_response_, static_cast<uint64>(0)); - EXPECT_GT(io_watcher_->failed_response_, static_cast<uint64>(0)); + EXPECT_EQ(base::subtle::NoBarrier_Load(&(io_watcher_->success_response_)), + static_cast<base::subtle::Atomic32>(0)); + EXPECT_GT(base::subtle::NoBarrier_Load(&(io_watcher_->failed_response_)), + static_cast<base::subtle::Atomic32>(0)); // DeActivate thread watching for shutdown. WatchDogThread::PostTask( @@ -541,8 +547,10 @@ TEST_F(ThreadWatcherTest, MultipleThreadsResponding) { EXPECT_GT(db_watcher_->ping_sent_, static_cast<uint64>(0)); EXPECT_GT(db_watcher_->pong_received_, static_cast<uint64>(0)); EXPECT_GE(db_watcher_->ping_sequence_number_, static_cast<uint64>(0)); - EXPECT_GT(db_watcher_->success_response_, static_cast<uint64>(0)); - EXPECT_EQ(db_watcher_->failed_response_, static_cast<uint64>(0)); + EXPECT_GT(base::subtle::NoBarrier_Load(&(db_watcher_->success_response_)), + static_cast<base::subtle::Atomic32>(0)); + EXPECT_EQ(base::subtle::NoBarrier_Load(&(db_watcher_->failed_response_)), + static_cast<base::subtle::Atomic32>(0)); // Verify IO thread is responding with ping/pong messaging. io_watcher_->WaitForCheckResponse( @@ -550,8 +558,10 @@ TEST_F(ThreadWatcherTest, MultipleThreadsResponding) { EXPECT_GT(io_watcher_->ping_sent_, static_cast<uint64>(0)); EXPECT_GT(io_watcher_->pong_received_, static_cast<uint64>(0)); EXPECT_GE(io_watcher_->ping_sequence_number_, static_cast<uint64>(0)); - EXPECT_GT(io_watcher_->success_response_, static_cast<uint64>(0)); - EXPECT_EQ(io_watcher_->failed_response_, static_cast<uint64>(0)); + EXPECT_GT(base::subtle::NoBarrier_Load(&(io_watcher_->success_response_)), + static_cast<base::subtle::Atomic32>(0)); + EXPECT_EQ(base::subtle::NoBarrier_Load(&(io_watcher_->failed_response_)), + static_cast<base::subtle::Atomic32>(0)); // DeActivate thread watching for shutdown. WatchDogThread::PostTask( @@ -593,14 +603,18 @@ TEST_F(ThreadWatcherTest, MultipleThreadsNotResponding) { // Verify DB thread is responding with ping/pong messaging. db_watcher_->WaitForCheckResponse( kUnresponsiveTime + TimeDelta::FromMinutes(1), SUCCESSFUL); - EXPECT_GT(db_watcher_->success_response_, static_cast<uint64>(0)); - EXPECT_EQ(db_watcher_->failed_response_, static_cast<uint64>(0)); + EXPECT_GT(base::subtle::NoBarrier_Load(&(db_watcher_->success_response_)), + static_cast<base::subtle::Atomic32>(0)); + EXPECT_EQ(base::subtle::NoBarrier_Load(&(db_watcher_->failed_response_)), + static_cast<base::subtle::Atomic32>(0)); // Verify IO thread is not responding for ping messages. io_watcher_->WaitForCheckResponse( kUnresponsiveTime + TimeDelta::FromMinutes(1), FAILED); - EXPECT_EQ(io_watcher_->success_response_, static_cast<uint64>(0)); - EXPECT_GT(io_watcher_->failed_response_, static_cast<uint64>(0)); + EXPECT_EQ(base::subtle::NoBarrier_Load(&(io_watcher_->success_response_)), + static_cast<base::subtle::Atomic32>(0)); + EXPECT_GT(base::subtle::NoBarrier_Load(&(io_watcher_->failed_response_)), + static_cast<base::subtle::Atomic32>(0)); // DeActivate thread watching for shutdown. WatchDogThread::PostTask( diff --git a/chrome/browser/metro_utils/metro_chrome_win.cc b/chrome/browser/metro_utils/metro_chrome_win.cc index 95623a7c90..2959c2d108 100644 --- a/chrome/browser/metro_utils/metro_chrome_win.cc +++ b/chrome/browser/metro_utils/metro_chrome_win.cc @@ -9,6 +9,7 @@ #include "base/files/file_path.h" #include "base/path_service.h" +#include "base/win/metro.h" #include "base/win/scoped_com_initializer.h" #include "base/win/scoped_comptr.h" #include "chrome/installer/util/browser_distribution.h" @@ -53,4 +54,19 @@ bool ActivateMetroChrome() { return true; } +Win8Environment GetWin8Environment(HostDesktopType desktop) { +#if defined(USE_AURA) && defined(USE_ASH) + if (desktop == chrome::HOST_DESKTOP_TYPE_ASH) + return WIN_8_ENVIRONMENT_METRO_AURA; + else + return WIN_8_ENVIRONMENT_DESKTOP_AURA; +#else + if (base::win::IsProcessImmersive(::GetCurrentProcess())) + return WIN_8_ENVIRONMENT_METRO; + else + return WIN_8_ENVIRONMENT_DESKTOP; +#endif +} + + } // namespace chrome diff --git a/chrome/browser/metro_utils/metro_chrome_win.h b/chrome/browser/metro_utils/metro_chrome_win.h index 79b5782328..dadbb8e068 100644 --- a/chrome/browser/metro_utils/metro_chrome_win.h +++ b/chrome/browser/metro_utils/metro_chrome_win.h @@ -5,6 +5,8 @@ #ifndef CHROME_BROWSER_METRO_UTILS_METRO_CHROME_WIN_H_ #define CHROME_BROWSER_METRO_UTILS_METRO_CHROME_WIN_H_ +#include "chrome/browser/ui/host_desktop.h" + namespace chrome { // Using IApplicationActivationManager::ActivateApplication, activate the @@ -14,6 +16,16 @@ namespace chrome { // PostTask to handle that case. bool ActivateMetroChrome(); +enum Win8Environment { + WIN_8_ENVIRONMENT_METRO, + WIN_8_ENVIRONMENT_DESKTOP, + WIN_8_ENVIRONMENT_METRO_AURA, + WIN_8_ENVIRONMENT_DESKTOP_AURA, + WIN_8_ENVIRONMENT_MAX +}; + +Win8Environment GetWin8Environment(HostDesktopType desktop); + } // namespace chrome #endif // CHROME_BROWSER_METRO_UTILS_METRO_CHROME_WIN_H_ diff --git a/chrome/browser/nacl_host/nacl_browser.cc b/chrome/browser/nacl_host/nacl_browser.cc index 760638359d..b561432c21 100644 --- a/chrome/browser/nacl_host/nacl_browser.cc +++ b/chrome/browser/nacl_host/nacl_browser.cc @@ -176,11 +176,14 @@ void NaClBrowser::SetDelegate(NaClBrowserDelegate* delegate) { } NaClBrowserDelegate* NaClBrowser::GetDelegate() { + // The delegate is not owned by the IO thread. This accessor method can be + // called from other threads. DCHECK(GetInstance()->browser_delegate_.get() != NULL); return GetInstance()->browser_delegate_.get(); } void NaClBrowser::EarlyStartup() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); InitIrtFilePath(); InitValidationCacheFilePath(); } @@ -239,18 +242,21 @@ bool NaClBrowser::IsOk() const { } base::PlatformFile NaClBrowser::IrtFile() const { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); CHECK_EQ(irt_state_, NaClResourceReady); CHECK_NE(irt_platform_file_, base::kInvalidPlatformFileValue); return irt_platform_file_; } void NaClBrowser::EnsureAllResourcesAvailable() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); EnsureIrtAvailable(); EnsureValidationCacheAvailable(); } // Load the IRT async. void NaClBrowser::EnsureIrtAvailable() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); if (IsOk() && irt_state_ == NaClResourceUninitialized) { irt_state_ = NaClResourceRequested; // TODO(ncbray) use blocking pool. @@ -271,6 +277,7 @@ void NaClBrowser::EnsureIrtAvailable() { void NaClBrowser::OnIrtOpened(base::PlatformFileError error_code, base::PassPlatformFile file, bool created) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); DCHECK_EQ(irt_state_, NaClResourceRequested); DCHECK(!created); if (error_code == base::PLATFORM_FILE_OK) { @@ -369,6 +376,7 @@ void NaClBrowser::InitValidationCacheFilePath() { } void NaClBrowser::EnsureValidationCacheAvailable() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); if (IsOk() && validation_cache_state_ == NaClResourceUninitialized) { if (ValidationCacheIsEnabled()) { validation_cache_state_ = NaClResourceRequested; @@ -393,6 +401,7 @@ void NaClBrowser::EnsureValidationCacheAvailable() { } void NaClBrowser::OnValidationCacheLoaded(const std::string *data) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); // Did the cache get cleared before the load completed? If so, ignore the // incoming data. if (validation_cache_state_ == NaClResourceReady) @@ -418,6 +427,7 @@ void NaClBrowser::RunWithoutValidationCache() { } void NaClBrowser::CheckWaiting() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); if (!IsOk() || IsReady()) { // Queue the waiting tasks into the message loop. This helps avoid // re-entrancy problems that could occur if the closure was invoked @@ -448,6 +458,7 @@ const base::FilePath& NaClBrowser::GetIrtFilePath() { void NaClBrowser::PutFilePath(const base::FilePath& path, uint64* file_token_lo, uint64* file_token_hi) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); while (true) { uint64 file_token[2] = {base::RandUint64(), base::RandUint64()}; // A zero file_token indicates there is no file_token, if we get zero, ask @@ -468,6 +479,7 @@ void NaClBrowser::PutFilePath(const base::FilePath& path, uint64* file_token_lo, bool NaClBrowser::GetFilePath(uint64 file_token_lo, uint64 file_token_hi, base::FilePath* path) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); uint64 file_token[2] = {file_token_lo, file_token_hi}; std::string key(reinterpret_cast<char*>(file_token), sizeof(file_token)); PathCacheType::iterator iter = path_cache_.Peek(key); @@ -483,6 +495,7 @@ bool NaClBrowser::GetFilePath(uint64 file_token_lo, uint64 file_token_hi, bool NaClBrowser::QueryKnownToValidate(const std::string& signature, bool off_the_record) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); if (off_the_record) { // If we're off the record, don't reorder the main cache. return validation_cache_.QueryKnownToValidate(signature, false) || @@ -498,6 +511,7 @@ bool NaClBrowser::QueryKnownToValidate(const std::string& signature, void NaClBrowser::SetKnownToValidate(const std::string& signature, bool off_the_record) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); if (off_the_record) { off_the_record_validation_cache_.SetKnownToValidate(signature); } else { @@ -510,6 +524,7 @@ void NaClBrowser::SetKnownToValidate(const std::string& signature, } void NaClBrowser::ClearValidationCache(const base::Closure& callback) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); // Note: this method may be called before EnsureValidationCacheAvailable has // been invoked. In other words, this method may be called before any NaCl // processes have been created. This method must succeed and invoke the @@ -561,6 +576,7 @@ void NaClBrowser::MarkValidationCacheAsModified() { } void NaClBrowser::PersistValidationCache() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); // validation_cache_is_modified_ may be false if the cache was cleared while // this delayed task was pending. // validation_cache_file_path_ may be empty if something went wrong during diff --git a/chrome/browser/nacl_host/nacl_browser_delegate_impl.cc b/chrome/browser/nacl_host/nacl_browser_delegate_impl.cc index eec61e5188..558c53e152 100644 --- a/chrome/browser/nacl_host/nacl_browser_delegate_impl.cc +++ b/chrome/browser/nacl_host/nacl_browser_delegate_impl.cc @@ -5,17 +5,33 @@ #include "chrome/browser/nacl_host/nacl_browser_delegate_impl.h" #include "base/path_service.h" +#include "base/strings/string_util.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/component_updater/pnacl/pnacl_component_installer.h" +#include "chrome/browser/extensions/extension_info_map.h" +#include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/nacl_host/nacl_infobar_delegate.h" #include "chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_paths_internal.h" #include "chrome/common/chrome_version_info.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/manifest_handlers/shared_module_info.h" #include "chrome/common/logging_chrome.h" #include "content/public/browser/browser_thread.h" +#include "extensions/common/constants.h" #include "ppapi/c/private/ppb_nacl_private.h" +using extensions::SharedModuleInfo; + +NaClBrowserDelegateImpl::NaClBrowserDelegateImpl( + ExtensionInfoMap* extension_info_map) + : extension_info_map_(extension_info_map) { +} + +NaClBrowserDelegateImpl::~NaClBrowserDelegateImpl() { +} + void NaClBrowserDelegateImpl::ShowNaClInfobar(int render_process_id, int render_view_id, int error_id) { @@ -68,3 +84,64 @@ void NaClBrowserDelegateImpl::TryInstallPnacl( else installed.Run(false); } + +// This function is security sensitive. Be sure to check with a security +// person before you modify it. +bool NaClBrowserDelegateImpl::MapUrlToLocalFilePath( + const GURL& file_url, bool use_blocking_api, base::FilePath* file_path) { + DCHECK(extension_info_map_); + // Check that the URL is recognized by the extension system. + const extensions::Extension* extension = + extension_info_map_->extensions().GetExtensionOrAppByURL(file_url); + if (!extension) + return false; + + // This is a short-cut which avoids calling a blocking file operation + // (GetFilePath()), so that this can be called on the IO thread. It only + // handles a subset of the urls. + if (!use_blocking_api) { + if (file_url.SchemeIs(extensions::kExtensionScheme)) { + std::string path = file_url.path(); + TrimString(path, "/", &path); // Remove first slash + *file_path = extension->path().AppendASCII(path); + return true; + } + return false; + } + + std::string path = file_url.path(); + extensions::ExtensionResource resource; + + if (SharedModuleInfo::IsImportedPath(path)) { + // Check if this is a valid path that is imported for this extension. + std::string new_extension_id; + std::string new_relative_path; + SharedModuleInfo::ParseImportedPath(path, &new_extension_id, + &new_relative_path); + const extensions::Extension* new_extension = + extension_info_map_->extensions().GetByID(new_extension_id); + if (!new_extension) + return false; + + if (!SharedModuleInfo::ImportsExtensionById(extension, new_extension_id) || + !SharedModuleInfo::IsExportAllowed(new_extension, new_relative_path)) { + return false; + } + + resource = new_extension->GetResource(new_relative_path); + } else { + // Check that the URL references a resource in the extension. + resource = extension->GetResource(path); + } + + if (resource.empty()) + return false; + + // GetFilePath is a blocking function call. + const base::FilePath resource_file_path = resource.GetFilePath(); + if (resource_file_path.empty()) + return false; + + *file_path = resource_file_path; + return true; +} diff --git a/chrome/browser/nacl_host/nacl_browser_delegate_impl.h b/chrome/browser/nacl_host/nacl_browser_delegate_impl.h index 84933e0814..e71fbdd544 100644 --- a/chrome/browser/nacl_host/nacl_browser_delegate_impl.h +++ b/chrome/browser/nacl_host/nacl_browser_delegate_impl.h @@ -6,12 +6,13 @@ #define CHROME_BROWSER_NACL_HOST_NACL_BROWSER_DELEGATE_IMPL_H_ #include "base/compiler_specific.h" +#include "chrome/browser/extensions/extension_info_map.h" #include "components/nacl/common/nacl_browser_delegate.h" class NaClBrowserDelegateImpl : public NaClBrowserDelegate { public: - NaClBrowserDelegateImpl() {} - virtual ~NaClBrowserDelegateImpl() {} + explicit NaClBrowserDelegateImpl(ExtensionInfoMap* extension_info_map); + virtual ~NaClBrowserDelegateImpl(); virtual void ShowNaClInfobar(int render_process_id, int render_view_id, int error_id) OVERRIDE; @@ -25,6 +26,13 @@ class NaClBrowserDelegateImpl : public NaClBrowserDelegate { content::BrowserPpapiHost* ppapi_host) OVERRIDE; virtual void TryInstallPnacl( const base::Callback<void(bool)>& installed) OVERRIDE; + virtual bool MapUrlToLocalFilePath(const GURL& url, + bool is_blocking, + base::FilePath* file_path) OVERRIDE; + + private: + scoped_refptr<ExtensionInfoMap> extension_info_map_; + DISALLOW_COPY_AND_ASSIGN(NaClBrowserDelegateImpl); }; diff --git a/chrome/browser/nacl_host/nacl_file_host.cc b/chrome/browser/nacl_host/nacl_file_host.cc index 1acdf429df..e400df710a 100644 --- a/chrome/browser/nacl_host/nacl_file_host.cc +++ b/chrome/browser/nacl_host/nacl_file_host.cc @@ -11,10 +11,8 @@ #include "base/platform_file.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/sequenced_worker_pool.h" -#include "chrome/browser/extensions/extension_info_map.h" #include "chrome/browser/nacl_host/nacl_browser.h" #include "chrome/browser/nacl_host/nacl_host_message_filter.h" -#include "chrome/common/extensions/manifest_handlers/shared_module_info.h" #include "components/nacl/common/nacl_browser_delegate.h" #include "components/nacl/common/nacl_host_messages.h" #include "components/nacl/common/pnacl_types.h" @@ -24,7 +22,6 @@ #include "ipc/ipc_platform_file.h" using content::BrowserThread; -using extensions::SharedModuleInfo; namespace { @@ -152,67 +149,18 @@ void DoRegisterOpenedNaClExecutableFile( nacl_host_message_filter->Send(reply_msg); } -// Convert the file URL into a file path in the extension directory. -// This function is security sensitive. Be sure to check with a security -// person before you modify it. -bool GetExtensionFilePath( - scoped_refptr<ExtensionInfoMap> extension_info_map, - const GURL& file_url, - base::FilePath* file_path) { - // Check that the URL is recognized by the extension system. - const extensions::Extension* extension = - extension_info_map->extensions().GetExtensionOrAppByURL(file_url); - if (!extension) - return false; - - std::string path = file_url.path(); - extensions::ExtensionResource resource; - - if (SharedModuleInfo::IsImportedPath(path)) { - // Check if this is a valid path that is imported for this extension. - std::string new_extension_id; - std::string new_relative_path; - SharedModuleInfo::ParseImportedPath(path, &new_extension_id, - &new_relative_path); - const extensions::Extension* new_extension = - extension_info_map->extensions().GetByID(new_extension_id); - if (!new_extension) - return false; - - if (!SharedModuleInfo::ImportsExtensionById(extension, new_extension_id) || - !SharedModuleInfo::IsExportAllowed(new_extension, new_relative_path)) { - return false; - } - - resource = new_extension->GetResource(new_relative_path); - } else { - // Check that the URL references a resource in the extension. - resource = extension->GetResource(path); - } - - if (resource.empty()) - return false; - - const base::FilePath resource_file_path = resource.GetFilePath(); - if (resource_file_path.empty()) - return false; - - *file_path = resource_file_path; - return true; -} - // Convert the file URL into a file descriptor. // This function is security sensitive. Be sure to check with a security // person before you modify it. void DoOpenNaClExecutableOnThreadPool( scoped_refptr<NaClHostMessageFilter> nacl_host_message_filter, - scoped_refptr<ExtensionInfoMap> extension_info_map, const GURL& file_url, IPC::Message* reply_msg) { DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); base::FilePath file_path; - if (!GetExtensionFilePath(extension_info_map, file_url, &file_path)) { + if (!NaClBrowser::GetDelegate()->MapUrlToLocalFilePath( + file_url, true /* use_blocking_api */, &file_path)) { NotifyRendererOfError(nacl_host_message_filter.get(), reply_msg); return; } @@ -299,7 +247,6 @@ bool PnaclCanOpenFile(const std::string& filename, void OpenNaClExecutable( scoped_refptr<NaClHostMessageFilter> nacl_host_message_filter, - scoped_refptr<ExtensionInfoMap> extension_info_map, int render_view_id, const GURL& file_url, IPC::Message* reply_msg) { @@ -309,7 +256,6 @@ void OpenNaClExecutable( base::Bind( &OpenNaClExecutable, nacl_host_message_filter, - extension_info_map, render_view_id, file_url, reply_msg)); return; } @@ -339,7 +285,6 @@ void OpenNaClExecutable( base::Bind( &DoOpenNaClExecutableOnThreadPool, nacl_host_message_filter, - extension_info_map, file_url, reply_msg))) { NotifyRendererOfError(nacl_host_message_filter.get(), reply_msg); } diff --git a/chrome/browser/nacl_host/nacl_file_host.h b/chrome/browser/nacl_host/nacl_file_host.h index b6e04fba6e..baf2d5a6af 100644 --- a/chrome/browser/nacl_host/nacl_file_host.h +++ b/chrome/browser/nacl_host/nacl_file_host.h @@ -10,7 +10,6 @@ #include "base/callback_forward.h" #include "base/memory/ref_counted.h" -class ExtensionInfoMap; class GURL; class NaClHostMessageFilter; @@ -56,7 +55,6 @@ bool PnaclCanOpenFile(const std::string& filename, // Opens a NaCl executable file for reading and executing. void OpenNaClExecutable( scoped_refptr<NaClHostMessageFilter> nacl_host_message_filter, - scoped_refptr<ExtensionInfoMap> extension_info_map, int render_view_id, const GURL& file_url, IPC::Message* reply_msg); diff --git a/chrome/browser/nacl_host/nacl_file_host_unittest.cc b/chrome/browser/nacl_host/nacl_file_host_unittest.cc index 3bf9e9aa0f..8d74850703 100644 --- a/chrome/browser/nacl_host/nacl_file_host_unittest.cc +++ b/chrome/browser/nacl_host/nacl_file_host_unittest.cc @@ -60,6 +60,12 @@ class TestNaClBrowserDelegate : public NaClBrowserDelegate { return NULL; } + virtual bool MapUrlToLocalFilePath(const GURL& file_url, + bool use_blocking_api, + base::FilePath* file_path) OVERRIDE { + return false; + } + virtual void TryInstallPnacl( const base::Callback<void(bool)>& installed) OVERRIDE { installed.Run(should_pnacl_install_succeed_); diff --git a/chrome/browser/nacl_host/nacl_host_message_filter.cc b/chrome/browser/nacl_host/nacl_host_message_filter.cc index 7ad5ac6be6..a8d6cfbefd 100644 --- a/chrome/browser/nacl_host/nacl_host_message_filter.cc +++ b/chrome/browser/nacl_host/nacl_host_message_filter.cc @@ -4,42 +4,25 @@ #include "chrome/browser/nacl_host/nacl_host_message_filter.h" -#include "chrome/browser/extensions/extension_info_map.h" #include "chrome/browser/nacl_host/nacl_browser.h" #include "chrome/browser/nacl_host/nacl_file_host.h" #include "chrome/browser/nacl_host/nacl_process_host.h" #include "chrome/browser/nacl_host/pnacl_host.h" #include "components/nacl/common/nacl_host_messages.h" -#include "extensions/common/constants.h" #include "ipc/ipc_platform_file.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" - -static base::FilePath GetManifestPath( - ExtensionInfoMap* extension_info_map, const std::string& manifest) { - GURL manifest_url(manifest); - const extensions::Extension* extension = extension_info_map->extensions() - .GetExtensionOrAppByURL(manifest_url); - if (extension != NULL && - manifest_url.SchemeIs(extensions::kExtensionScheme)) { - std::string path = manifest_url.path(); - TrimString(path, "/", &path); // Remove first slash - return extension->path().AppendASCII(path); - } - return base::FilePath(); -} +#include "url/gurl.h" NaClHostMessageFilter::NaClHostMessageFilter( int render_process_id, bool is_off_the_record, const base::FilePath& profile_directory, - ExtensionInfoMap* extension_info_map, net::URLRequestContextGetter* request_context) : render_process_id_(render_process_id), off_the_record_(is_off_the_record), profile_directory_(profile_directory), request_context_(request_context), - extension_info_map_(extension_info_map), weak_ptr_factory_(this) { } @@ -94,9 +77,14 @@ void NaClHostMessageFilter::OnLaunchNaCl( launch_params.enable_crash_throttling, off_the_record_, profile_directory_); - base::FilePath manifest_url = - GetManifestPath(extension_info_map_.get(), launch_params.manifest_url); - host->Launch(this, reply_msg, manifest_url); + GURL manifest_url(launch_params.manifest_url); + base::FilePath manifest_path; + // We're calling MapUrlToLocalFilePath with the non-blocking API + // because we're running in the I/O thread. Ideally we'd use the other path, + // which would cover more cases. + NaClBrowser::GetDelegate()->MapUrlToLocalFilePath( + manifest_url, false /* use_blocking_api */, &manifest_path); + host->Launch(this, reply_msg, manifest_path); } void NaClHostMessageFilter::ReplyEnsurePnaclInstalled( @@ -202,7 +190,7 @@ void NaClHostMessageFilter::OnNaClErrorStatus(int render_view_id, void NaClHostMessageFilter::OnOpenNaClExecutable(int render_view_id, const GURL& file_url, IPC::Message* reply_msg) { - nacl_file_host::OpenNaClExecutable(this, extension_info_map_, - render_view_id, file_url, reply_msg); + nacl_file_host::OpenNaClExecutable(this, render_view_id, file_url, + reply_msg); } #endif diff --git a/chrome/browser/nacl_host/nacl_host_message_filter.h b/chrome/browser/nacl_host/nacl_host_message_filter.h index 4c05f22b82..f4493fc132 100644 --- a/chrome/browser/nacl_host/nacl_host_message_filter.h +++ b/chrome/browser/nacl_host/nacl_host_message_filter.h @@ -10,7 +10,6 @@ #include "base/platform_file.h" #include "content/public/browser/browser_message_filter.h" -class ExtensionInfoMap; class GURL; namespace nacl { @@ -31,7 +30,6 @@ class NaClHostMessageFilter : public content::BrowserMessageFilter { NaClHostMessageFilter(int render_process_id, bool is_off_the_record, const base::FilePath& profile_directory, - ExtensionInfoMap* extension_info_map, net::URLRequestContextGetter* request_context); // content::BrowserMessageFilter methods: @@ -82,7 +80,6 @@ class NaClHostMessageFilter : public content::BrowserMessageFilter { bool off_the_record_; base::FilePath profile_directory_; scoped_refptr<net::URLRequestContextGetter> request_context_; - scoped_refptr<ExtensionInfoMap> extension_info_map_; base::WeakPtrFactory<NaClHostMessageFilter> weak_ptr_factory_; diff --git a/chrome/browser/nacl_host/nacl_process_host.cc b/chrome/browser/nacl_host/nacl_process_host.cc index 157a8d2f7a..68e5fd7da8 100644 --- a/chrome/browser/nacl_host/nacl_process_host.cc +++ b/chrome/browser/nacl_host/nacl_process_host.cc @@ -28,7 +28,6 @@ #include "chrome/browser/nacl_host/nacl_browser.h" #include "chrome/browser/nacl_host/nacl_host_message_filter.h" #include "chrome/common/chrome_switches.h" -#include "components/nacl/common/nacl_browser_delegate.h" #include "components/nacl/common/nacl_cmd_line.h" #include "components/nacl/common/nacl_host_messages.h" #include "components/nacl/common/nacl_messages.h" @@ -313,8 +312,7 @@ void NaClProcessHost::OnProcessCrashed(int exit_status) { // This is called at browser startup. // static -void NaClProcessHost::EarlyStartup(NaClBrowserDelegate* delegate) { - NaClBrowser::SetDelegate(delegate); +void NaClProcessHost::EarlyStartup() { NaClBrowser::GetInstance()->EarlyStartup(); #if defined(OS_LINUX) && !defined(OS_CHROMEOS) // Open the IRT file early to make sure that it isn't replaced out from diff --git a/chrome/browser/nacl_host/nacl_process_host.h b/chrome/browser/nacl_host/nacl_process_host.h index 8a884fcf15..6db00bc395 100644 --- a/chrome/browser/nacl_host/nacl_process_host.h +++ b/chrome/browser/nacl_host/nacl_process_host.h @@ -23,7 +23,6 @@ class CommandLine; class ExtensionInfoMap; -class NaClBrowserDelegate; class NaClHostMessageFilter; namespace content { @@ -76,7 +75,7 @@ class NaClProcessHost : public content::BrowserChildProcessHostDelegate { virtual void OnProcessCrashed(int exit_status) OVERRIDE; // Do any minimal work that must be done at browser startup. - static void EarlyStartup(NaClBrowserDelegate* delegate); + static void EarlyStartup(); // Initialize the new NaCl process. Result is returned by sending ipc // message reply_msg. diff --git a/chrome/browser/net/OWNERS b/chrome/browser/net/OWNERS index 525917bbfb..85296d3598 100644 --- a/chrome/browser/net/OWNERS +++ b/chrome/browser/net/OWNERS @@ -10,3 +10,6 @@ rdsmith@chromium.org rtenneti@chromium.org willchan@chromium.org wtc@chromium.org + +per-file proxy_policy_handler*=joaodasilva@chromium.org +per-file proxy_policy_handler*=dconnelly@chromium.org diff --git a/chrome/browser/net/net_error_tab_helper.cc b/chrome/browser/net/net_error_tab_helper.cc index c441c2cd18..cd40152f1f 100644 --- a/chrome/browser/net/net_error_tab_helper.cc +++ b/chrome/browser/net/net_error_tab_helper.cc @@ -95,6 +95,7 @@ void NetErrorTabHelper::DidStartProvisionalLoadForFrame( void NetErrorTabHelper::DidCommitProvisionalLoadForFrame( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& url, PageTransition transition_type, @@ -120,6 +121,7 @@ void NetErrorTabHelper::DidCommitProvisionalLoadForFrame( void NetErrorTabHelper::DidFailProvisionalLoad( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& validated_url, int error_code, diff --git a/chrome/browser/net/net_error_tab_helper.h b/chrome/browser/net/net_error_tab_helper.h index 2a38e0c032..e9e1fc64d0 100644 --- a/chrome/browser/net/net_error_tab_helper.h +++ b/chrome/browser/net/net_error_tab_helper.h @@ -57,6 +57,7 @@ class NetErrorTabHelper virtual void DidCommitProvisionalLoadForFrame( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& url, content::PageTransition transition_type, @@ -64,6 +65,7 @@ class NetErrorTabHelper virtual void DidFailProvisionalLoad( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& validated_url, int error_code, diff --git a/chrome/browser/net/net_error_tab_helper_unittest.cc b/chrome/browser/net/net_error_tab_helper_unittest.cc index 1d4a225fe8..488faeb443 100644 --- a/chrome/browser/net/net_error_tab_helper_unittest.cc +++ b/chrome/browser/net/net_error_tab_helper_unittest.cc @@ -78,6 +78,7 @@ class NetErrorTabHelperTest : public testing::Test { void CommitProvisionalLoad(MainFrame main_frame) { tab_helper_.DidCommitProvisionalLoadForFrame( 1, // frame id + string16(), (main_frame == MAIN_FRAME), bogus_url_, // url content::PAGE_TRANSITION_TYPED, @@ -94,6 +95,7 @@ class NetErrorTabHelperTest : public testing::Test { tab_helper_.DidFailProvisionalLoad( 1, // frame id + string16(), (main_frame == MAIN_FRAME), bogus_url_, // validated_url net_error, diff --git a/chrome/browser/net/predictor_tab_helper.cc b/chrome/browser/net/predictor_tab_helper.cc index 0b8be8f3b5..b1fd4509a4 100644 --- a/chrome/browser/net/predictor_tab_helper.cc +++ b/chrome/browser/net/predictor_tab_helper.cc @@ -4,8 +4,11 @@ #include "chrome/browser/net/predictor_tab_helper.h" +#include "base/command_line.h" #include "chrome/browser/net/predictor.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/url_constants.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/frame_navigate_params.h" @@ -38,6 +41,20 @@ PredictorTabHelper::PredictorTabHelper(content::WebContents* web_contents) PredictorTabHelper::~PredictorTabHelper() { } +void PredictorTabHelper::NavigateToPendingEntry( + const GURL& url, + content::NavigationController::ReloadType reload_type) { + Profile* profile = + Profile::FromBrowserContext(web_contents()->GetBrowserContext()); + chrome_browser_net::Predictor* predictor = profile->GetNetworkPredictor(); + if (!predictor) + return; + if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kChromeFrame) && + (url.SchemeIs(content::kHttpScheme) || + url.SchemeIs(content::kHttpsScheme))) + predictor->PreconnectUrlAndSubresources(url, GURL()); +} + void PredictorTabHelper::DidNavigateMainFrame( const content::LoadCommittedDetails& details, const content::FrameNavigateParams& params) { diff --git a/chrome/browser/net/predictor_tab_helper.h b/chrome/browser/net/predictor_tab_helper.h index 2a2a898c58..90a121dfd4 100644 --- a/chrome/browser/net/predictor_tab_helper.h +++ b/chrome/browser/net/predictor_tab_helper.h @@ -19,6 +19,9 @@ class PredictorTabHelper virtual ~PredictorTabHelper(); // content::WebContentsObserver implementation + virtual void NavigateToPendingEntry( + const GURL& url, + content::NavigationController::ReloadType reload_type) OVERRIDE; virtual void DidNavigateMainFrame( const content::LoadCommittedDetails& details, const content::FrameNavigateParams& params) OVERRIDE; diff --git a/chrome/browser/net/proxy_policy_handler.cc b/chrome/browser/net/proxy_policy_handler.cc new file mode 100644 index 0000000000..d1ce622e5c --- /dev/null +++ b/chrome/browser/net/proxy_policy_handler.cc @@ -0,0 +1,333 @@ +// Copyright 2013 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/net/proxy_policy_handler.h" + +#include "base/logging.h" +#include "base/prefs/pref_value_map.h" +#include "base/strings/string_number_conversions.h" +#include "base/values.h" +#include "chrome/browser/policy/configuration_policy_handler.h" +#include "chrome/browser/policy/policy_error_map.h" +#include "chrome/browser/policy/policy_map.h" +#include "chrome/browser/prefs/proxy_config_dictionary.h" +#include "chrome/browser/prefs/proxy_prefs.h" +#include "chrome/common/pref_names.h" +#include "grit/generated_resources.h" +#include "policy/policy_constants.h" + +namespace { + +// This is used to check whether for a given ProxyMode value, the ProxyPacUrl, +// the ProxyBypassList and the ProxyServer policies are allowed to be specified. +// |error_message_id| is the message id of the localized error message to show +// when the policies are not specified as allowed. Each value of ProxyMode +// has a ProxyModeValidationEntry in the |kProxyModeValidationMap| below. +struct ProxyModeValidationEntry { + const char* mode_value; + bool pac_url_allowed; + bool bypass_list_allowed; + bool server_allowed; + int error_message_id; +}; + +// List of entries determining which proxy policies can be specified, depending +// on the ProxyMode. +const ProxyModeValidationEntry kProxyModeValidationMap[] = { + { ProxyPrefs::kDirectProxyModeName, + false, false, false, IDS_POLICY_PROXY_MODE_DISABLED_ERROR }, + { ProxyPrefs::kAutoDetectProxyModeName, + false, false, false, IDS_POLICY_PROXY_MODE_AUTO_DETECT_ERROR }, + { ProxyPrefs::kPacScriptProxyModeName, + true, false, false, IDS_POLICY_PROXY_MODE_PAC_URL_ERROR }, + { ProxyPrefs::kFixedServersProxyModeName, + false, true, true, IDS_POLICY_PROXY_MODE_FIXED_SERVERS_ERROR }, + { ProxyPrefs::kSystemProxyModeName, + false, false, false, IDS_POLICY_PROXY_MODE_SYSTEM_ERROR }, +}; + +} // namespace + +namespace policy { + +// The proxy policies have the peculiarity that they are loaded from individual +// policies, but the providers then expose them through a unified +// DictionaryValue. Once Dictionary policies are fully supported, the individual +// proxy policies will be deprecated. http://crbug.com/108996 + +ProxyPolicyHandler::ProxyPolicyHandler() {} + +ProxyPolicyHandler::~ProxyPolicyHandler() { +} + +bool ProxyPolicyHandler::CheckPolicySettings(const PolicyMap& policies, + PolicyErrorMap* errors) { + const base::Value* mode = GetProxyPolicyValue(policies, key::kProxyMode); + const base::Value* server = GetProxyPolicyValue(policies, key::kProxyServer); + const base::Value* server_mode = + GetProxyPolicyValue(policies, key::kProxyServerMode); + const base::Value* pac_url = GetProxyPolicyValue(policies, key::kProxyPacUrl); + const base::Value* bypass_list = + GetProxyPolicyValue(policies, key::kProxyBypassList); + + if ((server || pac_url || bypass_list) && !(mode || server_mode)) { + errors->AddError(key::kProxySettings, + key::kProxyMode, + IDS_POLICY_NOT_SPECIFIED_ERROR); + return false; + } + + std::string mode_value; + if (!CheckProxyModeAndServerMode(policies, errors, &mode_value)) + return false; + + // If neither ProxyMode nor ProxyServerMode are specified, mode_value will be + // empty and the proxy shouldn't be configured at all. + if (mode_value.empty()) + return true; + + bool is_valid_mode = false; + for (size_t i = 0; i != arraysize(kProxyModeValidationMap); ++i) { + const ProxyModeValidationEntry& entry = kProxyModeValidationMap[i]; + if (entry.mode_value != mode_value) + continue; + + is_valid_mode = true; + + if (!entry.pac_url_allowed && pac_url) { + errors->AddError(key::kProxySettings, + key::kProxyPacUrl, + entry.error_message_id); + } + if (!entry.bypass_list_allowed && bypass_list) { + errors->AddError(key::kProxySettings, + key::kProxyBypassList, + entry.error_message_id); + } + if (!entry.server_allowed && server) { + errors->AddError(key::kProxySettings, + key::kProxyServer, + entry.error_message_id); + } + + if ((!entry.pac_url_allowed && pac_url) || + (!entry.bypass_list_allowed && bypass_list) || + (!entry.server_allowed && server)) { + return false; + } + } + + if (!is_valid_mode) { + errors->AddError(key::kProxySettings, + mode ? key::kProxyMode : key::kProxyServerMode, + IDS_POLICY_OUT_OF_RANGE_ERROR, + mode_value); + return false; + } + return true; +} + +void ProxyPolicyHandler::ApplyPolicySettings(const PolicyMap& policies, + PrefValueMap* prefs) { + const base::Value* mode = GetProxyPolicyValue(policies, key::kProxyMode); + const base::Value* server = GetProxyPolicyValue(policies, key::kProxyServer); + const base::Value* server_mode = + GetProxyPolicyValue(policies, key::kProxyServerMode); + const base::Value* pac_url = GetProxyPolicyValue(policies, key::kProxyPacUrl); + const base::Value* bypass_list = + GetProxyPolicyValue(policies, key::kProxyBypassList); + + ProxyPrefs::ProxyMode proxy_mode; + if (mode) { + std::string string_mode; + CHECK(mode->GetAsString(&string_mode)); + CHECK(ProxyPrefs::StringToProxyMode(string_mode, &proxy_mode)); + } else if (server_mode) { + int int_mode = 0; + CHECK(server_mode->GetAsInteger(&int_mode)); + + switch (int_mode) { + case PROXY_SERVER_MODE: + proxy_mode = ProxyPrefs::MODE_DIRECT; + break; + case PROXY_AUTO_DETECT_PROXY_SERVER_MODE: + proxy_mode = ProxyPrefs::MODE_AUTO_DETECT; + break; + case PROXY_MANUALLY_CONFIGURED_PROXY_SERVER_MODE: + proxy_mode = ProxyPrefs::MODE_FIXED_SERVERS; + if (pac_url) + proxy_mode = ProxyPrefs::MODE_PAC_SCRIPT; + break; + case PROXY_USE_SYSTEM_PROXY_SERVER_MODE: + proxy_mode = ProxyPrefs::MODE_SYSTEM; + break; + default: + proxy_mode = ProxyPrefs::MODE_DIRECT; + NOTREACHED(); + } + } else { + return; + } + + switch (proxy_mode) { + case ProxyPrefs::MODE_DIRECT: + prefs->SetValue(prefs::kProxy, ProxyConfigDictionary::CreateDirect()); + break; + case ProxyPrefs::MODE_AUTO_DETECT: + prefs->SetValue(prefs::kProxy, ProxyConfigDictionary::CreateAutoDetect()); + break; + case ProxyPrefs::MODE_PAC_SCRIPT: { + std::string pac_url_string; + if (pac_url && pac_url->GetAsString(&pac_url_string)) { + prefs->SetValue( + prefs::kProxy, + ProxyConfigDictionary::CreatePacScript(pac_url_string, false)); + } else { + NOTREACHED(); + } + break; + } + case ProxyPrefs::MODE_FIXED_SERVERS: { + std::string proxy_server; + std::string bypass_list_string; + if (server->GetAsString(&proxy_server)) { + if (bypass_list) + bypass_list->GetAsString(&bypass_list_string); + prefs->SetValue(prefs::kProxy, + ProxyConfigDictionary::CreateFixedServers( + proxy_server, bypass_list_string)); + } + break; + } + case ProxyPrefs::MODE_SYSTEM: + prefs->SetValue(prefs::kProxy, ProxyConfigDictionary::CreateSystem()); + break; + case ProxyPrefs::kModeCount: + NOTREACHED(); + } +} + +const base::Value* ProxyPolicyHandler::GetProxyPolicyValue( + const PolicyMap& policies, const char* policy_name) { + // See note on the ProxyPolicyHandler implementation above. + const base::Value* value = policies.GetValue(key::kProxySettings); + const DictionaryValue* settings; + if (!value || !value->GetAsDictionary(&settings)) + return NULL; + + const base::Value* policy_value = NULL; + std::string tmp; + if (!settings->Get(policy_name, &policy_value) || + policy_value->IsType(Value::TYPE_NULL) || + (policy_value->IsType(Value::TYPE_STRING) && + policy_value->GetAsString(&tmp) && + tmp.empty())) { + return NULL; + } + return policy_value; +} + +bool ProxyPolicyHandler::CheckProxyModeAndServerMode(const PolicyMap& policies, + PolicyErrorMap* errors, + std::string* mode_value) { + const base::Value* mode = GetProxyPolicyValue(policies, key::kProxyMode); + const base::Value* server = GetProxyPolicyValue(policies, key::kProxyServer); + const base::Value* server_mode = + GetProxyPolicyValue(policies, key::kProxyServerMode); + const base::Value* pac_url = GetProxyPolicyValue(policies, key::kProxyPacUrl); + + // If there's a server mode, convert it into a mode. + // When both are specified, the mode takes precedence. + if (mode) { + if (server_mode) { + errors->AddError(key::kProxySettings, + key::kProxyServerMode, + IDS_POLICY_OVERRIDDEN, + key::kProxyMode); + } + if (!mode->GetAsString(mode_value)) { + errors->AddError(key::kProxySettings, + key::kProxyMode, + IDS_POLICY_TYPE_ERROR, + ValueTypeToString(Value::TYPE_BOOLEAN)); + return false; + } + + ProxyPrefs::ProxyMode mode; + if (!ProxyPrefs::StringToProxyMode(*mode_value, &mode)) { + errors->AddError(key::kProxySettings, + key::kProxyMode, + IDS_POLICY_INVALID_PROXY_MODE_ERROR); + return false; + } + + if (mode == ProxyPrefs::MODE_PAC_SCRIPT && !pac_url) { + errors->AddError(key::kProxySettings, + key::kProxyPacUrl, + IDS_POLICY_NOT_SPECIFIED_ERROR); + return false; + } else if (mode == ProxyPrefs::MODE_FIXED_SERVERS && !server) { + errors->AddError(key::kProxySettings, + key::kProxyServer, + IDS_POLICY_NOT_SPECIFIED_ERROR); + return false; + } + } else if (server_mode) { + int server_mode_value; + if (!server_mode->GetAsInteger(&server_mode_value)) { + errors->AddError(key::kProxySettings, + key::kProxyServerMode, + IDS_POLICY_TYPE_ERROR, + ValueTypeToString(Value::TYPE_INTEGER)); + return false; + } + + switch (server_mode_value) { + case PROXY_SERVER_MODE: + *mode_value = ProxyPrefs::kDirectProxyModeName; + break; + case PROXY_AUTO_DETECT_PROXY_SERVER_MODE: + *mode_value = ProxyPrefs::kAutoDetectProxyModeName; + break; + case PROXY_MANUALLY_CONFIGURED_PROXY_SERVER_MODE: + if (server && pac_url) { + int message_id = IDS_POLICY_PROXY_BOTH_SPECIFIED_ERROR; + errors->AddError(key::kProxySettings, + key::kProxyServer, + message_id); + errors->AddError(key::kProxySettings, + key::kProxyPacUrl, + message_id); + return false; + } + if (!server && !pac_url) { + int message_id = IDS_POLICY_PROXY_NEITHER_SPECIFIED_ERROR; + errors->AddError(key::kProxySettings, + key::kProxyServer, + message_id); + errors->AddError(key::kProxySettings, + key::kProxyPacUrl, + message_id); + return false; + } + if (pac_url) + *mode_value = ProxyPrefs::kPacScriptProxyModeName; + else + *mode_value = ProxyPrefs::kFixedServersProxyModeName; + break; + case PROXY_USE_SYSTEM_PROXY_SERVER_MODE: + *mode_value = ProxyPrefs::kSystemProxyModeName; + break; + default: + errors->AddError(key::kProxySettings, + key::kProxyServerMode, + IDS_POLICY_OUT_OF_RANGE_ERROR, + base::IntToString(server_mode_value)); + return false; + } + } + return true; +} + +} // namespace policy diff --git a/chrome/browser/net/proxy_policy_handler.h b/chrome/browser/net/proxy_policy_handler.h new file mode 100644 index 0000000000..b4208d4806 --- /dev/null +++ b/chrome/browser/net/proxy_policy_handler.h @@ -0,0 +1,64 @@ +// Copyright 2013 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_NET_PROXY_POLICY_HANDLER_H_ +#define CHROME_BROWSER_NET_PROXY_POLICY_HANDLER_H_ + +#include <string> + +#include "base/basictypes.h" +#include "chrome/browser/policy/configuration_policy_handler.h" + +namespace policy { + +class ProxyMap; +class ProxyErrorMap; + +// ConfigurationPolicyHandler for the proxy policies. +class ProxyPolicyHandler : public ConfigurationPolicyHandler { + public: + // Constants for the "Proxy Server Mode" defined in the policies. + // Note that these diverge from internal presentation defined in + // ProxyPrefs::ProxyMode for legacy reasons. The following four + // PolicyProxyModeType types were not very precise and had overlapping use + // cases. + enum ProxyModeType { + // Disable Proxy, connect directly. + PROXY_SERVER_MODE = 0, + // Auto detect proxy or use specific PAC script if given. + PROXY_AUTO_DETECT_PROXY_SERVER_MODE = 1, + // Use manually configured proxy servers (fixed servers). + PROXY_MANUALLY_CONFIGURED_PROXY_SERVER_MODE = 2, + // Use system proxy server. + PROXY_USE_SYSTEM_PROXY_SERVER_MODE = 3, + + MODE_COUNT + }; + + ProxyPolicyHandler(); + virtual ~ProxyPolicyHandler(); + + // ConfigurationPolicyHandler methods: + virtual bool CheckPolicySettings(const PolicyMap& policies, + PolicyErrorMap* errors) OVERRIDE; + virtual void ApplyPolicySettings(const PolicyMap& policies, + PrefValueMap* prefs) OVERRIDE; + + private: + const base::Value* GetProxyPolicyValue(const PolicyMap& policies, + const char* policy_name); + + // Converts the deprecated ProxyServerMode policy value to a ProxyMode value + // and places the result in |mode_value|. Returns whether the conversion + // succeeded. + bool CheckProxyModeAndServerMode(const PolicyMap& policies, + PolicyErrorMap* errors, + std::string* mode_value); + + DISALLOW_COPY_AND_ASSIGN(ProxyPolicyHandler); +}; + +} // namespace policy + +#endif // CHROME_BROWSER_NET_PROXY_POLICY_HANDLER_H_ diff --git a/chrome/browser/net/proxy_policy_handler_unittest.cc b/chrome/browser/net/proxy_policy_handler_unittest.cc new file mode 100644 index 0000000000..4ff300a624 --- /dev/null +++ b/chrome/browser/net/proxy_policy_handler_unittest.cc @@ -0,0 +1,271 @@ +// Copyright 2013 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 <string> + +#include "base/values.h" +#include "chrome/browser/net/proxy_policy_handler.h" +#include "chrome/browser/policy/configuration_policy_pref_store.h" +#include "chrome/browser/policy/configuration_policy_pref_store_unittest.h" +#include "chrome/browser/prefs/proxy_config_dictionary.h" +#include "chrome/browser/prefs/proxy_prefs.h" +#include "chrome/common/pref_names.h" +#include "policy/policy_constants.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace policy { + +// Test cases for the proxy policy settings. +class ProxyPolicyHandlerTest + : public ConfigurationPolicyPrefStoreTest { + protected: + // Verify that all the proxy prefs are set to the specified expected values. + void VerifyProxyPrefs( + const std::string& expected_proxy_server, + const std::string& expected_proxy_pac_url, + const std::string& expected_proxy_bypass_list, + const ProxyPrefs::ProxyMode& expected_proxy_mode) { + const base::Value* value = NULL; + ASSERT_TRUE(store_->GetValue(prefs::kProxy, &value)); + ASSERT_EQ(base::Value::TYPE_DICTIONARY, value->GetType()); + ProxyConfigDictionary dict( + static_cast<const base::DictionaryValue*>(value)); + std::string s; + if (expected_proxy_server.empty()) { + EXPECT_FALSE(dict.GetProxyServer(&s)); + } else { + ASSERT_TRUE(dict.GetProxyServer(&s)); + EXPECT_EQ(expected_proxy_server, s); + } + if (expected_proxy_pac_url.empty()) { + EXPECT_FALSE(dict.GetPacUrl(&s)); + } else { + ASSERT_TRUE(dict.GetPacUrl(&s)); + EXPECT_EQ(expected_proxy_pac_url, s); + } + if (expected_proxy_bypass_list.empty()) { + EXPECT_FALSE(dict.GetBypassList(&s)); + } else { + ASSERT_TRUE(dict.GetBypassList(&s)); + EXPECT_EQ(expected_proxy_bypass_list, s); + } + ProxyPrefs::ProxyMode mode; + ASSERT_TRUE(dict.GetMode(&mode)); + EXPECT_EQ(expected_proxy_mode, mode); + } +}; + +TEST_F(ProxyPolicyHandlerTest, ManualOptions) { + PolicyMap policy; + policy.Set(key::kProxyBypassList, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateStringValue("http://chromium.org/override"), + NULL); + policy.Set(key::kProxyServer, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateStringValue("chromium.org"), NULL); + policy.Set( + key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateIntegerValue( + ProxyPolicyHandler::PROXY_MANUALLY_CONFIGURED_PROXY_SERVER_MODE), + NULL); + UpdateProviderPolicy(policy); + + VerifyProxyPrefs("chromium.org", + std::string(), + "http://chromium.org/override", + ProxyPrefs::MODE_FIXED_SERVERS); +} + +TEST_F(ProxyPolicyHandlerTest, ManualOptionsReversedApplyOrder) { + PolicyMap policy; + policy.Set( + key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateIntegerValue( + ProxyPolicyHandler::PROXY_MANUALLY_CONFIGURED_PROXY_SERVER_MODE), + NULL); + policy.Set(key::kProxyBypassList, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateStringValue("http://chromium.org/override"), + NULL); + policy.Set(key::kProxyServer, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateStringValue("chromium.org"), NULL); + UpdateProviderPolicy(policy); + + VerifyProxyPrefs("chromium.org", + std::string(), + "http://chromium.org/override", + ProxyPrefs::MODE_FIXED_SERVERS); +} + +TEST_F(ProxyPolicyHandlerTest, ManualOptionsInvalid) { + PolicyMap policy; + policy.Set( + key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateIntegerValue( + ProxyPolicyHandler::PROXY_MANUALLY_CONFIGURED_PROXY_SERVER_MODE), + NULL); + UpdateProviderPolicy(policy); + + const base::Value* value = NULL; + EXPECT_FALSE(store_->GetValue(prefs::kProxy, &value)); +} + +TEST_F(ProxyPolicyHandlerTest, NoProxyServerMode) { + PolicyMap policy; + policy.Set(key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateIntegerValue( + ProxyPolicyHandler::PROXY_SERVER_MODE), + NULL); + UpdateProviderPolicy(policy); + VerifyProxyPrefs( + std::string(), std::string(), std::string(), ProxyPrefs::MODE_DIRECT); +} + +TEST_F(ProxyPolicyHandlerTest, NoProxyModeName) { + PolicyMap policy; + policy.Set(key::kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateStringValue(ProxyPrefs::kDirectProxyModeName), + NULL); + UpdateProviderPolicy(policy); + VerifyProxyPrefs( + std::string(), std::string(), std::string(), ProxyPrefs::MODE_DIRECT); +} + +TEST_F(ProxyPolicyHandlerTest, AutoDetectProxyServerMode) { + PolicyMap policy; + policy.Set( + key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateIntegerValue( + ProxyPolicyHandler::PROXY_AUTO_DETECT_PROXY_SERVER_MODE), + NULL); + UpdateProviderPolicy(policy); + VerifyProxyPrefs(std::string(), + std::string(), + std::string(), + ProxyPrefs::MODE_AUTO_DETECT); +} + +TEST_F(ProxyPolicyHandlerTest, AutoDetectProxyModeName) { + PolicyMap policy; + policy.Set(key::kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateStringValue( + ProxyPrefs::kAutoDetectProxyModeName), + NULL); + UpdateProviderPolicy(policy); + VerifyProxyPrefs(std::string(), + std::string(), + std::string(), + ProxyPrefs::MODE_AUTO_DETECT); +} + +TEST_F(ProxyPolicyHandlerTest, PacScriptProxyMode) { + PolicyMap policy; + policy.Set(key::kProxyPacUrl, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateStringValue("http://short.org/proxy.pac"), + NULL); + policy.Set(key::kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateStringValue( + ProxyPrefs::kPacScriptProxyModeName), + NULL); + UpdateProviderPolicy(policy); + VerifyProxyPrefs(std::string(), + "http://short.org/proxy.pac", + std::string(), + ProxyPrefs::MODE_PAC_SCRIPT); +} + +TEST_F(ProxyPolicyHandlerTest, PacScriptProxyModeInvalid) { + PolicyMap policy; + policy.Set(key::kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateStringValue( + ProxyPrefs::kPacScriptProxyModeName), + NULL); + UpdateProviderPolicy(policy); + const base::Value* value = NULL; + EXPECT_FALSE(store_->GetValue(prefs::kProxy, &value)); +} + +// Regression test for http://crbug.com/78016, CPanel returns empty strings +// for unset properties. +TEST_F(ProxyPolicyHandlerTest, PacScriptProxyModeBug78016) { + PolicyMap policy; + policy.Set(key::kProxyServer, + POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_USER, + base::Value::CreateStringValue(std::string()), + NULL); + policy.Set(key::kProxyPacUrl, + POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_USER, + base::Value::CreateStringValue("http://short.org/proxy.pac"), + NULL); + policy.Set(key::kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateStringValue( + ProxyPrefs::kPacScriptProxyModeName), + NULL); + UpdateProviderPolicy(policy); + VerifyProxyPrefs(std::string(), + "http://short.org/proxy.pac", + std::string(), + ProxyPrefs::MODE_PAC_SCRIPT); +} + +TEST_F(ProxyPolicyHandlerTest, UseSystemProxyServerMode) { + PolicyMap policy; + policy.Set(key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateIntegerValue( + ProxyPolicyHandler::PROXY_USE_SYSTEM_PROXY_SERVER_MODE), + NULL); + UpdateProviderPolicy(policy); + VerifyProxyPrefs( + std::string(), std::string(), std::string(), ProxyPrefs::MODE_SYSTEM); +} + +TEST_F(ProxyPolicyHandlerTest, UseSystemProxyMode) { + PolicyMap policy; + policy.Set(key::kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateStringValue(ProxyPrefs::kSystemProxyModeName), + NULL); + UpdateProviderPolicy(policy); + VerifyProxyPrefs( + std::string(), std::string(), std::string(), ProxyPrefs::MODE_SYSTEM); +} + +TEST_F(ProxyPolicyHandlerTest, + ProxyModeOverridesProxyServerMode) { + PolicyMap policy; + policy.Set(key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateIntegerValue( + ProxyPolicyHandler::PROXY_SERVER_MODE), + NULL); + policy.Set(key::kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateStringValue( + ProxyPrefs::kAutoDetectProxyModeName), + NULL); + UpdateProviderPolicy(policy); + VerifyProxyPrefs(std::string(), + std::string(), + std::string(), + ProxyPrefs::MODE_AUTO_DETECT); +} + +TEST_F(ProxyPolicyHandlerTest, ProxyInvalid) { + // No mode expects all three parameters being set. + PolicyMap policy; + policy.Set(key::kProxyPacUrl, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateStringValue("http://short.org/proxy.pac"), + NULL); + policy.Set(key::kProxyBypassList, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateStringValue("http://chromium.org/override"), + NULL); + policy.Set(key::kProxyServer, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateStringValue("chromium.org"), NULL); + for (int i = 0; i < ProxyPolicyHandler::MODE_COUNT; ++i) { + policy.Set(key::kProxyServerMode, POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_USER, base::Value::CreateIntegerValue(i), NULL); + UpdateProviderPolicy(policy); + const base::Value* value = NULL; + EXPECT_FALSE(store_->GetValue(prefs::kProxy, &value)); + } +} + +} // namespace policy diff --git a/chrome/browser/net/spdyproxy/OWNERS b/chrome/browser/net/spdyproxy/OWNERS index 845f1d7b66..a46d1ea6da 100644 --- a/chrome/browser/net/spdyproxy/OWNERS +++ b/chrome/browser/net/spdyproxy/OWNERS @@ -1 +1,2 @@ bengr@chromium.org +marq@chromium.org diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_settings.cc b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings.cc new file mode 100644 index 0000000000..4318d77054 --- /dev/null +++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings.cc @@ -0,0 +1,464 @@ +// Copyright 2013 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/net/spdyproxy/data_reduction_proxy_settings.h" + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/metrics/field_trial.h" +#include "base/metrics/histogram.h" +#include "base/prefs/pref_member.h" +#include "base/prefs/pref_service.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/prefs/proxy_prefs.h" +#include "chrome/browser/prefs/scoped_user_pref_update.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/pref_names.h" +#include "net/base/host_port_pair.h" +#include "net/base/load_flags.h" +#include "net/base/net_errors.h" +#include "net/url_request/url_fetcher.h" +#include "net/url_request/url_fetcher_delegate.h" +#include "net/url_request/url_request_status.h" +#include "url/gurl.h" + +using base::FieldTrialList; +using base::StringPrintf; + +namespace { + +// Key of the UMA DataReductionProxy.StartupState histogram. +const char kUMAProxyStartupStateHistogram[] = + "DataReductionProxy.StartupState"; +// Values of the UMA DataReductionProxy.StartupState histogram. +enum ProxyStartupState { + PROXY_NOT_AVAILABLE = 0, + PROXY_DISABLED, + PROXY_ENABLED, + PROXY_STARTUP_STATE_COUNT, +}; + +const char kEnabled[] = "Enabled"; + +int64 GetInt64PrefValue(const ListValue& list_value, size_t index) { + int64 val = 0; + std::string pref_value; + bool rv = list_value.GetString(index, &pref_value); + DCHECK(rv); + if (rv) { + rv = base::StringToInt64(pref_value, &val); + DCHECK(rv); + } + return val; +} + +} // namespace + +DataReductionProxySettings::DataReductionProxySettings() + : has_turned_on_(false), + has_turned_off_(false), + disabled_by_carrier_(false), + enabled_by_user_(false) { +} + +DataReductionProxySettings::~DataReductionProxySettings() { + if (IsDataReductionProxyAllowed()) + spdy_proxy_auth_enabled_.Destroy(); +} + +void DataReductionProxySettings::InitPrefMembers() { + spdy_proxy_auth_enabled_.Init( + prefs::kSpdyProxyAuthEnabled, + GetOriginalProfilePrefs(), + base::Bind(&DataReductionProxySettings::OnProxyEnabledPrefChange, + base::Unretained(this))); +} + +void DataReductionProxySettings::InitDataReductionProxySettings() { + // Disable the proxy if it is not allowed to be used. + if (!IsDataReductionProxyAllowed()) + return; + + InitPrefMembers(); + + AddDefaultProxyBypassRules(); + net::NetworkChangeNotifier::AddIPAddressObserver(this); + + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + + // Setting the kEnableSpdyProxyAuth switch has the same effect as enabling + // the feature via settings, in that once set, the preference will be sticky + // across instances of Chrome. Disabling the feature can only be done through + // the settings menu. + RecordDataReductionInit(); + if (spdy_proxy_auth_enabled_.GetValue() || + command_line.HasSwitch(switches::kEnableSpdyProxyAuth)) { + MaybeActivateDataReductionProxy(true); + } else { + // This is logged so we can use this information in user feedback. + LogProxyState(false /* enabled */, true /* at startup */); + } +} + +void DataReductionProxySettings::AddHostPatternToBypass( + const std::string& pattern) { + bypass_rules_.push_back(pattern); +} + +void DataReductionProxySettings::AddURLPatternToBypass( + const std::string& pattern) { + size_t pos = pattern.find("/"); + if (pattern.find("/", pos + 1) == pos + 1) + pos = pattern.find("/", pos + 2); + + std::string host_pattern; + if (pos != std::string::npos) + host_pattern = pattern.substr(0, pos); + else + host_pattern = pattern; + + AddHostPatternToBypass(host_pattern); +} + +bool DataReductionProxySettings::IsDataReductionProxyAllowed() { + return IsProxyOriginSetOnCommandLine() || + (FieldTrialList::FindFullName("DataCompressionProxyRollout") == kEnabled); +} + +bool DataReductionProxySettings::IsDataReductionProxyPromoAllowed() { + return IsProxyOriginSetOnCommandLine() || + (IsDataReductionProxyAllowed() && + FieldTrialList::FindFullName("DataCompressionProxyPromoVisibility") == + kEnabled); +} + +std::string DataReductionProxySettings::GetDataReductionProxyOrigin() { + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + if (command_line.HasSwitch(switches::kSpdyProxyAuthOrigin)) + return command_line.GetSwitchValueASCII(switches::kSpdyProxyAuthOrigin); +#if defined(SPDY_PROXY_AUTH_ORIGIN) + return SPDY_PROXY_AUTH_ORIGIN; +#else + return std::string(); +#endif +} + +std::string DataReductionProxySettings::GetDataReductionProxyAuth() { + if (!IsDataReductionProxyAllowed()) + return std::string(); + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + if (command_line.HasSwitch(switches::kSpdyProxyAuthOrigin)) { + // If an origin is provided via a switch, then only consider the value + // that is provided by a switch. Do not use the preprocessor constant. + // Don't expose SPDY_PROXY_AUTH_VALUE to a proxy passed in via the command + // line. + if (command_line.HasSwitch(switches::kSpdyProxyAuthValue)) + return command_line.GetSwitchValueASCII(switches::kSpdyProxyAuthValue); + return std::string(); + } +#if defined(SPDY_PROXY_AUTH_VALUE) + return SPDY_PROXY_AUTH_VALUE; +#else + return std::string(); +#endif +} + +bool DataReductionProxySettings::IsDataReductionProxyEnabled() { + return spdy_proxy_auth_enabled_.GetValue(); +} + +bool DataReductionProxySettings::IsDataReductionProxyManaged() { + return spdy_proxy_auth_enabled_.IsManaged(); +} + +void DataReductionProxySettings::SetDataReductionProxyEnabled(bool enabled) { + // Prevent configuring the proxy when it is not allowed to be used. + if (!IsDataReductionProxyAllowed()) + return; + + spdy_proxy_auth_enabled_.SetValue(enabled); + OnProxyEnabledPrefChange(); +} + +int64 DataReductionProxySettings::GetDataReductionLastUpdateTime() { + PrefService* local_state = GetLocalStatePrefs(); + int64 last_update_internal = + local_state->GetInt64(prefs::kDailyHttpContentLengthLastUpdateDate); + base::Time last_update = base::Time::FromInternalValue(last_update_internal); + return static_cast<int64>(last_update.ToJsTime()); +} + +std::vector<long long> +DataReductionProxySettings::GetDailyOriginalContentLengths() { + return GetDailyContentLengths(prefs::kDailyHttpOriginalContentLength); +} + +std::vector<long long> +DataReductionProxySettings::GetDailyReceivedContentLengths() { + return GetDailyContentLengths(prefs::kDailyHttpReceivedContentLength); +} + +void DataReductionProxySettings::OnURLFetchComplete( + const net::URLFetcher* source) { + net::URLRequestStatus status = source->GetStatus(); + if (status.status() == net::URLRequestStatus::FAILED && + status.error() == net::ERR_INTERNET_DISCONNECTED) { + return; + } + + std::string response; + source->GetResponseAsString(&response); + + if ("OK" == response.substr(0, 2)) { + DVLOG(1) << "The data reduction proxy is not blocked."; + + if (enabled_by_user_ && disabled_by_carrier_) { + // The user enabled the proxy, but sometime previously in the session, + // the network operator had blocked the proxy. Now that the network + // operator is unblocking it, configure it to the user's desires. + SetProxyConfigs(true, false); + } + disabled_by_carrier_ = false; + return; + } + DVLOG(1) << "The data reduction proxy is blocked."; + + if (enabled_by_user_ && !disabled_by_carrier_) { + // Disable the proxy. + SetProxyConfigs(false, false); + } + disabled_by_carrier_ = true; +} + +std::string DataReductionProxySettings::GetDataReductionProxyOriginHostPort() { + std::string spdy_proxy = GetDataReductionProxyOrigin(); + if (spdy_proxy.empty()) { + DLOG(ERROR) << "A SPDY proxy has not been set."; + return spdy_proxy; + } + // Remove a trailing slash from the proxy string if one exists as well as + // leading HTTPS scheme. + return net::HostPortPair::FromURL(GURL(spdy_proxy)).ToString(); +} + +void DataReductionProxySettings::OnIPAddressChanged() { + if (enabled_by_user_) { + DCHECK(IsDataReductionProxyAllowed()); + ProbeWhetherDataReductionProxyIsAvailable(); + } +} + +void DataReductionProxySettings::OnProxyEnabledPrefChange() { + if (!IsDataReductionProxyAllowed()) + return; + MaybeActivateDataReductionProxy(false); +} + +void DataReductionProxySettings::AddDefaultProxyBypassRules() { + // localhost + AddHostPatternToBypass("<local>"); + // RFC1918 private addresses. + AddHostPatternToBypass("10.0.0.0/8"); + AddHostPatternToBypass("172.16.0.0/12"); + AddHostPatternToBypass("192.168.0.0/16"); + // RFC4193 private addresses. + AddHostPatternToBypass("fc00::/7"); +} + +void DataReductionProxySettings::LogProxyState(bool enabled, bool at_startup) { + // This must stay a LOG(WARNING); the output is used in processing customer + // feedback. + const char kAtStartup[] = "at startup"; + const char kByUser[] = "by user action"; + const char kOn[] = "ON"; + const char kOff[] = "OFF"; + + LOG(WARNING) << "SPDY proxy " << (enabled ? kOn : kOff) + << " " << (at_startup ? kAtStartup : kByUser); +} + +PrefService* DataReductionProxySettings::GetOriginalProfilePrefs() { + return g_browser_process->profile_manager()->GetLastUsedProfile()-> + GetOriginalProfile()->GetPrefs(); +} + +PrefService* DataReductionProxySettings::GetLocalStatePrefs() { + return g_browser_process->local_state(); +} + +bool DataReductionProxySettings::IsProxyOriginSetOnCommandLine() { + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + return command_line.HasSwitch(switches::kSpdyProxyAuthOrigin); +} + +void DataReductionProxySettings::ResetDataReductionStatistics() { + PrefService* prefs = GetLocalStatePrefs(); + if (!prefs) + return; + ListPrefUpdate original_update(prefs, prefs::kDailyHttpOriginalContentLength); + ListPrefUpdate received_update(prefs, prefs::kDailyHttpReceivedContentLength); + original_update->Clear(); + received_update->Clear(); + for (size_t i = 0; i < spdyproxy::kNumDaysInHistory; ++i) { + original_update->AppendString(base::Int64ToString(0)); + received_update->AppendString(base::Int64ToString(0)); + } +} + +void DataReductionProxySettings::MaybeActivateDataReductionProxy( + bool at_startup) { + PrefService* prefs = GetOriginalProfilePrefs(); + + // TODO(marq): Consider moving this so stats are wiped the first time the + // proxy settings are actually (not maybe) turned on. + if (spdy_proxy_auth_enabled_.GetValue() && + !prefs->GetBoolean(prefs::kSpdyProxyAuthWasEnabledBefore)) { + prefs->SetBoolean(prefs::kSpdyProxyAuthWasEnabledBefore, true); + ResetDataReductionStatistics(); + } + + std::string spdy_proxy_origin = GetDataReductionProxyOriginHostPort(); + + // Configure use of the data reduction proxy if it is enabled and the proxy + // origin is non-empty. + enabled_by_user_= + spdy_proxy_auth_enabled_.GetValue() && !spdy_proxy_origin.empty(); + SetProxyConfigs(enabled_by_user_ && !disabled_by_carrier_, at_startup); + + // Check if the proxy has been disabled explicitly by the carrier. + if (enabled_by_user_) + ProbeWhetherDataReductionProxyIsAvailable(); +} + +void DataReductionProxySettings::SetProxyConfigs(bool enabled, + bool at_startup) { + LogProxyState(enabled, at_startup); + PrefService* prefs = GetOriginalProfilePrefs(); + DCHECK(prefs); + DictionaryPrefUpdate update(prefs, prefs::kProxy); + base::DictionaryValue* dict = update.Get(); + if (enabled) { + std::string proxy_server_config = + "http=" + GetDataReductionProxyOrigin() + ",direct://;"; + dict->SetString("server", proxy_server_config); + dict->SetString("mode", + ProxyModeToString(ProxyPrefs::MODE_FIXED_SERVERS)); + dict->SetString("bypass_list", JoinString(bypass_rules_, ", ")); + } else { + dict->SetString("mode", ProxyModeToString(ProxyPrefs::MODE_SYSTEM)); + dict->SetString("server", ""); + dict->SetString("bypass_list", ""); + } +} + +// Metrics methods +void DataReductionProxySettings::RecordDataReductionInit() { + ProxyStartupState state = PROXY_NOT_AVAILABLE; + if (IsDataReductionProxyAllowed()) + state = IsDataReductionProxyEnabled() ? PROXY_ENABLED : PROXY_DISABLED; + UMA_HISTOGRAM_ENUMERATION(kUMAProxyStartupStateHistogram, + state, + PROXY_STARTUP_STATE_COUNT); +} + +DataReductionProxySettings::ContentLengthList +DataReductionProxySettings::GetDailyContentLengths(const char* pref_name) { + DataReductionProxySettings::ContentLengthList content_lengths; + const ListValue* list_value = GetLocalStatePrefs()->GetList(pref_name); + if (list_value->GetSize() == spdyproxy::kNumDaysInHistory) { + for (size_t i = 0; i < spdyproxy::kNumDaysInHistory; ++i) { + content_lengths.push_back(GetInt64PrefValue(*list_value, i)); + } + } + return content_lengths; + } + +void DataReductionProxySettings::GetContentLengths( + unsigned int days, + int64* original_content_length, + int64* received_content_length, + int64* last_update_time) { + DCHECK_LE(days, spdyproxy::kNumDaysInHistory); + PrefService* local_state = GetLocalStatePrefs(); + if (!local_state) { + *original_content_length = 0L; + *received_content_length = 0L; + *last_update_time = 0L; + return; + } + + const ListValue* original_list = + local_state->GetList(prefs::kDailyHttpOriginalContentLength); + const ListValue* received_list = + local_state->GetList(prefs::kDailyHttpReceivedContentLength); + + if (original_list->GetSize() != spdyproxy::kNumDaysInHistory || + received_list->GetSize() != spdyproxy::kNumDaysInHistory) { + *original_content_length = 0L; + *received_content_length = 0L; + *last_update_time = 0L; + return; + } + + int64 orig = 0L; + int64 recv = 0L; + // Include days from the end of the list going backwards. + for (size_t i = spdyproxy::kNumDaysInHistory - days; + i < spdyproxy::kNumDaysInHistory; ++i) { + orig += GetInt64PrefValue(*original_list, i); + recv += GetInt64PrefValue(*received_list, i); + } + *original_content_length = orig; + *received_content_length = recv; + *last_update_time = + local_state->GetInt64(prefs::kDailyHttpContentLengthLastUpdateDate); +} + +std::string DataReductionProxySettings::GetProxyCheckURL() { + if (!IsDataReductionProxyAllowed()) + return std::string(); + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + if (command_line.HasSwitch(switches::kDataReductionProxyProbeURL)) { + return command_line.GetSwitchValueASCII( + switches::kDataReductionProxyProbeURL); + } +#if defined(DATA_REDUCTION_PROXY_PROBE_URL) + return DATA_REDUCTION_PROXY_PROBE_URL; +#else + return std::string(); +#endif +} + +net::URLFetcher* DataReductionProxySettings::GetURLFetcher() { + std::string url = GetProxyCheckURL(); + if (url.empty()) + return NULL; + net::URLFetcher* fetcher = net::URLFetcher::Create(GURL(url), + net::URLFetcher::GET, + this); + fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE); + fetcher->SetLoadFlags(net::LOAD_BYPASS_PROXY); + Profile* profile = g_browser_process->profile_manager()-> + GetDefaultProfile(); + fetcher->SetRequestContext(profile->GetRequestContext()); + // Configure max retries to be at most kMaxRetries times for 5xx errors. + static const int kMaxRetries = 5; + fetcher->SetMaxRetriesOn5xx(kMaxRetries); + return fetcher; +} + +void +DataReductionProxySettings::ProbeWhetherDataReductionProxyIsAvailable() { + net::URLFetcher* fetcher = GetURLFetcher(); + if (!fetcher) + return; + fetcher_.reset(fetcher); + fetcher_->Start(); +} diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_settings.h b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings.h new file mode 100644 index 0000000000..f066d66171 --- /dev/null +++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings.h @@ -0,0 +1,207 @@ +// Copyright 2013 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_NET_SPDYPROXY_DATA_REDUCTION_PROXY_SETTINGS_H_ +#define CHROME_BROWSER_NET_SPDYPROXY_DATA_REDUCTION_PROXY_SETTINGS_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/gtest_prod_util.h" +#include "base/memory/scoped_ptr.h" +#include "base/prefs/pref_member.h" +#include "net/base/network_change_notifier.h" +#include "net/url_request/url_fetcher_delegate.h" + +class PrefService; + +namespace net { +class URLFetcher; +} + +namespace spdyproxy { + +// The number of days of bandwidth usage statistics that are tracked. +const unsigned int kNumDaysInHistory = 60; + +// The number of days of bandwidth usage statistics that are presented. +const unsigned int kNumDaysInHistorySummary = 30; + +COMPILE_ASSERT(kNumDaysInHistorySummary <= kNumDaysInHistory, + DataReductionProxySettings_summary_too_long); + +} // namespace spdyproxy + +// Central point for configuring the data reduction proxy. +// This object lives on the UI thread and all of its methods are expected to +// be called from there. +class DataReductionProxySettings + : public net::URLFetcherDelegate, + public net::NetworkChangeNotifier::IPAddressObserver { + public: + typedef std::vector<long long> ContentLengthList; + + DataReductionProxySettings(); + virtual ~DataReductionProxySettings(); + + void InitDataReductionProxySettings(); + + // Add a host pattern to bypass. This should follow the same syntax used + // in net::ProxyBypassRules; that is, a hostname pattern, a hostname suffix + // pattern, an IP literal, a CIDR block, or the magic string '<local>'. + // Bypass settings persist for the life of this object and are applied + // each time the proxy is enabled, but are not updated while it is enabled. + void AddHostPatternToBypass(const std::string& pattern); + + // Add a URL pattern to bypass the proxy. The base implementation strips + // everything in |pattern| after the first single slash and then treats it + // as a hostname pattern. Subclasses may implement other semantics. + virtual void AddURLPatternToBypass(const std::string& pattern); + + // Returns true if the data reduction proxy is allowed to be used on this + // instance of Chrome. This could return false, for example, if this instance + // is not part of the field trial, or if the proxy name is not configured + // via gyp. + bool IsDataReductionProxyAllowed(); + + // Returns true if a screen promoting the data reduction proxy is allowed to + // be shown. Logic that decides when to show the promo should check its + // availability. This would return false if not part of a separate field + // trial that governs the use of the promotion. + bool IsDataReductionProxyPromoAllowed(); + + // Returns the URL of the data reduction proxy. + std::string GetDataReductionProxyOrigin(); + + // Returns a configuration string for the proxy. + std::string GetDataReductionProxyAuth(); + + // Returns true if the proxy is enabled. + bool IsDataReductionProxyEnabled(); + + // Returns true if the proxy is managed by an adminstrator's policy. + bool IsDataReductionProxyManaged(); + + // Enables or disables the data reduction proxy. If a probe URL is available, + // and a probe request fails at some point, the proxy won't be used until a + // probe succeeds. + void SetDataReductionProxyEnabled(bool enabled); + + // Returns the time in microseconds that the last update was made to the + // daily original and received content lengths. + int64 GetDataReductionLastUpdateTime(); + + // Returns a vector containing the total size of all HTTP content that was + // received over the last |kNumDaysInHistory| before any compression by the + // data reduction proxy. Each element in the vector contains one day of data. + ContentLengthList GetDailyOriginalContentLengths(); + + // Returns an vector containing the aggregate received HTTP content in the + // last |kNumDaysInHistory| days. + ContentLengthList GetDailyReceivedContentLengths(); + + // net::URLFetcherDelegate: + virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; + + protected: + void InitPrefMembers(); + virtual net::URLFetcher* GetURLFetcher(); + + // Virtualized for unit test support. + virtual PrefService* GetOriginalProfilePrefs(); + virtual PrefService* GetLocalStatePrefs(); + + std::string GetDataReductionProxyOriginHostPort(); + + bool IsProxyOriginSetOnCommandLine(); + void GetContentLengths(unsigned int days, + int64* original_content_length, + int64* received_content_length, + int64* last_update_time); + ContentLengthList GetDailyContentLengths(const char* pref_name); + + // Sets the proxy configs, enabling or disabling the proxy according to + // the value of |enabled|. |at_startup| is true when this method is called + // from InitDataReductionProxySettings. + virtual void SetProxyConfigs(bool enabled, bool at_startup); + + // Metrics methods. Subclasses should override if they wish to provide + // alternate methods. + virtual void RecordDataReductionInit(); + + virtual void AddDefaultProxyBypassRules(); + + // Writes a warning to the log that is used in backend processing of + // customer feedback. + void LogProxyState(bool enabled, bool at_startup); + + bool HasTurnedOn() { return has_turned_on_; } + bool HasTurnedOff() { return has_turned_off_; } + // Note that these flags may only be toggled to true, never back to false. + void SetHasTurnedOn() { has_turned_on_ = true; } + void SetHasTurnedOff() { has_turned_off_ = true; } + + // Accessor for unit tests. + std::vector<std::string> BypassRules() { return bypass_rules_;} + + private: + friend class DataReductionProxySettingsTestBase; + friend class DataReductionProxySettingsTest; + FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest, + TestResetDataReductionStatistics); + FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest, + TestIsProxyEnabledOrManaged); + FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest, + TestContentLengths); + FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest, + TestGetDailyContentLengths); + FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest, + TestMaybeActivateDataReductionProxy); + FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest, + TestOnIPAddressChanged); + FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest, + TestOnProxyEnabledPrefChange); + FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest, + TestInitDataReductionProxyOn); + FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest, + TestInitDataReductionProxyOff); + FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest, + TestBypassList); + + // NetworkChangeNotifier::IPAddressObserver: + virtual void OnIPAddressChanged() OVERRIDE; + + void OnProxyEnabledPrefChange(); + + void ResetDataReductionStatistics(); + + void MaybeActivateDataReductionProxy(bool at_startup); + + // Requests the proxy probe URL, if one is set. If unable to do so, disables + // the proxy, if enabled. Otherwise enables the proxy if disabled by a probe + // failure. + void ProbeWhetherDataReductionProxyIsAvailable(); + std::string GetProxyCheckURL(); + + std::vector<std::string> bypass_rules_; + + // Indicate whether a user has turned on the data reduction proxy previously + // in this session. + bool has_turned_on_; + + // Indicate whether a user has turned off the data reduction proxy previously + // in this session. + bool has_turned_off_; + + bool disabled_by_carrier_; + bool enabled_by_user_; + + scoped_ptr<net::URLFetcher> fetcher_; + BooleanPrefMember spdy_proxy_auth_enabled_; + + DISALLOW_COPY_AND_ASSIGN(DataReductionProxySettings); +}; + +#endif // CHROME_BROWSER_NET_SPDYPROXY_DATA_REDUCTION_PROXY_SETTINGS_H_ diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.cc b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.cc index be70d9212f..2c23fbff12 100644 --- a/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.cc +++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.cc @@ -41,7 +41,6 @@ using base::android::ScopedJavaLocalRef; using base::FieldTrialList; using base::StringPrintf; - namespace { // The C++ definition of enum SpdyProxyAuthState defined in @@ -56,205 +55,80 @@ enum { NUM_SPDY_PROXY_AUTH_STATE }; -const char kEnabled[] = "Enabled"; - -bool IsProxyOriginSetOnCommandLine() { - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); - return command_line.HasSwitch(switches::kSpdyProxyAuthOrigin); -} - -bool IsProxyAllowed() { - return IsProxyOriginSetOnCommandLine() || - (FieldTrialList::FindFullName("DataCompressionProxyRollout") == kEnabled); -} - -bool IsProxyPromoAllowed() { - return IsProxyOriginSetOnCommandLine() || - (IsProxyAllowed() && - FieldTrialList::FindFullName("DataCompressionProxyPromoVisibility") == - kEnabled); -} - -int64 GetInt64PrefValue(const ListValue& list_value, size_t index) { - int64 val = 0; - std::string pref_value; - bool rv = list_value.GetString(index, &pref_value); - DCHECK(rv); - if (rv) { - rv = base::StringToInt64(pref_value, &val); - DCHECK(rv); - } - return val; -} - -std::string GetProxyCheckURL(){ - if (!IsProxyAllowed()) - return std::string(); - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); - if (command_line.HasSwitch(switches::kDataReductionProxyProbeURL)) { - return command_line.GetSwitchValueASCII(switches::kSpdyProxyAuthOrigin); - } -#if defined(DATA_REDUCTION_PROXY_PROBE_URL) - return DATA_REDUCTION_PROXY_PROBE_URL; -#else - return std::string(); -#endif -} - -std::string GetDataReductionProxyOriginInternal() { - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); - if (command_line.HasSwitch(switches::kSpdyProxyAuthOrigin)) { - return command_line.GetSwitchValueASCII(switches::kSpdyProxyAuthOrigin); - } -#if defined(SPDY_PROXY_AUTH_ORIGIN) - return SPDY_PROXY_AUTH_ORIGIN; -#else - return std::string(); -#endif -} - -std::string GetDataReductionProxyOriginHostPort() { - std::string spdy_proxy = GetDataReductionProxyOriginInternal(); - if (spdy_proxy.empty()) { - DLOG(ERROR) << "A SPDY proxy has not been set."; - return spdy_proxy; - } - // Remove a trailing slash from the proxy string if one exists as well as - // leading HTTPS scheme. - return net::HostPortPair::FromURL(GURL(spdy_proxy)).ToString(); -} - } // namespace - DataReductionProxySettingsAndroid::DataReductionProxySettingsAndroid( - JNIEnv* env, jobject obj) - : has_turned_on_(false), - has_turned_off_(false), - disabled_by_carrier_(false), - enabled_by_user_(false) { + JNIEnv* env, jobject obj): DataReductionProxySettings() { } -DataReductionProxySettingsAndroid::~DataReductionProxySettingsAndroid() { - if (IsProxyAllowed()) - spdy_proxy_auth_enabled_.Destroy(); -} - -void DataReductionProxySettingsAndroid::InitPrefMembers() { - spdy_proxy_auth_enabled_.Init( - prefs::kSpdyProxyAuthEnabled, - GetOriginalProfilePrefs(), - base::Bind(&DataReductionProxySettingsAndroid::OnProxyEnabledPrefChange, - base::Unretained(this))); -} +DataReductionProxySettingsAndroid::~DataReductionProxySettingsAndroid() {} void DataReductionProxySettingsAndroid::InitDataReductionProxySettings( JNIEnv* env, jobject obj) { - // Disable the proxy if it is not allowed to be used. - if (!IsProxyAllowed()) - return; - - InitPrefMembers(); - - AddDefaultProxyBypassRules(); - net::NetworkChangeNotifier::AddIPAddressObserver(this); - - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); - - // Setting the kEnableSpdyProxyAuth switch has the same effect as enabling - // the feature via settings, in that once set, the preference will be sticky - // across instances of Chrome. Disabling the feature can only be done through - // the settings menu. - UMA_HISTOGRAM_ENUMERATION("SpdyProxyAuth.State", CHROME_STARTUP, - NUM_SPDY_PROXY_AUTH_STATE); - if (spdy_proxy_auth_enabled_.GetValue() || - command_line.HasSwitch(switches::kEnableSpdyProxyAuth)) { - MaybeActivateDataReductionProxy(true); - } else { - LOG(WARNING) << "SPDY proxy OFF at startup."; - } + DataReductionProxySettings::InitDataReductionProxySettings(); } void DataReductionProxySettingsAndroid::BypassHostPattern( JNIEnv* env, jobject obj, jstring pattern) { - AddHostPatternToBypass(ConvertJavaStringToUTF8(env, pattern)); + DataReductionProxySettings::AddHostPatternToBypass( + ConvertJavaStringToUTF8(env, pattern)); } void DataReductionProxySettingsAndroid::BypassURLPattern( JNIEnv* env, jobject obj, jstring pattern) { AddURLPatternToBypass(ConvertJavaStringToUTF8(env, pattern)); } +void DataReductionProxySettingsAndroid::AddURLPatternToBypass( + const std::string& pattern) { + pac_bypass_rules_.push_back( + StringPrintf("shExpMatch(%s, '%s')", "url", pattern.c_str())); +} + jboolean DataReductionProxySettingsAndroid::IsDataReductionProxyAllowed( JNIEnv* env, jobject obj) { - return IsProxyAllowed(); + return DataReductionProxySettings::IsDataReductionProxyAllowed(); } jboolean DataReductionProxySettingsAndroid::IsDataReductionProxyPromoAllowed( JNIEnv* env, jobject obj) { - return IsProxyPromoAllowed(); + return DataReductionProxySettings::IsDataReductionProxyPromoAllowed(); } ScopedJavaLocalRef<jstring> DataReductionProxySettingsAndroid::GetDataReductionProxyOrigin( JNIEnv* env, jobject obj) { - return ConvertUTF8ToJavaString(env, GetDataReductionProxyOriginInternal()); + return ConvertUTF8ToJavaString( + env, DataReductionProxySettings::GetDataReductionProxyOrigin()); } ScopedJavaLocalRef<jstring> DataReductionProxySettingsAndroid::GetDataReductionProxyAuth( JNIEnv* env, jobject obj) { - if (!IsProxyAllowed()) - return ConvertUTF8ToJavaString(env, std::string()); - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); - if (command_line.HasSwitch(switches::kSpdyProxyAuthOrigin)) { - // If an origin is provided via a switch, then only consider the value - // that is provided by a switch. Do not use the preprocessor constant. - // Don't expose SPDY_PROXY_AUTH_VALUE to a proxy passed in via the command - // line. - if (command_line.HasSwitch(switches::kSpdyProxyAuthValue)) { - return ConvertUTF8ToJavaString( - env, - command_line.GetSwitchValueASCII(switches::kSpdyProxyAuthValue)); - } - return ConvertUTF8ToJavaString(env, std::string()); - } -#if defined(SPDY_PROXY_AUTH_VALUE) - return ConvertUTF8ToJavaString(env, SPDY_PROXY_AUTH_VALUE); -#else - return ConvertUTF8ToJavaString(env, std::string()); -#endif + return ConvertUTF8ToJavaString( + env, DataReductionProxySettings::GetDataReductionProxyAuth()); } jboolean DataReductionProxySettingsAndroid::IsDataReductionProxyEnabled( JNIEnv* env, jobject obj) { - return spdy_proxy_auth_enabled_.GetValue(); + return DataReductionProxySettings::IsDataReductionProxyEnabled(); } jboolean DataReductionProxySettingsAndroid::IsDataReductionProxyManaged( JNIEnv* env, jobject obj) { - return spdy_proxy_auth_enabled_.IsManaged(); + return DataReductionProxySettings::IsDataReductionProxyManaged(); } void DataReductionProxySettingsAndroid::SetDataReductionProxyEnabled( JNIEnv* env, jobject obj, jboolean enabled) { - // Prevent configuring the proxy when it is not allowed to be used. - if (!IsProxyAllowed()) - return; - - spdy_proxy_auth_enabled_.SetValue(enabled); - OnProxyEnabledPrefChange(); + DataReductionProxySettings::SetDataReductionProxyEnabled(enabled); } jlong DataReductionProxySettingsAndroid::GetDataReductionLastUpdateTime( JNIEnv* env, jobject obj) { - PrefService* local_state = GetLocalStatePrefs(); - int64 last_update_internal = - local_state->GetInt64(prefs::kDailyHttpContentLengthLastUpdateDate); - base::Time last_update = base::Time::FromInternalValue(last_update_internal); - return static_cast<int64>(last_update.ToJsTime()); + return DataReductionProxySettings::GetDataReductionLastUpdateTime(); } base::android::ScopedJavaLocalRef<jobject> @@ -263,10 +137,11 @@ DataReductionProxySettingsAndroid::GetContentLengths(JNIEnv* env, int64 original_content_length; int64 received_content_length; int64 last_update_internal; - GetContentLengthsInternal(spdyproxy::kNumDaysInHistorySummary, - &original_content_length, - &received_content_length, - &last_update_internal); + DataReductionProxySettings::GetContentLengths( + spdyproxy::kNumDaysInHistorySummary, + &original_content_length, + &received_content_length, + &last_update_internal); return Java_ContentLengths_create(env, original_content_length, @@ -291,158 +166,37 @@ bool DataReductionProxySettingsAndroid::Register(JNIEnv* env) { return register_natives_impl_result; } -void DataReductionProxySettingsAndroid::OnURLFetchComplete( - const net::URLFetcher* source) { - net::URLRequestStatus status = source->GetStatus(); - if (status.status() == net::URLRequestStatus::FAILED && - status.error() == net::ERR_INTERNET_DISCONNECTED) { - return; - } - - std::string response; - source->GetResponseAsString(&response); - - if ("OK" == response.substr(0, 2)) { - DVLOG(1) << "The data reduction proxy is not blocked."; - - if (enabled_by_user_ && disabled_by_carrier_) { - // The user enabled the proxy, but sometime previously in the session, - // the network operator had blocked the proxy. Now that the network - // operator is unblocking it, configure it to the user's desires. - SetProxyPac(true, false); - } - disabled_by_carrier_ = false; - return; - } - DVLOG(1) << "The data reduction proxy is blocked."; - - if (enabled_by_user_ && !disabled_by_carrier_) { - // Disable the proxy. - SetProxyPac(false, false); - } - disabled_by_carrier_ = true; -} - -void DataReductionProxySettingsAndroid::OnIPAddressChanged() { - if (enabled_by_user_) { - DCHECK(IsProxyAllowed()); - ProbeWhetherDataReductionProxyIsAvailable(); - } -} - -void DataReductionProxySettingsAndroid::OnProxyEnabledPrefChange() { - if (!IsProxyAllowed()) - return; - MaybeActivateDataReductionProxy(false); +// Metrics methods -- obsolete; see crbug/241518 +void DataReductionProxySettingsAndroid::RecordDataReductionInit() { + UMA_HISTOGRAM_ENUMERATION("SpdyProxyAuth.State", CHROME_STARTUP, + NUM_SPDY_PROXY_AUTH_STATE); } void DataReductionProxySettingsAndroid::AddDefaultProxyBypassRules() { - // localhost - AddHostToBypass("localhost"); - AddHostPatternToBypass("localhost.*"); - AddHostToBypass("127.0.0.1"); - AddHostToBypass("::1"); - // TODO(bengr): revisit 192.168.*, 10.*, 172.16.0.0 - 172.31.255.255. The - // concern was that adding these and other rules would add to the processing - // time. + DataReductionProxySettings::AddDefaultProxyBypassRules(); // TODO(bengr): See http://crbug.com/169959. For some reason the data // reduction proxy is breaking the omnibox SearchProvider. Remove this rule // when this is fixed. AddURLPatternToBypass("http://www.google.com/complete/search*"); - - // Check for proxy availability - std::string proxy_check_url = GetProxyCheckURL(); - if (!proxy_check_url.empty()) { - AddURLPatternToBypass(GetProxyCheckURL()); - } -} - -void DataReductionProxySettingsAndroid::AddURLPatternToBypass( - const std::string& pattern) { - AddPatternToBypass("url", pattern); -} - -void DataReductionProxySettingsAndroid::AddHostPatternToBypass( - const std::string& pattern) { - AddPatternToBypass("host", pattern); -} - -void DataReductionProxySettingsAndroid::AddPatternToBypass( - const std::string& url_or_host, - const std::string& pattern) { - bypass_rules_.push_back( - StringPrintf("shExpMatch(%s, '%s')", - url_or_host.c_str(), pattern.c_str())); -} - -void DataReductionProxySettingsAndroid::AddHostToBypass( - const std::string& host) { - bypass_rules_.push_back( - StringPrintf("host == '%s'", host.c_str())); -} - -PrefService* DataReductionProxySettingsAndroid::GetOriginalProfilePrefs() { - return g_browser_process->profile_manager()->GetDefaultProfile()-> - GetOriginalProfile()->GetPrefs(); -} - -PrefService* DataReductionProxySettingsAndroid::GetLocalStatePrefs() { - return g_browser_process->local_state(); -} - -void DataReductionProxySettingsAndroid::ResetDataReductionStatistics() { - PrefService* prefs = GetLocalStatePrefs(); - if (!prefs) - return; - ListPrefUpdate original_update(prefs, prefs::kDailyHttpOriginalContentLength); - ListPrefUpdate received_update(prefs, prefs::kDailyHttpReceivedContentLength); - original_update->Clear(); - received_update->Clear(); - for (size_t i = 0; i < spdyproxy::kNumDaysInHistory; ++i) { - original_update->AppendString(base::Int64ToString(0)); - received_update->AppendString(base::Int64ToString(0)); - } -} - -void DataReductionProxySettingsAndroid::MaybeActivateDataReductionProxy( - bool at_startup) { - PrefService* prefs = GetOriginalProfilePrefs(); - - if (spdy_proxy_auth_enabled_.GetValue() && - !prefs->GetBoolean(prefs::kSpdyProxyAuthWasEnabledBefore)) { - prefs->SetBoolean(prefs::kSpdyProxyAuthWasEnabledBefore, true); - ResetDataReductionStatistics(); - } - - std::string spdy_proxy_origin = GetDataReductionProxyOriginHostPort(); - - // Configure use of the data reduction proxy if it is enabled and the proxy - // origin is non-empty. - enabled_by_user_= - spdy_proxy_auth_enabled_.GetValue() && !spdy_proxy_origin.empty(); - SetProxyPac(enabled_by_user_ && !disabled_by_carrier_, at_startup); - - // Check if the proxy has been disabled explicitly by the carrier. - if (enabled_by_user_) - ProbeWhetherDataReductionProxyIsAvailable(); } -void DataReductionProxySettingsAndroid::SetProxyPac(bool enable_spdy_proxy, - bool at_startup) { - PrefService* prefs = GetOriginalProfilePrefs(); - DCHECK(prefs); +void DataReductionProxySettingsAndroid::SetProxyConfigs(bool enabled, + bool at_startup) { // Keys duplicated from proxy_config_dictionary.cc // TODO(bengr): Move these to proxy_config_dictionary.h and reuse them here. const char kProxyMode[] = "mode"; const char kProxyPacURL[] = "pac_url"; - const char kAtStartup[] = "at startup"; - const char kByUser[] = "by user action"; + const char kProxyBypassList[] = "bypass_list"; + + LogProxyState(enabled, at_startup); + PrefService* prefs = GetOriginalProfilePrefs(); + DCHECK(prefs); DictionaryPrefUpdate update(prefs, prefs::kProxy); - DictionaryValue* dict = update.Get(); - if (enable_spdy_proxy) { - LOG(WARNING) << "SPDY proxy ON " << (at_startup ? kAtStartup : kByUser); + base::DictionaryValue* dict = update.Get(); + // TODO(marq): All of the UMA in here are obsolete. + if (enabled) { // Convert to a data URI and update the PAC settings. std::string base64_pac; base::Base64Encode(GetProxyPacScript(), &base64_pac); @@ -452,135 +206,72 @@ void DataReductionProxySettingsAndroid::SetProxyPac(bool enable_spdy_proxy, base64_pac); dict->SetString(kProxyMode, ProxyModeToString(ProxyPrefs::MODE_PAC_SCRIPT)); + dict->SetString(kProxyBypassList, JoinString(BypassRules(), ", ")); if (at_startup) { UMA_HISTOGRAM_ENUMERATION("SpdyProxyAuth.State", SPDY_PROXY_AUTH_ON_AT_STARTUP, NUM_SPDY_PROXY_AUTH_STATE); - } else if (!has_turned_on_) { + } else if (!DataReductionProxySettings::HasTurnedOn()) { // SPDY proxy auth is turned on by user action for the first time in // this session. UMA_HISTOGRAM_ENUMERATION("SpdyProxyAuth.State", SPDY_PROXY_AUTH_ON_BY_USER, NUM_SPDY_PROXY_AUTH_STATE); - has_turned_on_ = true; + DataReductionProxySettings::SetHasTurnedOn(); } } else { - LOG(WARNING) << "SPDY proxy OFF " << (at_startup ? kAtStartup : kByUser); dict->SetString(kProxyMode, ProxyModeToString(ProxyPrefs::MODE_SYSTEM)); dict->SetString(kProxyPacURL, ""); + dict->SetString(kProxyBypassList, ""); - if (!at_startup && !has_turned_off_) { + if (!at_startup && !DataReductionProxySettings::HasTurnedOff()) { UMA_HISTOGRAM_ENUMERATION("SpdyProxyAuth.State", SPDY_PROXY_AUTH_OFF_BY_USER, NUM_SPDY_PROXY_AUTH_STATE); - has_turned_off_ = true; + DataReductionProxySettings::SetHasTurnedOff(); } } } ScopedJavaLocalRef<jlongArray> DataReductionProxySettingsAndroid::GetDailyContentLengths( - JNIEnv* env, const char* pref_name) { + JNIEnv* env, const char* pref_name) { jlongArray result = env->NewLongArray(spdyproxy::kNumDaysInHistory); - PrefService* local_state = GetLocalStatePrefs(); - if (!local_state) - return ScopedJavaLocalRef<jlongArray>(env, result); - - const ListValue* list_value = local_state->GetList(pref_name); - if (list_value->GetSize() != spdyproxy::kNumDaysInHistory) - return ScopedJavaLocalRef<jlongArray>(env, result); - - jlong jval[spdyproxy::kNumDaysInHistory]; - for (size_t i = 0; i < spdyproxy::kNumDaysInHistory; ++i) { - jval[i] = GetInt64PrefValue(*list_value, i); - } - env->SetLongArrayRegion(result, 0, spdyproxy::kNumDaysInHistory, jval); - return ScopedJavaLocalRef<jlongArray>(env, result); -} -void DataReductionProxySettingsAndroid::GetContentLengthsInternal( - unsigned int days, - int64* original_content_length, - int64* received_content_length, - int64* last_update_time) { - DCHECK_LE(days, spdyproxy::kNumDaysInHistory); - PrefService* local_state = GetLocalStatePrefs(); - if (!local_state) { - *original_content_length = 0L; - *received_content_length = 0L; - *last_update_time = 0L; - return; - } + DataReductionProxySettings::ContentLengthList lengths = + DataReductionProxySettings::GetDailyContentLengths(pref_name); - const ListValue* original_list = - local_state->GetList(prefs::kDailyHttpOriginalContentLength); - const ListValue* received_list = - local_state->GetList(prefs::kDailyHttpReceivedContentLength); - - if (original_list->GetSize() != spdyproxy::kNumDaysInHistory || - received_list->GetSize() != spdyproxy::kNumDaysInHistory) { - *original_content_length = 0L; - *received_content_length = 0L; - *last_update_time = 0L; - return; + if (!lengths.empty()) { + DCHECK_EQ(lengths.size(), spdyproxy::kNumDaysInHistory); + env->SetLongArrayRegion(result, 0, lengths.size(), &lengths[0]); + return ScopedJavaLocalRef<jlongArray>(env, result); } - int64 orig = 0L; - int64 recv = 0L; - // Include days from the end of the list going backwards. - for (size_t i = spdyproxy::kNumDaysInHistory - days; - i < spdyproxy::kNumDaysInHistory; ++i) { - orig += GetInt64PrefValue(*original_list, i); - recv += GetInt64PrefValue(*received_list, i); - } - *original_content_length = orig; - *received_content_length = recv; - *last_update_time = - local_state->GetInt64(prefs::kDailyHttpContentLengthLastUpdateDate); -} - -net::URLFetcher* DataReductionProxySettingsAndroid::GetURLFetcher() { - std::string url = GetProxyCheckURL(); - if (url.empty()) - return NULL; - net::URLFetcher* fetcher = net::URLFetcher::Create(GURL(url), - net::URLFetcher::GET, - this); - fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE); - Profile* profile = g_browser_process->profile_manager()-> - GetDefaultProfile(); - fetcher->SetRequestContext(profile->GetRequestContext()); - // Configure max retries to be at most kMaxRetries times for 5xx errors. - static const int kMaxRetries = 5; - fetcher->SetMaxRetriesOn5xx(kMaxRetries); - return fetcher; -} - -void -DataReductionProxySettingsAndroid::ProbeWhetherDataReductionProxyIsAvailable() { - net::URLFetcher* fetcher = GetURLFetcher(); - if (!fetcher) - return; - fetcher_.reset(fetcher); - fetcher_->Start(); + return ScopedJavaLocalRef<jlongArray>(env, result); } // TODO(bengr): Replace with our own ProxyResolver. std::string DataReductionProxySettingsAndroid::GetProxyPacScript() { - std::string bypass_clause = "(" + JoinString(bypass_rules_, ") || (") + ")"; + // Compose the PAC-only bypass code; these will be URL patterns that + // are matched by regular expression. Host bypasses are handled outside + // of the PAC file using the regular proxy bypass list configs. + std::string bypass_clause = + "(" + JoinString(pac_bypass_rules_, ") || (") + ")"; // Generate a proxy PAC that falls back to direct loading when the proxy is // unavailable and only process HTTP traffic. (With a statically configured // proxy, proxy failures will simply result in a connection error presented to // users.) + std::string proxy_host = + DataReductionProxySettings::GetDataReductionProxyOriginHostPort(); std::string pac = "function FindProxyForURL(url, host) {" " if (" + bypass_clause + ") {" " return 'DIRECT';" " } " " if (url.substring(0, 5) == 'http:') {" - " return 'HTTPS " + GetDataReductionProxyOriginHostPort() + + " return 'HTTPS " + proxy_host + "; DIRECT';" " }" " return 'DIRECT';" @@ -594,5 +285,3 @@ static jint Init(JNIEnv* env, jobject obj) { new DataReductionProxySettingsAndroid(env, obj); return reinterpret_cast<jint>(settings); } - - diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.h b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.h index 3e28e736e1..10a00202aa 100644 --- a/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.h +++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.h @@ -13,185 +13,82 @@ #include "base/gtest_prod_util.h" #include "base/memory/scoped_ptr.h" #include "base/prefs/pref_member.h" -#include "net/base/network_change_notifier.h" -#include "net/url_request/url_fetcher_delegate.h" +#include "chrome/browser/net/spdyproxy/data_reduction_proxy_settings.h" -using base::android::ScopedJavaLocalRef; - -class PrefService; - -namespace net { -class URLFetcher; -} - -namespace spdyproxy { - -// The number of days of bandwidth usage statistics that are tracked. -const unsigned int kNumDaysInHistory = 60; - -// The number of days of bandwidth usage statistics that are presented. -const unsigned int kNumDaysInHistorySummary = 30; -COMPILE_ASSERT(kNumDaysInHistorySummary <= kNumDaysInHistory, - DataReductionProxySettings_summary_too_long); +using base::android::ScopedJavaLocalRef; -} // namespace spdyproxy // Central point for configuring the data reduction proxy on Android. // This object lives on the UI thread and all of its methods are expected to // be called from there. -class DataReductionProxySettingsAndroid - : public net::URLFetcherDelegate, - public net::NetworkChangeNotifier::IPAddressObserver { +class DataReductionProxySettingsAndroid : public DataReductionProxySettings { public: DataReductionProxySettingsAndroid(JNIEnv* env, jobject obj); virtual ~DataReductionProxySettingsAndroid(); void InitDataReductionProxySettings(JNIEnv* env, jobject obj); - // Add a host or URL pattern to bypass the proxy, respectively. Wildcards - // should be compatible with the JavaScript function shExpMatch, which can be - // used in proxy PAC resolution. These function must only be called before the - // proxy is used. + void BypassHostPattern(JNIEnv* env, jobject obj, jstring pattern); + // Add a URL pattern to bypass the proxy. Wildcards + // should be compatible with the JavaScript function shExpMatch, which can be + // used in proxy PAC resolution. These functions must only be called before + // the proxy is used. void BypassURLPattern(JNIEnv* env, jobject obj, jstring pattern); - // Returns true if the data reduction proxy is allowed to be used on this - // instance of Chrome. This could return false, for example, if this instance - // is not part of the field trial, or if the proxy name is not configured - // via gyp. - jboolean IsDataReductionProxyAllowed(JNIEnv* env, jobject obj); + virtual void AddURLPatternToBypass(const std::string& pattern) OVERRIDE; - // Returns true if a screen promoting the data reduction proxy is allowed to - // be shown. Logic that decides when to show the promo should check its - // availability. This would return false if not part of a separate field - // trial that governs the use of the promotion. + // JNI wrapper interfaces to the indentically-named superclass methods. + jboolean IsDataReductionProxyAllowed(JNIEnv* env, jobject obj); jboolean IsDataReductionProxyPromoAllowed(JNIEnv* env, jobject obj); - - // Returns the origin of the data reduction proxy. ScopedJavaLocalRef<jstring> GetDataReductionProxyOrigin(JNIEnv* env, jobject obj); - - // Returns a configuration string for the proxy. ScopedJavaLocalRef<jstring> GetDataReductionProxyAuth(JNIEnv* env, jobject obj); - - // Returns true if the proxy is enabled. jboolean IsDataReductionProxyEnabled(JNIEnv* env, jobject obj); - - // Returns true if the proxy is managed by an adminstrator's policy. jboolean IsDataReductionProxyManaged(JNIEnv* env, jobject obj); - - // Enables or disables the data reduction proxy. If a probe URL is available, - // and a probe request fails at some point, the proxy won't be used until a - // probe succeeds. void SetDataReductionProxyEnabled(JNIEnv* env, jobject obj, jboolean enabled); - // Returns the time in microseconds that the last update was made to the - // daily original and received content lengths. jlong GetDataReductionLastUpdateTime(JNIEnv* env, jobject obj); - - // Return a Java |ContentLengths| object containing the total number of bytes - // of all received content, before and after compression by the data - // reduction proxy. - base::android::ScopedJavaLocalRef<jobject> GetContentLengths(JNIEnv* env, - jobject obj); - - // Returns an array containing the total size of all HTTP content that was - // received over the last |kNumDaysInHistory| before any compression by the - // data reduction proxy. Each element in the array contains one day of data. ScopedJavaLocalRef<jlongArray> GetDailyOriginalContentLengths(JNIEnv* env, jobject obj); - - // Returns an array containing the aggregate received HTTP content in the last - // |kNumDaysInHistory| days. ScopedJavaLocalRef<jlongArray> GetDailyReceivedContentLengths(JNIEnv* env, jobject obj); + // Return a Java |ContentLengths| object wrapping the results of a call to + // DataReductionProxySettings::GetContentLengths. + base::android::ScopedJavaLocalRef<jobject> GetContentLengths(JNIEnv* env, + jobject obj); + // Registers the native methods to be call from Java. static bool Register(JNIEnv* env); - // net::URLFetcherDelegate: - virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; - protected: - void InitPrefMembers(); - virtual net::URLFetcher* GetURLFetcher(); - virtual PrefService* GetOriginalProfilePrefs(); - virtual PrefService* GetLocalStatePrefs(); + // DataReductionProxySettings overrides. + virtual void AddDefaultProxyBypassRules() OVERRIDE; + + // Configures the proxy settings by generating a data URL containing a PAC + // file. + virtual void SetProxyConfigs(bool enabled, bool at_startup) OVERRIDE; + + virtual void RecordDataReductionInit() OVERRIDE; private: friend class DataReductionProxySettingsAndroidTest; FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsAndroidTest, - TestBypassRules); - FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsAndroidTest, - TestResetDataReductionStatistics); - FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsAndroidTest, - TestIsProxyEnabledOrManaged); + TestBypassPACRules); FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsAndroidTest, TestSetProxyPac); FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsAndroidTest, TestGetDailyContentLengths); - FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsAndroidTest, - TestContentLengthsInternal); - FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsAndroidTest, - TestMaybeActivateDataReductionProxy); - FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsAndroidTest, - TestOnIPAddressChanged); - FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsAndroidTest, - TestOnProxyEnabledPrefChange); - FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsAndroidTest, - TestInitDataReductionProxyOn); - FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsAndroidTest, - TestInitDataReductionProxyOff); - // NetworkChangeNotifier::IPAddressObserver: - virtual void OnIPAddressChanged() OVERRIDE; - - void OnProxyEnabledPrefChange(); - - void AddDefaultProxyBypassRules(); - void AddURLPatternToBypass(const std::string& pattern); - void AddHostPatternToBypass(const std::string& pattern); - void AddPatternToBypass(const std::string& url_or_host, - const std::string& pattern); - void AddHostToBypass(const std::string& host); - - void ResetDataReductionStatistics(); - - void MaybeActivateDataReductionProxy(bool at_startup); - void SetProxyPac(bool enable_spdy_proxy, bool at_startup); - ScopedJavaLocalRef<jlongArray> GetDailyContentLengths(JNIEnv* env, const char* pref_name); - void GetContentLengthsInternal(unsigned int days, - int64* original_content_length, - int64* received_content_length, - int64* last_update_time); - - // Requests the proxy probe URL, if one is set. If unable to do so, disables - // the proxy, if enabled. Otherwise enables the proxy if disabled by a probe - // failure. - void ProbeWhetherDataReductionProxyIsAvailable(); - std::string GetProxyPacScript(); - std::vector<std::string> bypass_rules_; - - // Indicate whether a user has turned on the data reduction proxy previously - // in this session. - bool has_turned_on_; - - // Indicate whether a user has turned off the data reduction proxy previously - // in this session. - bool has_turned_off_; - - bool disabled_by_carrier_; - bool enabled_by_user_; - - scoped_ptr<net::URLFetcher> fetcher_; - BooleanPrefMember spdy_proxy_auth_enabled_; + std::vector<std::string> pac_bypass_rules_; DISALLOW_COPY_AND_ASSIGN(DataReductionProxySettingsAndroid); }; diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_ios.cc b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_ios.cc new file mode 100644 index 0000000000..6a0df92e7a --- /dev/null +++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_ios.cc @@ -0,0 +1,9 @@ +// Copyright 2013 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/net/spdyproxy/data_reduction_proxy_settings_ios.h" + +DataReductionProxySettingsIOS::DataReductionProxySettingsIOS() + : DataReductionProxySettings() { +} diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_ios.h b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_ios.h new file mode 100644 index 0000000000..be5bf42141 --- /dev/null +++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_ios.h @@ -0,0 +1,22 @@ +// Copyright 2013 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_NET_SPDYPROXY_DATA_REDUCTION_PROXY_SETTINGS_IOS_H_ +#define CHROME_BROWSER_NET_SPDYPROXY_DATA_REDUCTION_PROXY_SETTINGS_IOS_H_ + +#include "chrome/browser/net/spdyproxy/data_reduction_proxy_settings.h" + +// Central point for configuring the data reduction proxy on iOS. +// This object lives on the UI thread and all of its methods are expected to +// be called from there. +class DataReductionProxySettingsIOS : DataReductionProxySettings { + public: + DataReductionProxySettingsIOS(); + virtual ~DataReductionProxySettingsIOS() {} + + private: + DISALLOW_COPY_AND_ASSIGN(DataReductionProxySettingsIOS); +}; + +#endif // CHROME_BROWSER_NET_SPDYPROXY_DATA_REDUCTION_PROXY_SETTINGS_IOS_H_ diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_unittest.cc b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_unittest.cc new file mode 100644 index 0000000000..5721de91f2 --- /dev/null +++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_unittest.cc @@ -0,0 +1,418 @@ +// Copyright 2013 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/net/spdyproxy/data_reduction_proxy_settings_unittest.h" + +#include "base/command_line.h" +#include "base/metrics/field_trial.h" +#include "base/prefs/pref_registry_simple.h" +#include "base/prefs/pref_service.h" +#include "base/prefs/testing_pref_service.h" +#include "base/strings/string_number_conversions.h" +#include "chrome/browser/net/spdyproxy/data_reduction_proxy_settings.h" +#include "chrome/browser/prefs/proxy_prefs.h" +#include "chrome/browser/prefs/scoped_user_pref_update.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/metrics/variations/variations_util.h" +#include "chrome/common/pref_names.h" +#include "components/variations/entropy_provider.h" +#include "net/url_request/test_url_fetcher_factory.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +const char kDataReductionProxyOrigin[] = "https://foo:443/"; +const char kDataReductionProxyAuth[] = "12345"; + +const char kProbeURLWithOKResponse[] = "http://ok.org/"; +const char kProbeURLWithBadResponse[] = "http://bad.org/"; +const char kProbeURLWithNoResponse[] = "http://no.org/"; + +class TestDataReductionProxySettings + : public DataReductionProxySettings { + public: + TestDataReductionProxySettings(PrefService* profile_prefs, + PrefService* local_state_prefs) + : DataReductionProxySettings(), + success_(false), + fake_fetcher_request_count_(0), + profile_prefs_(profile_prefs), + local_state_prefs_(local_state_prefs) { + } + + // DataReductionProxySettings implementation: + virtual net::URLFetcher* GetURLFetcher() OVERRIDE { + if (test_url_.empty()) + return NULL; + net::URLFetcher* fetcher = + new net::FakeURLFetcher(GURL(test_url_), this, response_, success_); + fake_fetcher_request_count_++; + return fetcher; + } + + virtual PrefService* GetOriginalProfilePrefs() OVERRIDE { + return profile_prefs_; + } + + virtual PrefService* GetLocalStatePrefs() OVERRIDE { + return local_state_prefs_; + } + + void set_probe_result(const std::string& test_url, + const std::string& response, + bool success) { + test_url_ = test_url; + response_ = response; + success_ = success; + } + + const int fake_fetcher_request_count() { + return fake_fetcher_request_count_; + } + + private: + std::string test_url_; + std::string response_; + bool success_; + int fake_fetcher_request_count_; + PrefService* profile_prefs_; + PrefService* local_state_prefs_; +}; + +DataReductionProxySettingsTestBase::DataReductionProxySettingsTestBase() + : testing::Test() { +} + +DataReductionProxySettingsTestBase::~DataReductionProxySettingsTestBase() {} + +void DataReductionProxySettingsTestBase::AddProxyToCommandLine() { + CommandLine::ForCurrentProcess()->AppendSwitchASCII( + switches::kSpdyProxyAuthOrigin, kDataReductionProxyOrigin); + CommandLine::ForCurrentProcess()->AppendSwitchASCII( + switches::kSpdyProxyAuthValue, kDataReductionProxyAuth); +} + +// testing::Test implementation: +void DataReductionProxySettingsTestBase::SetUp() { + PrefRegistrySimple* registry = pref_service_.registry(); + registry->RegisterListPref(prefs::kDailyHttpOriginalContentLength); + registry->RegisterListPref(prefs::kDailyHttpReceivedContentLength); + registry->RegisterInt64Pref( + prefs::kDailyHttpContentLengthLastUpdateDate, 0L); + registry->RegisterDictionaryPref(prefs::kProxy); + registry->RegisterBooleanPref(prefs::kSpdyProxyAuthEnabled, false); + registry->RegisterBooleanPref(prefs::kSpdyProxyAuthWasEnabledBefore, false); + ResetSettings(); + + ListPrefUpdate original_update(&pref_service_, + prefs::kDailyHttpOriginalContentLength); + ListPrefUpdate received_update(&pref_service_, + prefs::kDailyHttpReceivedContentLength); + for (int64 i = 0; i < spdyproxy::kNumDaysInHistory; i++) { + original_update->Insert(0, new StringValue(base::Int64ToString(2 * i))); + received_update->Insert(0, new StringValue(base::Int64ToString(i))); + } + last_update_time_ = base::Time::Now().LocalMidnight(); + pref_service_.SetInt64( + prefs::kDailyHttpContentLengthLastUpdateDate, + last_update_time_.ToInternalValue()); +} + +void DataReductionProxySettingsTestBase::CheckProxyPref( + const std::string& expected_servers, + const std::string& expected_mode) { + const DictionaryValue* dict = pref_service_.GetDictionary(prefs::kProxy); + std::string mode; + std::string server; + dict->GetString("mode", &mode); + ASSERT_EQ(expected_mode, mode); + dict->GetString("server", &server); + ASSERT_EQ(expected_servers, server); +} + +void DataReductionProxySettingsTestBase::CheckProxyConfigs( + bool expected_enabled) { + if (expected_enabled) { + std::string servers = + "http=" + Settings()->GetDataReductionProxyOrigin() + ",direct://;"; + CheckProxyPref(servers, + ProxyModeToString(ProxyPrefs::MODE_FIXED_SERVERS)); + } else { + CheckProxyPref(std::string(), ProxyModeToString(ProxyPrefs::MODE_SYSTEM)); + } +} + +void DataReductionProxySettingsTestBase::CheckProbe(bool initially_enabled, + const std::string& probe_url, + const std::string& response, + bool request_success, + bool expected_enabled) { + pref_service_.SetBoolean(prefs::kSpdyProxyAuthEnabled, initially_enabled); + SetProbeResult(probe_url, response, request_success); + Settings()->MaybeActivateDataReductionProxy(false); + base::MessageLoop::current()->RunUntilIdle(); + CheckProxyConfigs(expected_enabled); +} + +void DataReductionProxySettingsTestBase::CheckProbeOnIPChange( + const std::string& probe_url, + const std::string& response, + bool request_success, + bool expected_enabled) { + SetProbeResult(probe_url, response, request_success); + Settings()->OnIPAddressChanged(); + base::MessageLoop::current()->RunUntilIdle(); + CheckProxyConfigs(expected_enabled); +} + +void DataReductionProxySettingsTestBase::CheckOnPrefChange( + bool enabled, + const std::string& probe_url, + const std::string& response, + bool request_success, + bool expected_enabled) { + SetProbeResult(probe_url, response, request_success); + pref_service_.SetBoolean(prefs::kSpdyProxyAuthEnabled, enabled); + base::MessageLoop::current()->RunUntilIdle(); + CheckProxyConfigs(expected_enabled); +} + +void DataReductionProxySettingsTestBase::CheckInitDataReductionProxy( + bool enabled_at_startup) { + AddProxyToCommandLine(); + base::MessageLoop loop(base::MessageLoop::TYPE_UI); + pref_service_.SetBoolean(prefs::kSpdyProxyAuthEnabled, enabled_at_startup); + SetProbeResult(kProbeURLWithOKResponse, "OK", true); + Settings()->InitDataReductionProxySettings(); + base::MessageLoop::current()->RunUntilIdle(); + if (enabled_at_startup) { + CheckProxyConfigs(enabled_at_startup); + } else { + // This presumes the proxy preference hadn't been set up by Chrome. + CheckProxyPref(std::string(), std::string()); + } +} + +class DataReductionProxySettingsTest: + public DataReductionProxySettingsTestBase { + public: + virtual void ResetSettings() OVERRIDE { + settings_.reset(new TestDataReductionProxySettings(&pref_service_, + &pref_service_)); + } + + virtual TestDataReductionProxySettings* Settings() OVERRIDE { + return settings_.get(); + } + + virtual void SetProbeResult(const std::string& test_url, + const std::string& response, + bool success) OVERRIDE { + settings_->set_probe_result(test_url, response, success); + } + + scoped_ptr<TestDataReductionProxySettings> settings_; +}; + +TEST_F(DataReductionProxySettingsTest, TestGetDataReductionProxyOrigin) { + AddProxyToCommandLine(); + // SetUp() adds the origin to the command line, which should be returned here. + std::string result = settings_->GetDataReductionProxyOrigin(); + EXPECT_EQ(kDataReductionProxyOrigin, result); +} + +TEST_F(DataReductionProxySettingsTest, TestGetDataReductionProxyAuth) { + AddProxyToCommandLine(); + // SetUp() adds the auth string to the command line, which should be returned + // here. + std::string result = settings_->GetDataReductionProxyAuth(); + EXPECT_EQ(kDataReductionProxyAuth, result); +} + +// Test that the auth value set by preprocessor directive is not returned +// when an origin is set via a switch. This test only does anything useful in +// Chrome builds. +TEST_F(DataReductionProxySettingsTest, + TestGetDataReductionProxyAuthWithOriginSetViaSwitch) { + CommandLine::ForCurrentProcess()->AppendSwitchASCII( + switches::kSpdyProxyAuthOrigin, kDataReductionProxyOrigin); + std::string result = settings_->GetDataReductionProxyAuth(); + EXPECT_EQ("", result); +} + +TEST_F(DataReductionProxySettingsTest, TestIsProxyEnabledOrManaged) { + settings_->InitPrefMembers(); + EXPECT_FALSE(settings_->IsDataReductionProxyEnabled()); + EXPECT_FALSE(settings_->IsDataReductionProxyManaged()); + + pref_service_.SetBoolean(prefs::kSpdyProxyAuthEnabled, true); + EXPECT_TRUE(settings_->IsDataReductionProxyEnabled()); + EXPECT_FALSE(settings_->IsDataReductionProxyManaged()); + + pref_service_.SetManagedPref(prefs::kSpdyProxyAuthEnabled, + base::Value::CreateBooleanValue(true)); + EXPECT_TRUE(settings_->IsDataReductionProxyEnabled()); + EXPECT_TRUE(settings_->IsDataReductionProxyManaged()); +} + +TEST_F(DataReductionProxySettingsTest, TestResetDataReductionStatistics) { + int64 original_content_length; + int64 received_content_length; + int64 last_update_time; + settings_->ResetDataReductionStatistics(); + settings_->GetContentLengths(spdyproxy::kNumDaysInHistory, + &original_content_length, + &received_content_length, + &last_update_time); + EXPECT_EQ(0L, original_content_length); + EXPECT_EQ(0L, received_content_length); + EXPECT_EQ(last_update_time_.ToInternalValue(), last_update_time); +} + +TEST_F(DataReductionProxySettingsTest, TestContentLengths) { + int64 original_content_length; + int64 received_content_length; + int64 last_update_time; + + // Request |kNumDaysInHistory| days. + settings_->GetContentLengths(spdyproxy::kNumDaysInHistory, + &original_content_length, + &received_content_length, + &last_update_time); + const unsigned int days = spdyproxy::kNumDaysInHistory; + // Received content length history values are 0 to |kNumDaysInHistory - 1|. + int64 expected_total_received_content_length = (days - 1L) * days / 2; + // Original content length history values are 0 to + // |2 * (kNumDaysInHistory - 1)|. + long expected_total_original_content_length = (days - 1L) * days; + EXPECT_EQ(expected_total_original_content_length, original_content_length); + EXPECT_EQ(expected_total_received_content_length, received_content_length); + EXPECT_EQ(last_update_time_.ToInternalValue(), last_update_time); + + // Request |kNumDaysInHistory - 1| days. + settings_->GetContentLengths(spdyproxy::kNumDaysInHistory - 1, + &original_content_length, + &received_content_length, + &last_update_time); + expected_total_received_content_length -= (days - 1); + expected_total_original_content_length -= 2 * (days - 1); + EXPECT_EQ(expected_total_original_content_length, original_content_length); + EXPECT_EQ(expected_total_received_content_length, received_content_length); + + // Request 0 days. + settings_->GetContentLengths(0, + &original_content_length, + &received_content_length, + &last_update_time); + expected_total_received_content_length = 0; + expected_total_original_content_length = 0; + EXPECT_EQ(expected_total_original_content_length, original_content_length); + EXPECT_EQ(expected_total_received_content_length, received_content_length); + + // Request 1 day. First day had 0 bytes so should be same as 0 days. + settings_->GetContentLengths(1, + &original_content_length, + &received_content_length, + &last_update_time); + EXPECT_EQ(expected_total_original_content_length, original_content_length); + EXPECT_EQ(expected_total_received_content_length, received_content_length); +} + +TEST_F(DataReductionProxySettingsTest, TestMaybeActivateDataReductionProxy) { + AddProxyToCommandLine(); + settings_->InitPrefMembers(); + // TODO(bengr): Test enabling/disabling while a probe is outstanding. + base::MessageLoop loop(base::MessageLoop::TYPE_UI); + // The proxy is enabled initially. + // Request succeeded but with bad response, expect proxy to be disabled. + CheckProbe(true, kProbeURLWithBadResponse, "Bad", true, false); + // Request succeeded with valid response, expect proxy to be enabled. + CheckProbe(true, kProbeURLWithOKResponse, "OK", true, true); + // Request failed, expect proxy to be disabled. + CheckProbe(true, kProbeURLWithNoResponse, "", false, false); + + // The proxy is disabled initially. Probes should not be emitted to change + // state. + EXPECT_EQ(3, settings_->fake_fetcher_request_count()); + CheckProbe(false, kProbeURLWithOKResponse, "OK", true, false); + EXPECT_EQ(3, settings_->fake_fetcher_request_count()); +} + +TEST_F(DataReductionProxySettingsTest, TestOnIPAddressChanged) { + AddProxyToCommandLine(); + base::MessageLoop loop(base::MessageLoop::TYPE_UI); + // The proxy is enabled initially. + settings_->enabled_by_user_ = true; + settings_->SetProxyConfigs(true, true); + // IP address change triggers a probe that succeeds. Proxy remains enabled. + CheckProbeOnIPChange(kProbeURLWithOKResponse, "OK", true, true); + // IP address change triggers a probe that fails. Proxy is disabled. + CheckProbeOnIPChange(kProbeURLWithBadResponse, "Bad", true, false); + // IP address change triggers a probe that fails. Proxy remains disabled. + CheckProbeOnIPChange(kProbeURLWithBadResponse, "Bad", true, false); + // IP address change triggers a probe that succeed. Proxy is enabled. + CheckProbeOnIPChange(kProbeURLWithBadResponse, "OK", true, true); + EXPECT_EQ(4, settings_->fake_fetcher_request_count()); +} + +TEST_F(DataReductionProxySettingsTest, TestOnProxyEnabledPrefChange) { + AddProxyToCommandLine(); + settings_->InitPrefMembers(); + base::MessageLoop loop(base::MessageLoop::TYPE_UI); + // The proxy is enabled initially. + settings_->enabled_by_user_ = true; + settings_->SetProxyConfigs(true, true); + // The pref is disabled, so correspondingly should be the proxy. + CheckOnPrefChange(false, kProbeURLWithOKResponse, "OK", true, false); + // The pref is enabled, so correspondingly should be the proxy. + CheckOnPrefChange(true, kProbeURLWithOKResponse, "OK", true, true); + EXPECT_EQ(1, settings_->fake_fetcher_request_count()); +} + +TEST_F(DataReductionProxySettingsTest, TestInitDataReductionProxyOn) { + CheckInitDataReductionProxy(true); +} + +TEST_F(DataReductionProxySettingsTest, TestInitDataReductionProxyOff) { + CheckInitDataReductionProxy(false); +} + +TEST_F(DataReductionProxySettingsTest, TestGetDailyContentLengths) { + DataReductionProxySettings::ContentLengthList result = + settings_->GetDailyContentLengths(prefs::kDailyHttpOriginalContentLength); + + ASSERT_FALSE(result.empty()); + ASSERT_EQ(spdyproxy::kNumDaysInHistory, result.size()); + + for (size_t i = 0; i < spdyproxy::kNumDaysInHistory; ++i) { + long expected_length = + static_cast<long>((spdyproxy::kNumDaysInHistory - 1 - i) * 2); + ASSERT_EQ(expected_length, result[i]); + } +} + +TEST_F(DataReductionProxySettingsTest, TestBypassList) { + settings_->AddHostPatternToBypass("http://www.google.com"); + settings_->AddHostPatternToBypass("fefe:13::abc/33"); + settings_->AddURLPatternToBypass("foo.org/images/*"); + settings_->AddURLPatternToBypass("http://foo.com/*"); + settings_->AddURLPatternToBypass("http://baz.com:22/bar/*"); + settings_->AddURLPatternToBypass("http://*bat.com/bar/*"); + + std::string expected[] = { + "http://www.google.com", + "fefe:13::abc/33", + "foo.org", + "http://foo.com", + "http://baz.com:22", + "http://*bat.com" + }; + + ASSERT_EQ(settings_->bypass_rules_.size(), 6u); + int i = 0; + for (std::vector<std::string>::iterator it = settings_->bypass_rules_.begin(); + it != settings_->bypass_rules_.end(); ++it) { + EXPECT_EQ(expected[i++], *it); + } +} + diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_unittest.h b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_unittest.h new file mode 100644 index 0000000000..ea3e085169 --- /dev/null +++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_unittest.h @@ -0,0 +1,55 @@ +// Copyright 2013 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_NET_SPDYPROXY_DATA_REDUCTION_PROXY_SETTINGS_UNITTEST_H_ +#define CHROME_BROWSER_NET_SPDYPROXY_DATA_REDUCTION_PROXY_SETTINGS_UNITTEST_H_ + + +#include "base/metrics/field_trial.h" +#include "base/prefs/testing_pref_service.h" +#include "chrome/browser/net/spdyproxy/data_reduction_proxy_settings.h" +#include "testing/gtest/include/gtest/gtest.h" + +class PrefService; +class TestingPrefServiceSimple; + +class DataReductionProxySettingsTestBase : public testing::Test { + public: + DataReductionProxySettingsTestBase(); + virtual ~DataReductionProxySettingsTestBase(); + + void AddProxyToCommandLine(); + virtual void ResetSettings() = 0; + virtual DataReductionProxySettings* Settings() = 0; + virtual void SetProbeResult(const std::string& test_url, + const std::string& response, + bool success) = 0; + + virtual void SetUp() OVERRIDE; + void CheckProxyPref(const std::string& expected_servers, + const std::string& expected_mode); + void CheckProxyConfigs(bool expected_enabled); + void CheckProbe(bool initially_enabled, + const std::string& probe_url, + const std::string& response, + bool request_success, + bool expected_enabled); + void CheckProbeOnIPChange(const std::string& probe_url, + const std::string& response, + bool request_success, + bool expected_enabled); + void CheckOnPrefChange(bool enabled, + const std::string& probe_url, + const std::string& response, + bool request_success, + bool expected_enabled); + void CheckInitDataReductionProxy(bool enabled_at_startup); + + TestingPrefServiceSimple pref_service_; + base::Time last_update_time_; + // This is a singleton that will clear all set field trials on destruction. + scoped_ptr<base::FieldTrialList> field_trial_list_; +}; + +#endif // CHROME_BROWSER_NET_SPDYPROXY_DATA_REDUCTION_PROXY_SETTINGS_UNITTEST_H_ diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_unittest_android.cc b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_unittest_android.cc index d76bd172b9..98a4275ccf 100644 --- a/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_unittest_android.cc +++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_unittest_android.cc @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "chrome/browser/net/spdyproxy/data_reduction_proxy_settings_unittest.h" + + #include "base/android/jni_android.h" #include "base/android/jni_string.h" #include "base/android/scoped_java_ref.h" #include "base/base64.h" #include "base/command_line.h" #include "base/metrics/field_trial.h" -#include "base/prefs/pref_registry_simple.h" #include "base/prefs/pref_service.h" #include "base/prefs/testing_pref_service.h" #include "base/strings/string_number_conversions.h" @@ -24,32 +26,28 @@ #include "url/gurl.h" const char kDataReductionProxyOrigin[] = "https://foo:443/"; -const char kDataReductionProxyOriginHostPort[] = "foo:443"; const char kDataReductionProxyAuth[] = "12345"; -const char kProbeURLWithOKResponse[] = "http://ok.org/"; -const char kProbeURLWithBadResponse[] = "http://bad.org/"; -const char kProbeURLWithNoResponse[] = "http://no.org/"; - class TestDataReductionProxySettingsAndroid : public DataReductionProxySettingsAndroid { public: - TestDataReductionProxySettingsAndroid(JNIEnv* env, jobject obj, + TestDataReductionProxySettingsAndroid(JNIEnv* env, + jobject obj, PrefService* profile_prefs, PrefService* local_state_prefs) - : DataReductionProxySettingsAndroid(env, obj), - success_(false), - fake_fetcher_request_count_(0), - profile_prefs_(profile_prefs), - local_state_prefs_(local_state_prefs) { + : DataReductionProxySettingsAndroid(env, obj), + success_(false), + fake_fetcher_request_count_(0), + profile_prefs_(profile_prefs), + local_state_prefs_(local_state_prefs) { } - // DataReductionProxySettingsAndroid implementation: + // DataReductionProxySettings implementation: virtual net::URLFetcher* GetURLFetcher() OVERRIDE { if (test_url_.empty()) return NULL; - net::URLFetcher* fetcher = new net::FakeURLFetcher(GURL(test_url_), this, - response_, success_); + net::URLFetcher* fetcher = + new net::FakeURLFetcher(GURL(test_url_), this, response_, success_); fake_fetcher_request_count_++; return fetcher; } @@ -82,46 +80,34 @@ class TestDataReductionProxySettingsAndroid PrefService* local_state_prefs_; }; -class DataReductionProxySettingsAndroidTest : public testing::Test { - protected: - void AddProxyToCommandLine() { - CommandLine::ForCurrentProcess()->AppendSwitchASCII( - switches::kSpdyProxyAuthOrigin, kDataReductionProxyOrigin); - CommandLine::ForCurrentProcess()->AppendSwitchASCII( - switches::kSpdyProxyAuthValue, kDataReductionProxyAuth); +class DataReductionProxySettingsAndroidTest + : public DataReductionProxySettingsTestBase { + public: + virtual void ResetSettings() OVERRIDE{ + settings_.reset(new TestDataReductionProxySettingsAndroid(NULL, NULL, + &pref_service_, + &pref_service_)); } - // testing::Test implementation: + virtual TestDataReductionProxySettingsAndroid* Settings() OVERRIDE { + return settings_.get(); + } + + virtual void SetProbeResult(const std::string& test_url, + const std::string& response, + bool success) OVERRIDE { + settings_->set_probe_result(test_url, response, success); + } + + // DataReductionProxySettingsTest implementation: virtual void SetUp() OVERRIDE { env_ = base::android::AttachCurrentThread(); DataReductionProxySettingsAndroid::Register(env_); - PrefRegistrySimple* registry = pref_service_.registry(); - registry->RegisterListPref(prefs::kDailyHttpOriginalContentLength); - registry->RegisterListPref(prefs::kDailyHttpReceivedContentLength); - registry->RegisterInt64Pref( - prefs::kDailyHttpContentLengthLastUpdateDate, 0L); - registry->RegisterDictionaryPref(prefs::kProxy); - registry->RegisterBooleanPref(prefs::kSpdyProxyAuthEnabled, false); - registry->RegisterBooleanPref(prefs::kSpdyProxyAuthWasEnabledBefore, false); - settings_.reset(new TestDataReductionProxySettingsAndroid(NULL, NULL, - &pref_service_, - &pref_service_)); - ListPrefUpdate original_update(&pref_service_, - prefs::kDailyHttpOriginalContentLength); - ListPrefUpdate received_update(&pref_service_, - prefs::kDailyHttpReceivedContentLength); - for (int64 i = 0; i < spdyproxy::kNumDaysInHistory; i++) { - original_update->Insert(0, new StringValue(base::Int64ToString(2 * i))); - received_update->Insert(0, new StringValue(base::Int64ToString(i))); - } - last_update_time_ = base::Time::Now().LocalMidnight(); - pref_service_.SetInt64( - prefs::kDailyHttpContentLengthLastUpdateDate, - last_update_time_.ToInternalValue()); + DataReductionProxySettingsTestBase::SetUp(); } - void CheckProxyPref(const std::string& expected_pac_url, - const std::string& expected_mode) { + void CheckProxyPacPref(const std::string& expected_pac_url, + const std::string& expected_mode) { const DictionaryValue* dict = pref_service_.GetDictionary(prefs::kProxy); std::string mode; std::string pac_url; @@ -131,68 +117,7 @@ class DataReductionProxySettingsAndroidTest : public testing::Test { ASSERT_EQ(expected_pac_url, pac_url); } - void CheckProxyPac(bool expected_enabled) { - if (expected_enabled) { - std::string pac; - base::Base64Encode(settings_->GetProxyPacScript(), &pac); - std::string expected_pac_url = - "data:application/x-ns-proxy-autoconfig;base64," + pac; - CheckProxyPref(expected_pac_url, - ProxyModeToString(ProxyPrefs::MODE_PAC_SCRIPT)); - } else { - CheckProxyPref(std::string(), ProxyModeToString(ProxyPrefs::MODE_SYSTEM)); - } - } - - void CheckProbe(bool initially_enabled, const std::string& probe_url, - const std::string& response, bool request_success, - bool expected_enabled) { - pref_service_.SetBoolean(prefs::kSpdyProxyAuthEnabled, initially_enabled); - settings_->set_probe_result(probe_url, response, request_success); - settings_->MaybeActivateDataReductionProxy(false); - base::MessageLoop::current()->RunUntilIdle(); - CheckProxyPac(expected_enabled); - } - - void CheckProbeOnIPChange(const std::string& probe_url, - const std::string& response, - bool request_success, - bool expected_enabled) { - settings_->set_probe_result(probe_url, response, request_success); - settings_->OnIPAddressChanged(); - base::MessageLoop::current()->RunUntilIdle(); - CheckProxyPac(expected_enabled); - } - - void CheckOnPrefChange(bool enabled, const std::string& probe_url, - const std::string& response, bool request_success, - bool expected_enabled) { - settings_->set_probe_result(probe_url, response, request_success); - pref_service_.SetBoolean(prefs::kSpdyProxyAuthEnabled, enabled); - base::MessageLoop::current()->RunUntilIdle(); - CheckProxyPac(expected_enabled); - } - - void CheckInitDataReductionProxy(bool enabled_at_startup) { - AddProxyToCommandLine(); - base::MessageLoop loop(base::MessageLoop::TYPE_UI); - pref_service_.SetBoolean(prefs::kSpdyProxyAuthEnabled, enabled_at_startup); - settings_->set_probe_result(kProbeURLWithOKResponse, "OK", true); - settings_->InitDataReductionProxySettings(NULL, NULL); - base::MessageLoop::current()->RunUntilIdle(); - if (enabled_at_startup) { - CheckProxyPac(enabled_at_startup); - } else { - // This presumes the proxy pref hadn't been set up by Chrome. - CheckProxyPref(std::string(), std::string()); - } - } - - TestingPrefServiceSimple pref_service_; scoped_ptr<TestDataReductionProxySettingsAndroid> settings_; - base::Time last_update_time_; - // This is a singleton that will clear all set field trials on destruction. - scoped_ptr<base::FieldTrialList> field_trial_list_; JNIEnv* env_; }; @@ -235,37 +160,16 @@ TEST_F(DataReductionProxySettingsAndroidTest, // Confirm that the bypass rule functions generate the intended JavaScript // code for the Proxy PAC. -TEST_F(DataReductionProxySettingsAndroidTest, TestBypassRules) { +TEST_F(DataReductionProxySettingsAndroidTest, TestBypassPACRules) { settings_->AddURLPatternToBypass("http://foo.com/*"); settings_->AddHostPatternToBypass("bar.com"); - settings_->AddHostToBypass("127.0.0.1"); - - std::string expected[] = { - "shExpMatch(url, 'http://foo.com/*')", - "shExpMatch(host, 'bar.com')", - "host == '127.0.0.1'" - }; - - int i = 0; - for (std::vector<std::string>::iterator it = settings_->bypass_rules_.begin(); - it != settings_->bypass_rules_.end(); ++it) { - EXPECT_EQ(expected[i++], *it); - } -} - -TEST_F(DataReductionProxySettingsAndroidTest, TestIsProxyEnabledOrManaged) { - settings_->InitPrefMembers(); - EXPECT_FALSE(settings_->IsDataReductionProxyEnabled(NULL, NULL)); - EXPECT_FALSE(settings_->IsDataReductionProxyManaged(NULL, NULL)); - pref_service_.SetBoolean(prefs::kSpdyProxyAuthEnabled, true); - EXPECT_TRUE(settings_->IsDataReductionProxyEnabled(NULL, NULL)); - EXPECT_FALSE(settings_->IsDataReductionProxyManaged(NULL, NULL)); + EXPECT_EQ(settings_->pac_bypass_rules_.size(), 1u); + EXPECT_EQ("shExpMatch(url, 'http://foo.com/*')", + settings_->pac_bypass_rules_[0]); - pref_service_.SetManagedPref(prefs::kSpdyProxyAuthEnabled, - base::Value::CreateBooleanValue(true)); - EXPECT_TRUE(settings_->IsDataReductionProxyEnabled(NULL, NULL)); - EXPECT_TRUE(settings_->IsDataReductionProxyManaged(NULL, NULL)); + EXPECT_EQ(settings_->BypassRules().size(), 1u); + EXPECT_EQ("bar.com", settings_->BypassRules()[0]); } TEST_F(DataReductionProxySettingsAndroidTest, TestSetProxyPac) { @@ -275,21 +179,20 @@ TEST_F(DataReductionProxySettingsAndroidTest, TestSetProxyPac) { std::string expected_pac_url = "data:application/x-ns-proxy-autoconfig;base64," + pac; // Test setting the PAC, without generating histograms. - settings_->has_turned_on_ = true; - settings_->SetProxyPac(true, false); - CheckProxyPref(expected_pac_url, - ProxyModeToString(ProxyPrefs::MODE_PAC_SCRIPT)); + settings_->SetHasTurnedOn(); + settings_->SetProxyConfigs(true, false); + CheckProxyPacPref(expected_pac_url, + ProxyModeToString(ProxyPrefs::MODE_PAC_SCRIPT)); // Test disabling the PAC, without generating histograms. - settings_->has_turned_off_ = true; - settings_->SetProxyPac(false, false); - CheckProxyPref(std::string(), ProxyModeToString(ProxyPrefs::MODE_SYSTEM)); + settings_->SetHasTurnedOff(); + settings_->SetProxyConfigs(false, false); + CheckProxyPacPref(std::string(), ProxyModeToString(ProxyPrefs::MODE_SYSTEM)); } TEST_F(DataReductionProxySettingsAndroidTest, TestGetDailyContentLengths) { - ScopedJavaLocalRef<jlongArray> result = - settings_->GetDailyContentLengths(env_, - prefs::kDailyHttpOriginalContentLength); + ScopedJavaLocalRef<jlongArray> result = settings_->GetDailyContentLengths( + env_, prefs::kDailyHttpOriginalContentLength); ASSERT_TRUE(result.obj()); jsize java_array_len = env_->GetArrayLength(result.obj()); @@ -303,133 +206,3 @@ TEST_F(DataReductionProxySettingsAndroidTest, TestGetDailyContentLengths) { } } -TEST_F(DataReductionProxySettingsAndroidTest, - TestResetDataReductionStatistics) { - int64 original_content_length; - int64 received_content_length; - int64 last_update_time; - settings_->ResetDataReductionStatistics(); - settings_->GetContentLengthsInternal(spdyproxy::kNumDaysInHistory, - &original_content_length, - &received_content_length, - &last_update_time); - EXPECT_EQ(0L, original_content_length); - EXPECT_EQ(0L, received_content_length); - EXPECT_EQ(last_update_time_.ToInternalValue(), last_update_time); -} - -TEST_F(DataReductionProxySettingsAndroidTest, TestContentLengthsInternal) { - int64 original_content_length; - int64 received_content_length; - int64 last_update_time; - - // Request |kNumDaysInHistory| days. - settings_->GetContentLengthsInternal(spdyproxy::kNumDaysInHistory, - &original_content_length, - &received_content_length, - &last_update_time); - const unsigned int days = spdyproxy::kNumDaysInHistory; - // Received content length history values are 0 to |kNumDaysInHistory - 1|. - int64 expected_total_received_content_length = (days - 1L) * days / 2; - // Original content length history values are 0 to - // |2 * (kNumDaysInHistory - 1)|. - long expected_total_original_content_length = (days - 1L) * days; - EXPECT_EQ(expected_total_original_content_length, original_content_length); - EXPECT_EQ(expected_total_received_content_length, received_content_length); - EXPECT_EQ(last_update_time_.ToInternalValue(), last_update_time); - - // Request |kNumDaysInHistory - 1| days. - settings_->GetContentLengthsInternal(spdyproxy::kNumDaysInHistory - 1, - &original_content_length, - &received_content_length, - &last_update_time); - expected_total_received_content_length -= (days - 1); - expected_total_original_content_length -= 2 * (days - 1); - EXPECT_EQ(expected_total_original_content_length, original_content_length); - EXPECT_EQ(expected_total_received_content_length, received_content_length); - - // Request 0 days. - settings_->GetContentLengthsInternal(0, - &original_content_length, - &received_content_length, - &last_update_time); - expected_total_received_content_length = 0; - expected_total_original_content_length = 0; - EXPECT_EQ(expected_total_original_content_length, original_content_length); - EXPECT_EQ(expected_total_received_content_length, received_content_length); - - // Request 1 day. First day had 0 bytes so should be same as 0 days. - settings_->GetContentLengthsInternal(1, - &original_content_length, - &received_content_length, - &last_update_time); - EXPECT_EQ(expected_total_original_content_length, original_content_length); - EXPECT_EQ(expected_total_received_content_length, received_content_length); -} - -TEST_F(DataReductionProxySettingsAndroidTest, - TestMaybeActivateDataReductionProxy) { - AddProxyToCommandLine(); - settings_->InitPrefMembers(); - // TODO(bengr): Test enabling/disabling while a probe is outstanding. - base::MessageLoop loop(base::MessageLoop::TYPE_UI); - // The proxy is enabled initially. - // Request succeeded but with bad response, expect proxy to be disabled. - CheckProbe(true, kProbeURLWithBadResponse, "Bad", true, false); - // Request succeeded with valid response, expect proxy to be enabled. - CheckProbe(true, kProbeURLWithOKResponse, "OK", true, true); - // Request failed, expect proxy to be disabled. - CheckProbe(true, kProbeURLWithNoResponse, "", false, false); - - // The proxy is disabled initially. Probes should not be emitted to change - // state. - EXPECT_EQ(3, settings_->fake_fetcher_request_count()); - CheckProbe(false, kProbeURLWithOKResponse, "OK", true, false); - EXPECT_EQ(3, settings_->fake_fetcher_request_count()); -} - -TEST_F(DataReductionProxySettingsAndroidTest, - TestOnIPAddressChanged) { - AddProxyToCommandLine(); - base::MessageLoop loop(base::MessageLoop::TYPE_UI); - // The proxy is enabled initially. - settings_->enabled_by_user_ = true; - settings_->SetProxyPac(true, true); - // IP address change triggers a probe that succeeds. Proxy remains enabled. - CheckProbeOnIPChange(kProbeURLWithOKResponse, "OK", true, true); - // IP address change triggers a probe that fails. Proxy is disabled. - CheckProbeOnIPChange(kProbeURLWithBadResponse, "Bad", true, false); - // IP address change triggers a probe that fails. Proxy remains disabled. - CheckProbeOnIPChange(kProbeURLWithBadResponse, "Bad", true, false); - // IP address change triggers a probe that succeed. Proxy is enabled. - CheckProbeOnIPChange(kProbeURLWithBadResponse, "OK", true, true); - EXPECT_EQ(4, settings_->fake_fetcher_request_count()); -} - -TEST_F(DataReductionProxySettingsAndroidTest, - TestOnProxyEnabledPrefChange) { - AddProxyToCommandLine(); - settings_->InitPrefMembers(); - base::MessageLoop loop(base::MessageLoop::TYPE_UI); - LOG(WARNING) << "Before init pref members"; - // The proxy is enabled initially. - settings_->enabled_by_user_ = true; - settings_->SetProxyPac(true, true); - LOG(WARNING) << "after set proxy pac"; - // The pref is disabled, so correspondingly should be the proxy. - CheckOnPrefChange(false, kProbeURLWithOKResponse, "OK", true, false); - // The pref is enabled, so correspondingly should be the proxy. - CheckOnPrefChange(true, kProbeURLWithOKResponse, "OK", true, true); - EXPECT_EQ(1, settings_->fake_fetcher_request_count()); -} - -TEST_F(DataReductionProxySettingsAndroidTest, - TestInitDataReductionProxyOn) { - CheckInitDataReductionProxy(true); -} - -TEST_F(DataReductionProxySettingsAndroidTest, - TestInitDataReductionProxyOff) { - CheckInitDataReductionProxy(false); -} - diff --git a/chrome/browser/notifications/balloon_host.cc b/chrome/browser/notifications/balloon_host.cc index 362804fe2a..c3de873cc9 100644 --- a/chrome/browser/notifications/balloon_host.cc +++ b/chrome/browser/notifications/balloon_host.cc @@ -5,6 +5,7 @@ #include "chrome/browser/notifications/balloon_host.h" #include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/extensions/extension_web_contents_observer.h" #include "chrome/browser/notifications/balloon.h" #include "chrome/browser/notifications/balloon_collection_impl.h" #include "chrome/browser/notifications/notification.h" @@ -141,6 +142,8 @@ void BalloonHost::Init() { extensions::SetViewType( web_contents_.get(), extensions::VIEW_TYPE_NOTIFICATION); web_contents_->SetDelegate(this); + extensions::ExtensionWebContentsObserver::CreateForWebContents( + web_contents_.get()); Observe(web_contents_.get()); renderer_preferences_util::UpdateFromSystemSettings( web_contents_->GetMutableRendererPrefs(), balloon_->profile()); diff --git a/chrome/browser/notifications/desktop_notification_service.cc b/chrome/browser/notifications/desktop_notification_service.cc index b671fbd08c..a910ecbbfe 100644 --- a/chrome/browser/notifications/desktop_notification_service.cc +++ b/chrome/browser/notifications/desktop_notification_service.cc @@ -209,10 +209,7 @@ void DesktopNotificationService::RegisterProfilePrefs( registry->RegisterListPref( prefs::kMessageCenterEnabledSyncNotifierIds, user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); - registry->RegisterBooleanPref( - prefs::kWelcomeNotificationDismissed, - false, - user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); + WelcomeNotification::RegisterProfilePrefs(registry); } // static @@ -626,6 +623,17 @@ void DesktopNotificationService::SetNotifierEnabled( } } +void DesktopNotificationService::ShowWelcomeNotificationIfNecessary( + const Notification& notification) { + if (!welcome_notification && message_center::IsRichNotificationEnabled()) { + welcome_notification.reset( + new WelcomeNotification(profile_, g_browser_process->message_center())); + } + + if (welcome_notification) + welcome_notification->ShowWelcomeNotificationIfNecessary(notification); +} + void DesktopNotificationService::OnStringListPrefChanged( const char* pref_name, std::set<std::string>* ids_field) { ids_field->clear(); diff --git a/chrome/browser/notifications/desktop_notification_service.h b/chrome/browser/notifications/desktop_notification_service.h index c77720b4b0..46defd40f8 100644 --- a/chrome/browser/notifications/desktop_notification_service.h +++ b/chrome/browser/notifications/desktop_notification_service.h @@ -11,9 +11,11 @@ #include "base/basictypes.h" #include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" #include "base/prefs/pref_member.h" #include "base/strings/string16.h" #include "chrome/browser/content_settings/content_settings_provider.h" +#include "chrome/browser/notifications/welcome_notification.h" #include "chrome/common/content_settings.h" #include "components/browser_context_keyed_service/browser_context_keyed_service.h" #include "content/public/browser/notification_observer.h" @@ -162,6 +164,10 @@ class DesktopNotificationService : public BrowserContextKeyedService, void SetNotifierEnabled(const message_center::NotifierId& notifier_id, bool enabled); + // Adds in a the welcome notification if required for components built + // into Chrome that show notifications like Chrome Now. + void ShowWelcomeNotificationIfNecessary(const Notification& notification); + private: // Takes a notification object and shows it in the UI. void ShowNotification(const Notification& notification); @@ -222,6 +228,9 @@ class DesktopNotificationService : public BrowserContextKeyedService, // Registrar for the other kind of notifications (event signaling). content::NotificationRegistrar registrar_; + // Welcome Notification + scoped_ptr<WelcomeNotification> welcome_notification; + DISALLOW_COPY_AND_ASSIGN(DesktopNotificationService); }; diff --git a/chrome/browser/notifications/message_center_notification_manager.cc b/chrome/browser/notifications/message_center_notification_manager.cc index 7aa64dceef..d4048909c0 100644 --- a/chrome/browser/notifications/message_center_notification_manager.cc +++ b/chrome/browser/notifications/message_center_notification_manager.cc @@ -92,6 +92,9 @@ void MessageCenterNotificationManager::Add(const Notification& notification, if (Update(notification, profile)) return; + DesktopNotificationServiceFactory::GetForProfile(profile)-> + ShowWelcomeNotificationIfNecessary(notification); + AddProfileNotification( new ProfileNotification(profile, notification, message_center_)); } diff --git a/chrome/browser/notifications/message_center_settings_controller.cc b/chrome/browser/notifications/message_center_settings_controller.cc index 583ca0f0f1..b21fb16de2 100644 --- a/chrome/browser/notifications/message_center_settings_controller.cc +++ b/chrome/browser/notifications/message_center_settings_controller.cc @@ -162,7 +162,9 @@ void MessageCenterSettingsController::SwitchToNotifierGroup(size_t index) { void MessageCenterSettingsController::GetNotifierList( std::vector<Notifier*>* notifiers) { DCHECK(notifiers); - // TODO(mukai): Fix this for multi-profile. + if (notifier_groups_.empty()) + return; + // Temporarily use the last used profile to prevent chrome from crashing when // the default profile is not loaded. message_center::ProfileNotifierGroup* group = diff --git a/chrome/browser/notifications/message_center_settings_controller_unittest.cc b/chrome/browser/notifications/message_center_settings_controller_unittest.cc index e46505c092..362da5e26e 100644 --- a/chrome/browser/notifications/message_center_settings_controller_unittest.cc +++ b/chrome/browser/notifications/message_center_settings_controller_unittest.cc @@ -69,3 +69,15 @@ TEST_F(MessageCenterSettingsControllerTest, NotifierGroups) { EXPECT_EQ(controller->GetActiveNotifierGroup().name, UTF8ToUTF16("Profile-1")); } + +TEST_F(MessageCenterSettingsControllerTest, GuestNoBreak) { + // In the guest mode of ChromeOS, there're no notifier groups but + // GetNotifierList() shouldn't cause crash. + scoped_ptr<MessageCenterSettingsController> controller( + new MessageCenterSettingsController(GetCache())); + + EXPECT_EQ(controller->GetNotifierGroupCount(), 0u); + std::vector<message_center::Notifier*> notifiers; + controller->GetNotifierList(¬ifiers); + EXPECT_TRUE(notifiers.empty()); +} diff --git a/chrome/browser/notifications/message_center_stats_collector.cc b/chrome/browser/notifications/message_center_stats_collector.cc index f0257a9423..3279728b7a 100644 --- a/chrome/browser/notifications/message_center_stats_collector.cc +++ b/chrome/browser/notifications/message_center_stats_collector.cc @@ -134,5 +134,12 @@ void MessageCenterStatsCollector::OnCenterVisibilityChanged( } void MessageCenterStatsCollector::OnQuietModeChanged(bool in_quiet_mode) { + if (in_quiet_mode) { + content::RecordAction( + content::UserMetricsAction("Notifications.Mute")); + } else { + content::RecordAction( + content::UserMetricsAction("Notifications.Unmute")); + } } diff --git a/chrome/browser/notifications/notification_browsertest.cc b/chrome/browser/notifications/notification_browsertest.cc index b246ebb5e2..de06963a73 100644 --- a/chrome/browser/notifications/notification_browsertest.cc +++ b/chrome/browser/notifications/notification_browsertest.cc @@ -856,12 +856,11 @@ IN_PROC_BROWSER_TEST_F(NotificationsTest, TestCloseTabWithPermissionInfobar) { browser()->tab_strip_model()->ActivateTabAt(0, true); ui_test_utils::NavigateToURL(browser(), GetTestPageURL()); ASSERT_TRUE(RequestPermissionAndWait(browser())); - content::WindowedNotificationObserver observer( - content::NOTIFICATION_WEB_CONTENTS_DESTROYED, - content::NotificationService::AllSources()); + content::WebContentsDestroyedWatcher destroyed_watcher( + browser()->tab_strip_model()->GetWebContentsAt(0)); browser()->tab_strip_model()->CloseWebContentsAt(0, TabStripModel::CLOSE_NONE); - observer.Wait(); + destroyed_watcher.Wait(); } IN_PROC_BROWSER_TEST_F( diff --git a/chrome/browser/notifications/sync_notifier/chrome_notifier_delegate.cc b/chrome/browser/notifications/sync_notifier/chrome_notifier_delegate.cc index 96f74c9b1a..9585d81832 100644 --- a/chrome/browser/notifications/sync_notifier/chrome_notifier_delegate.cc +++ b/chrome/browser/notifications/sync_notifier/chrome_notifier_delegate.cc @@ -4,11 +4,14 @@ #include "chrome/browser/notifications/sync_notifier/chrome_notifier_delegate.h" + +#include "base/metrics/histogram.h" #include "chrome/browser/notifications/sync_notifier/chrome_notifier_service.h" #include "chrome/browser/notifications/sync_notifier/synced_notification.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_finder.h" #include "content/public/browser/page_navigator.h" +#include "content/public/browser/user_metrics.h" namespace notifier { ChromeNotifierDelegate::ChromeNotifierDelegate( @@ -26,6 +29,15 @@ content::RenderViewHost* ChromeNotifierDelegate::GetRenderViewHost() const { return NULL; } +void ChromeNotifierDelegate::CollectAction(SyncedNotificationActionType type) { + DCHECK(!notification_id_.empty()); + + UMA_HISTOGRAM_ENUMERATION("SyncedNotifications.Actions", + type, + SYNCED_NOTIFICATION_ACTION_COUNT); +} + + // TODO(petewil) Add the ability to do URL actions also. void ChromeNotifierDelegate::Click() { SyncedNotification* notification = @@ -36,6 +48,9 @@ void ChromeNotifierDelegate::Click() { GURL destination = notification->GetDefaultDestinationUrl(); NavigateToUrl(destination); chrome_notifier_->MarkNotificationAsRead(notification_id_); + + // Record the action in UMA statistics. + CollectAction(SYNCED_NOTIFICATION_ACTION_CLICK); } // TODO(petewil) Add the ability to do URL actions also. @@ -47,6 +62,9 @@ void ChromeNotifierDelegate::ButtonClick(int button_index) { NavigateToUrl(destination); chrome_notifier_->MarkNotificationAsRead(notification_id_); } + + // Now record the UMA statistics for this action. + CollectAction(SYNCED_NOTIFICATION_ACTION_BUTTON_CLICK); } void ChromeNotifierDelegate::NavigateToUrl(const GURL& destination) const { @@ -68,6 +86,10 @@ void ChromeNotifierDelegate::NavigateToUrl(const GURL& destination) const { void ChromeNotifierDelegate::Close(bool by_user) { if (by_user) chrome_notifier_->MarkNotificationAsRead(notification_id_); + + CollectAction(by_user ? + SYNCED_NOTIFICATION_ACTION_CLOSE_BY_USER : + SYNCED_NOTIFICATION_ACTION_CLOSE_BY_SYSTEM); } } // namespace notifier diff --git a/chrome/browser/notifications/sync_notifier/chrome_notifier_delegate.h b/chrome/browser/notifications/sync_notifier/chrome_notifier_delegate.h index f0ea79bac5..4bf75fedf9 100644 --- a/chrome/browser/notifications/sync_notifier/chrome_notifier_delegate.h +++ b/chrome/browser/notifications/sync_notifier/chrome_notifier_delegate.h @@ -5,6 +5,7 @@ #ifndef CHROME_BROWSER_NOTIFICATIONS_SYNC_NOTIFIER_CHROME_NOTIFIER_DELEGATE_H_ #define CHROME_BROWSER_NOTIFICATIONS_SYNC_NOTIFIER_CHROME_NOTIFIER_DELEGATE_H_ +#include <map> #include <string> #include "chrome/browser/notifications/notification_delegate.h" @@ -12,6 +13,19 @@ namespace notifier { +enum SyncedNotificationActionType { + SYNCED_NOTIFICATION_ACTION_UNKNOWN, + SYNCED_NOTIFICATION_ACTION_CLICK, + SYNCED_NOTIFICATION_ACTION_BUTTON_CLICK, + SYNCED_NOTIFICATION_ACTION_CLOSE_BY_USER, + SYNCED_NOTIFICATION_ACTION_CLOSE_BY_SYSTEM, + SYNCED_NOTIFICATION_ACTION_TOAST_TIMEOUT, + // NOTE: Add new action types only immediately above this line. Also, + // make sure the enum list in tools/histogram/histograms.xml is + // updated with any change in here. + SYNCED_NOTIFICATION_ACTION_COUNT +}; + class ChromeNotifierService; // ChromeNotifierDelegate is a NotificationDelegate which catches @@ -35,6 +49,8 @@ class ChromeNotifierDelegate : public NotificationDelegate { virtual content::RenderViewHost* GetRenderViewHost() const OVERRIDE; + void CollectAction(SyncedNotificationActionType type); + private: virtual ~ChromeNotifierDelegate(); void NavigateToUrl(const GURL& destination) const; diff --git a/chrome/browser/notifications/sync_notifier/chrome_notifier_service.cc b/chrome/browser/notifications/sync_notifier/chrome_notifier_service.cc index 939c2d4e16..3118a9e4ff 100644 --- a/chrome/browser/notifications/sync_notifier/chrome_notifier_service.cc +++ b/chrome/browser/notifications/sync_notifier/chrome_notifier_service.cc @@ -12,6 +12,7 @@ #include <vector> #include "base/command_line.h" +#include "base/metrics/histogram.h" #include "base/prefs/pref_service.h" #include "base/values.h" #include "chrome/browser/notifications/desktop_notification_service.h" @@ -23,6 +24,7 @@ #include "chrome/common/pref_names.h" #include "components/user_prefs/pref_registry_syncable.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/user_metrics.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" #include "sync/api/sync_change.h" @@ -36,6 +38,8 @@ #include "ui/message_center/notifier_settings.h" #include "url/gurl.h" +using content::UserMetricsAction; + namespace notifier { const char kFirstSyncedNotificationServiceId[] = "Google+"; @@ -493,10 +497,46 @@ void ChromeNotifierService::OnSyncedNotificationServiceEnabled( RemoveUnreadNotificationsFromSource(notifier_id_copy); } - // Otherwise, nothing to do, we can exit. + // Collect UMA statistics when a service is enabled or disabled. + if (enabled) { + content::RecordAction( + UserMetricsAction("SyncedNotifications.SendingServiceEnabled")); + } else { + content::RecordAction( + UserMetricsAction("SyncedNotifications.SendingServiceDisabled")); + } + + // Collect individual service enabling/disabling statistics. + CollectPerServiceEnablingStatistics(notifier_id, enabled); + return; } +void ChromeNotifierService::CollectPerServiceEnablingStatistics( + const std::string& notifier_id, + bool enabled) { + // TODO(petewil) - This approach does not scale well as we add new services, + // but we are limited to using predefined ENUM values in histogram based UMA + // data, which does not permit arbitrary strings. + // Find a way to make it scale, or remove enum value this when we have enough + // data. + + ChromeNotifierServiceActionType action = + CHROME_NOTIFIER_SERVICE_ACTION_UNKNOWN; + + // Derive action type from notifier_id and enabled. + // TODO(petewil): Add more sending services as they are enabled. + if (notifier_id == std::string(kFirstSyncedNotificationServiceId)) { + action = enabled + ? CHROME_NOTIFIER_SERVICE_ACTION_FIRST_SERVICE_ENABLED + : CHROME_NOTIFIER_SERVICE_ACTION_FIRST_SERVICE_DISABLED; + } + + UMA_HISTOGRAM_ENUMERATION("ChromeNotifierService.Actions", + action, + CHROME_NOTIFIER_SERVICE_ACTION_COUNT); +} + void ChromeNotifierService::BuildServiceListValueInplace( std::set<std::string> services, base::ListValue* list_value) { std::set<std::string>::iterator iter; @@ -616,6 +656,7 @@ void ChromeNotifierService::InitializePrefs() { // Get the prefs from last session into our memeber varilables OnEnabledSendingServiceListPrefChanged(&enabled_sending_services_); OnInitializedSendingServiceListPrefChanged(&initialized_sending_services_); + synced_notification_first_run_ = profile_->GetPrefs()->GetBoolean(prefs::kSyncedNotificationFirstRun); } diff --git a/chrome/browser/notifications/sync_notifier/chrome_notifier_service.h b/chrome/browser/notifications/sync_notifier/chrome_notifier_service.h index 787fcbd5b4..356448fbd1 100644 --- a/chrome/browser/notifications/sync_notifier/chrome_notifier_service.h +++ b/chrome/browser/notifications/sync_notifier/chrome_notifier_service.h @@ -32,6 +32,16 @@ extern const char kFirstSyncedNotificationServiceId[]; extern const char kServiceEnabledOnce[]; extern const char kSyncedNotificationFirstRun[]; +enum ChromeNotifierServiceActionType { + CHROME_NOTIFIER_SERVICE_ACTION_UNKNOWN, + CHROME_NOTIFIER_SERVICE_ACTION_FIRST_SERVICE_ENABLED, + CHROME_NOTIFIER_SERVICE_ACTION_FIRST_SERVICE_DISABLED, + // NOTE: Add new action types only immediately above this line. Also, + // make sure the enum list in tools/histogram/histograms.xml is + // updated with any change in here. + CHROME_NOTIFIER_SERVICE_ACTION_COUNT +}; + // The ChromeNotifierService holds notifications which represent the state of // delivered notifications for chrome. These are obtained from the sync service // and kept up to date. @@ -123,6 +133,11 @@ class ChromeNotifierService : public syncer::SyncableService, // for that service, and remove them from the message center. void RemoveUnreadNotificationsFromSource(const std::string& notifier_id); + // When we turn a sending service on or off, collect statistics about + // how often users turn it on or off. + void CollectPerServiceEnablingStatistics(const std::string& notifier_id, + bool enabled); + // When we start up or hear of a new service, turn it on by default. void AddNewSendingServices(); diff --git a/chrome/browser/notifications/welcome_notification.cc b/chrome/browser/notifications/welcome_notification.cc new file mode 100644 index 0000000000..ac48b752b4 --- /dev/null +++ b/chrome/browser/notifications/welcome_notification.cc @@ -0,0 +1,136 @@ +// Copyright 2013 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/notifications/welcome_notification.h" + +#include "base/guid.h" +#include "base/lazy_instance.h" +#include "base/message_loop/message_loop.h" +#include "base/prefs/pref_service.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/notifications/notification.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser_finder.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/url_constants.h" +#include "components/user_prefs/pref_registry_syncable.h" +#include "grit/generated_resources.h" +#include "grit/theme_resources.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/message_center/message_center.h" +#include "ui/message_center/notification.h" +#include "ui/message_center/notification_delegate.h" +#include "ui/message_center/notification_types.h" + +const char kChromeNowExtensionID[] = "pafkbggdmjlpgkdkcbjmhmfcdpncadgh"; + +class WelcomeNotificationDelegate + : public message_center::NotificationDelegate { + public: + WelcomeNotificationDelegate(const std::string& id, Profile* profile) + : profile_(profile) {} + + // Overridden from NotificationDelegate: + virtual void Display() OVERRIDE {} + virtual void Error() OVERRIDE {} + + virtual void Close(bool by_user) OVERRIDE { + if (by_user) { + // Setting the preference here may cause the notification erasing + // to reenter. Posting a task avoids this issue. + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&WelcomeNotificationDelegate::MarkAsDismissed, this)); + } + } + + virtual void Click() OVERRIDE {} + virtual void ButtonClick(int index) OVERRIDE {} + + private: + void MarkAsDismissed() { + profile_->GetPrefs()-> + SetBoolean(prefs::kWelcomeNotificationDismissed, true); + } + + virtual ~WelcomeNotificationDelegate() {} + + Profile* const profile_; + + DISALLOW_COPY_AND_ASSIGN(WelcomeNotificationDelegate); +}; + +WelcomeNotification::WelcomeNotification( + Profile* profile, + message_center::MessageCenter* message_center) + : profile_(profile), + message_center_(message_center) { + welcome_notification_dismissed_pref_.Init( + prefs::kWelcomeNotificationDismissed, + profile_->GetPrefs(), + base::Bind( + &WelcomeNotification::OnWelcomeNotificationDismissedChanged, + base::Unretained(this))); +} + +WelcomeNotification::~WelcomeNotification() {} + +void WelcomeNotification::ShowWelcomeNotificationIfNecessary( + const Notification& notification) { + if (notification.notifier_id().id == kChromeNowExtensionID) { + PrefService* pref_service = profile_->GetPrefs(); + if (!pref_service->GetBoolean(prefs::kWelcomeNotificationDismissed)) + ShowWelcomeNotification(); + } +} + +// Static +void WelcomeNotification::RegisterProfilePrefs( + user_prefs::PrefRegistrySyncable* prefs) { + prefs->RegisterBooleanPref( + prefs::kWelcomeNotificationDismissed, + false, + user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); +} + +void WelcomeNotification::ShowWelcomeNotification() { + message_center::RichNotificationData rich_notification_data; + rich_notification_data.priority = 2; + + if (welcome_notification_id_.empty()) + welcome_notification_id_ = base::GenerateGUID(); + + if (!welcome_notification_id_.empty()) { + scoped_ptr<message_center::Notification> message_center_notification( + new message_center::Notification( + message_center::NOTIFICATION_TYPE_BASE_FORMAT, + welcome_notification_id_, + l10n_util::GetStringUTF16(IDS_NOTIFICATION_WELCOME_TITLE), + l10n_util::GetStringUTF16(IDS_NOTIFICATION_WELCOME_BODY), + ui::ResourceBundle::GetSharedInstance().GetImageNamed( + IDR_NOTIFICATION_WELCOME_ICON), + l10n_util::GetStringUTF16(IDS_NOTIFICATION_WELCOME_DISPLAY_SOURCE), + message_center::NotifierId( + message_center::NotifierId::SYSTEM_COMPONENT), + rich_notification_data, + new WelcomeNotificationDelegate( + welcome_notification_id_, profile_))); + message_center_->AddNotification(message_center_notification.Pass()); + } +} + +void WelcomeNotification::HideWelcomeNotification() { + if (!welcome_notification_id_.empty() && + message_center_->HasNotification(welcome_notification_id_)) { + message_center_->RemoveNotification(welcome_notification_id_, false); + } +} + +void WelcomeNotification::OnWelcomeNotificationDismissedChanged() { + const bool welcome_notification_dismissed = + profile_->GetPrefs()->GetBoolean(prefs::kWelcomeNotificationDismissed); + if (welcome_notification_dismissed) + HideWelcomeNotification(); +} diff --git a/chrome/browser/notifications/welcome_notification.h b/chrome/browser/notifications/welcome_notification.h new file mode 100644 index 0000000000..54e1c432d8 --- /dev/null +++ b/chrome/browser/notifications/welcome_notification.h @@ -0,0 +1,63 @@ +// Copyright 2013 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_NOTIFICATIONS_WELCOME_NOTIFICATION_H_ +#define CHROME_BROWSER_NOTIFICATIONS_WELCOME_NOTIFICATION_H_ + +#include <string> + +#include "base/prefs/pref_member.h" + +namespace message_center { +class MessageCenter; +} + +namespace user_prefs { +class PrefRegistrySyncable; +} + +class Notification; +class Profile; + +// WelcomeNotification is a part of DesktopNotificationService and manages +// showing and hiding a welcome notification for built-in components that +// show notifications. +class WelcomeNotification { + public: + WelcomeNotification( + Profile* profile, + message_center::MessageCenter* message_center); + ~WelcomeNotification(); + + // Adds in a the welcome notification if required for components built + // into Chrome that show notifications like Chrome Now. + void ShowWelcomeNotificationIfNecessary( + const Notification& notification); + + // Handles Preference Registeration for the Welcome Notification. + static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* prefs); + + private: + // Unconditionally shows the welcome notification. + void ShowWelcomeNotification(); + + // Hides the welcome notification. + void HideWelcomeNotification(); + + // Called when the Welcome Notification Dismissed pref has been changed. + void OnWelcomeNotificationDismissedChanged(); + + // Prefs listener for welcome_notification_dismissed. + BooleanPrefMember welcome_notification_dismissed_pref_; + + // The profile which owns this object. + Profile* profile_; + + // Notification ID of the Welcome Notification. + std::string welcome_notification_id_; + + message_center::MessageCenter* message_center_; // Weak reference. +}; + +#endif // CHROME_BROWSER_NOTIFICATIONS_WELCOME_NOTIFICATION_H_ diff --git a/chrome/browser/notifications/welcome_notification_unittest.cc b/chrome/browser/notifications/welcome_notification_unittest.cc new file mode 100644 index 0000000000..71d5435a79 --- /dev/null +++ b/chrome/browser/notifications/welcome_notification_unittest.cc @@ -0,0 +1,235 @@ +// Copyright 2013 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/notifications/welcome_notification.h" + +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/prefs/pref_service.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/notifications/notification.h" +#include "chrome/common/pref_names.h" +#include "chrome/test/base/testing_pref_service_syncable.h" +#include "chrome/test/base/testing_profile.h" +#include "components/user_prefs/pref_registry_syncable.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/message_center/fake_message_center.h" +#include "ui/message_center/notification.h" + +const char kChromeNowExtensionID[] = "pafkbggdmjlpgkdkcbjmhmfcdpncadgh"; + +class MockMessageCenter : public message_center::FakeMessageCenter { + public: + MockMessageCenter() + : add_notification_calls_(0), + remove_notification_calls_(0) {}; + + int add_notification_calls() { return add_notification_calls_; } + int remove_notification_calls() { return remove_notification_calls_; } + + // message_center::FakeMessageCenter Overrides + virtual bool HasNotification(const std::string& id) OVERRIDE { + return last_notification.get() && + (last_notification->id() == id); + } + + virtual void AddNotification( + scoped_ptr<message_center::Notification> notification) OVERRIDE { + EXPECT_FALSE(last_notification.get()); + last_notification.swap(notification); + add_notification_calls_++; + } + + virtual void RemoveNotification(const std::string& id, bool by_user) + OVERRIDE { + EXPECT_TRUE(last_notification.get()); + last_notification.reset(); + remove_notification_calls_++; + } + + void CloseCurrentNotification() { + EXPECT_TRUE(last_notification.get()); + last_notification->delegate()->Close(true); + RemoveNotification(last_notification->id(), true); + } + + private: + scoped_ptr<message_center::Notification> last_notification; + int add_notification_calls_; + int remove_notification_calls_; +}; + +class WelcomeNotificationTest : public testing::Test { + protected: + WelcomeNotificationTest() { + scoped_refptr<user_prefs::PrefRegistrySyncable> pref_registry( + new user_prefs::PrefRegistrySyncable()); + WelcomeNotification::RegisterProfilePrefs(pref_registry.get()); + } + + virtual void SetUp() { + message_loop_.reset(new base::MessageLoop()); + profile_.reset(new TestingProfile()); + message_center_.reset(new MockMessageCenter()); + welcome_notification_.reset( + new WelcomeNotification(profile_.get(), message_center_.get())); + } + + virtual void TearDown() { + welcome_notification_.reset(); + message_center_.reset(); + profile_.reset(); + message_loop_.reset(); + } + + void ShowChromeNowNotification() { + ShowNotification( + "ChromeNowNotification", + message_center::NotifierId( + message_center::NotifierId::APPLICATION, + kChromeNowExtensionID)); + } + + void ShowRegularNotification() { + ShowNotification( + "RegularNotification", + message_center::NotifierId( + message_center::NotifierId::APPLICATION, + "aaaabbbbccccddddeeeeffffggghhhhi")); + } + + void FlushMessageLoop() { + message_loop_->RunUntilIdle(); + } + + TestingProfile* profile() { return profile_.get(); } + MockMessageCenter* message_center() { return message_center_.get(); } + + private: + class TestNotificationDelegate : public NotificationDelegate { + public: + explicit TestNotificationDelegate(const std::string& id) + : id_(id) {} + + // Overridden from NotificationDelegate: + virtual void Display() OVERRIDE {} + virtual void Error() OVERRIDE {} + virtual void Close(bool by_user) OVERRIDE {} + virtual void Click() OVERRIDE {} + virtual void ButtonClick(int index) OVERRIDE {} + + virtual std::string id() const OVERRIDE { return id_; } + + virtual content::RenderViewHost* GetRenderViewHost() const OVERRIDE { + return NULL; + } + + private: + virtual ~TestNotificationDelegate() {} + + const std::string id_; + + DISALLOW_COPY_AND_ASSIGN(TestNotificationDelegate); + }; + + void ShowNotification( + std::string notification_id, + const message_center::NotifierId& notifier_id) { + message_center::RichNotificationData rich_notification_data; + rich_notification_data.priority = 0; + Notification notification( + message_center::NOTIFICATION_TYPE_BASE_FORMAT, + GURL("http://tests.url"), + base::UTF8ToUTF16("Title"), + base::UTF8ToUTF16("Body"), + gfx::Image(), + WebKit::WebTextDirectionDefault, + notifier_id, + base::UTF8ToUTF16("Source"), + base::UTF8ToUTF16(notification_id), + rich_notification_data, + new TestNotificationDelegate("TestNotification")); + welcome_notification_->ShowWelcomeNotificationIfNecessary(notification); + } + + scoped_ptr<TestingProfile> profile_; + scoped_ptr<MockMessageCenter> message_center_; + scoped_ptr<WelcomeNotification> welcome_notification_; + scoped_ptr<base::MessageLoop> message_loop_; +}; + +// Show a regular notification. Expect that WelcomeNotification will +// not show a welcome notification. +TEST_F(WelcomeNotificationTest, FirstRunShowRegularNotification) { + EXPECT_FALSE( + profile()->GetPrefs()->GetBoolean(prefs::kWelcomeNotificationDismissed)); + + ShowRegularNotification(); + + EXPECT_TRUE(message_center()->add_notification_calls() == 0); + EXPECT_TRUE(message_center()->remove_notification_calls() == 0); + EXPECT_FALSE( + profile()->GetPrefs()->GetBoolean(prefs::kWelcomeNotificationDismissed)); +} + +// Show a Chrome Now notification. Expect that WelcomeNotification will +// show a welcome notification. +TEST_F(WelcomeNotificationTest, FirstRunChromeNowNotification) { + EXPECT_FALSE( + profile()->GetPrefs()->GetBoolean(prefs::kWelcomeNotificationDismissed)); + + ShowChromeNowNotification(); + + EXPECT_TRUE(message_center()->add_notification_calls() == 1); + EXPECT_TRUE(message_center()->remove_notification_calls() == 0); + EXPECT_FALSE( + profile()->GetPrefs()->GetBoolean(prefs::kWelcomeNotificationDismissed)); +} + +// Don't show a welcome notification if it was previously dismissed +TEST_F(WelcomeNotificationTest, WelcomeNotificationPreviouslyDismissed) { + profile()->GetPrefs()->SetBoolean(prefs::kWelcomeNotificationDismissed, true); + EXPECT_TRUE( + profile()->GetPrefs()->GetBoolean(prefs::kWelcomeNotificationDismissed)); + + ShowChromeNowNotification(); + + EXPECT_TRUE(message_center()->add_notification_calls() == 0); + EXPECT_TRUE(message_center()->remove_notification_calls() == 0); + EXPECT_TRUE( + profile()->GetPrefs()->GetBoolean(prefs::kWelcomeNotificationDismissed)); +} + +// Show a Chrome Now notification and dismiss it. +// Expect welcome toast dismissed to be true. +TEST_F(WelcomeNotificationTest, DismissWelcomeNotification) { + EXPECT_FALSE( + profile()->GetPrefs()->GetBoolean(prefs::kWelcomeNotificationDismissed)); + + ShowChromeNowNotification(); + message_center()->CloseCurrentNotification(); + FlushMessageLoop(); + + EXPECT_TRUE(message_center()->add_notification_calls() == 1); + EXPECT_TRUE(message_center()->remove_notification_calls() == 1); + EXPECT_TRUE( + profile()->GetPrefs()->GetBoolean(prefs::kWelcomeNotificationDismissed)); +} + +// Show a Chrome Now notification and dismiss it via a synced preference change. +// Expect welcome toast dismissed to be true. +TEST_F(WelcomeNotificationTest, SyncedDismissalWelcomeNotification) { + EXPECT_FALSE( + profile()->GetPrefs()->GetBoolean(prefs::kWelcomeNotificationDismissed)); + + ShowChromeNowNotification(); + profile()->GetPrefs()->SetBoolean(prefs::kWelcomeNotificationDismissed, true); + + EXPECT_TRUE(message_center()->add_notification_calls() == 1); + EXPECT_TRUE(message_center()->remove_notification_calls() == 1); + EXPECT_TRUE( + profile()->GetPrefs()->GetBoolean(prefs::kWelcomeNotificationDismissed)); +} diff --git a/chrome/browser/password_manager/password_generation_manager.cc b/chrome/browser/password_manager/password_generation_manager.cc index d00683ee03..cc058c3e8e 100644 --- a/chrome/browser/password_manager/password_generation_manager.cc +++ b/chrome/browser/password_manager/password_generation_manager.cc @@ -31,26 +31,11 @@ DEFINE_WEB_CONTENTS_USER_DATA_KEY(PasswordGenerationManager); PasswordGenerationManager::PasswordGenerationManager( content::WebContents* contents) - : content::WebContentsObserver(contents), - enabled_(false), - weak_factory_(this) { - RegisterWithSyncService(); -} + : content::WebContentsObserver(contents) {} PasswordGenerationManager::~PasswordGenerationManager() {} // static -void PasswordGenerationManager::CreateForWebContents( - content::WebContents* contents) { - content::WebContentsUserData<PasswordGenerationManager>:: - CreateForWebContents(contents); - - // Start observing changes to relevant prefs. This is not called in the - // constructor so that it's not enabled in testing. - FromWebContents(contents)->SetUpPrefChangeRegistrar(); -} - -// static void PasswordGenerationManager::RegisterProfilePrefs( user_prefs::PrefRegistrySyncable* registry) { registry->RegisterBooleanPref( @@ -74,31 +59,10 @@ void PasswordGenerationManager::DetectAccountCreationForms( } } } - SendAccountCreationFormsToRenderer(web_contents()->GetRenderViewHost(), - account_creation_forms); -} - -void PasswordGenerationManager::RegisterWithSyncService() { - Profile* profile = Profile::FromBrowserContext( - web_contents()->GetBrowserContext()); - ProfileSyncService* sync_service = - ProfileSyncServiceFactory::GetForProfile(profile); - if (sync_service) - sync_service->AddObserver(this); -} - -void PasswordGenerationManager::SetUpPrefChangeRegistrar() { - registrar_.Init(Profile::FromBrowserContext( - web_contents()->GetBrowserContext())->GetPrefs()); - registrar_.Add( - prefs::kPasswordGenerationEnabled, - base::Bind(&PasswordGenerationManager::OnPrefStateChanged, - weak_factory_.GetWeakPtr())); -} - -void PasswordGenerationManager::RenderViewCreated( - content::RenderViewHost* host) { - UpdateState(host, true); + if (!account_creation_forms.empty() && IsGenerationEnabled()) { + SendAccountCreationFormsToRenderer(web_contents()->GetRenderViewHost(), + account_creation_forms); + } } bool PasswordGenerationManager::OnMessageReceived(const IPC::Message& message) { @@ -112,42 +76,21 @@ bool PasswordGenerationManager::OnMessageReceived(const IPC::Message& message) { return handled; } -void PasswordGenerationManager::WebContentsDestroyed( - content::WebContents* contents) { - Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); - ProfileSyncService* sync_service = - ProfileSyncServiceFactory::GetForProfile(profile); - if (sync_service && sync_service->HasObserver(this)) - sync_service->RemoveObserver(this); -} - -void PasswordGenerationManager::OnPrefStateChanged() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - if (web_contents() && web_contents()->GetRenderViewHost()) - UpdateState(web_contents()->GetRenderViewHost(), false); -} - -void PasswordGenerationManager::OnStateChanged() { - // It is possible for sync state to change during tab contents destruction. - // In this case, we don't need to update the renderer since it's going away. - if (web_contents() && web_contents()->GetRenderViewHost()) - UpdateState(web_contents()->GetRenderViewHost(), false); -} - // In order for password generation to be enabled, we need to make sure: // (1) Password sync is enabled, // (2) Password manager is enabled, and // (3) Password generation preference check box is checked. -void PasswordGenerationManager::UpdateState(content::RenderViewHost* host, - bool new_renderer) { +bool PasswordGenerationManager::IsGenerationEnabled() const { + if (!web_contents()) + return false; + Profile* profile = Profile::FromBrowserContext( web_contents()->GetBrowserContext()); - bool saving_passwords_enabled = - PasswordManager::FromWebContents(web_contents())->IsSavingEnabled(); - - bool preference_checked = profile->GetPrefs()->GetBoolean( - prefs::kPasswordGenerationEnabled); + if (!PasswordManager::FromWebContents(web_contents())->IsSavingEnabled()) { + DVLOG(2) << "Generation disabled because password saving is disabled"; + return false; + } bool password_sync_enabled = false; ProfileSyncService* sync_service = @@ -157,21 +100,17 @@ void PasswordGenerationManager::UpdateState(content::RenderViewHost* host, password_sync_enabled = (sync_service->HasSyncSetupCompleted() && sync_set.Has(syncer::PASSWORDS)); } + if (!password_sync_enabled) { + DVLOG(2) << "Generation disabled because passwords are not being synced"; + return false; + } - bool new_enabled = (password_sync_enabled && - saving_passwords_enabled && - preference_checked); - - if (new_enabled != enabled_ || new_renderer) { - enabled_ = new_enabled; - SendStateToRenderer(host, enabled_); + if (!profile->GetPrefs()->GetBoolean(prefs::kPasswordGenerationEnabled)) { + DVLOG(2) << "Generation disabled by user"; + return false; } -} -void PasswordGenerationManager::SendStateToRenderer( - content::RenderViewHost* host, bool enabled) { - host->Send(new AutofillMsg_PasswordGenerationEnabled(host->GetRoutingID(), - enabled)); + return true; } void PasswordGenerationManager::SendAccountCreationFormsToRenderer( diff --git a/chrome/browser/password_manager/password_generation_manager.h b/chrome/browser/password_manager/password_generation_manager.h index 585ec9e522..c9382bbfd0 100644 --- a/chrome/browser/password_manager/password_generation_manager.h +++ b/chrome/browser/password_manager/password_generation_manager.h @@ -7,9 +7,6 @@ #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" -#include "base/prefs/pref_change_registrar.h" -#include "chrome/browser/sync/profile_sync_service_observer.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_user_data.h" @@ -35,20 +32,19 @@ class PrefRegistrySyncable; // not be enabled regardless of the above criteria without the switch being // present. // -// When enabled we will send a message enabling this feature in the renderer, -// which will show an icon next to password fields which we think are associated -// with account creation. This class also manages the popup which is created -// if the user chooses to generate a password. +// This class is used to determine what forms we should offer to generate +// passwords for and manages the popup which is created if the user chooses to +// generate a password. class PasswordGenerationManager - : public ProfileSyncServiceObserver, - public content::WebContentsObserver, + : public content::WebContentsObserver, public content::WebContentsUserData<PasswordGenerationManager> { public: - static void CreateForWebContents(content::WebContents* contents); static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); virtual ~PasswordGenerationManager(); // Detect account creation forms from forms with autofill type annotated. + // Will send a message to the renderer if we find a correctly annotated form + // and the feature is enabled. void DetectAccountCreationForms( const std::vector<autofill::FormStructure*>& forms); @@ -60,30 +56,13 @@ class PasswordGenerationManager friend class PasswordGenerationManagerTest; // WebContentsObserver: - virtual void RenderViewCreated(content::RenderViewHost* host) OVERRIDE; virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; - virtual void WebContentsDestroyed(content::WebContents* contents) OVERRIDE; - // ProfileSyncServiceObserver: - virtual void OnStateChanged() OVERRIDE; - - // Add ourselves as an observer to the sync service to be informed of changes - // to the password sync state. - void RegisterWithSyncService(); - - // Start watching for changes to the password generation enabled pref. - void SetUpPrefChangeRegistrar(); - void OnPrefStateChanged(); - - // Determines current state of password generation and sends this information - // to the renderer if it is different from |enabled_| or if |new_renderer| - // is true. - void UpdateState(content::RenderViewHost* host, bool new_renderer); - - // Sends a message to the renderer enabling or disabling this feature. This - // is a separate function to aid in testing. - virtual void SendStateToRenderer(content::RenderViewHost* host, bool enabled); + // Determines current state of password generation + bool IsGenerationEnabled() const; + // Sends a message to the renderer specifying form(s) that we should enable + // password generation on. This is a separate function to aid in testing. virtual void SendAccountCreationFormsToRenderer( content::RenderViewHost* host, const std::vector<autofill::FormData>& forms); @@ -95,15 +74,6 @@ class PasswordGenerationManager int max_length, const autofill::PasswordForm& form); - // Whether password generation is enabled. - bool enabled_; - - // Listens for changes to the state of the password generation pref. - PrefChangeRegistrar registrar_; - - // For vending a weak_ptr for |registrar_|. - base::WeakPtrFactory<PasswordGenerationManager> weak_factory_; - // Controls how passwords are generated. scoped_ptr<autofill::PasswordGenerator> password_generator_; diff --git a/chrome/browser/password_manager/password_generation_manager_unittest.cc b/chrome/browser/password_manager/password_generation_manager_unittest.cc index 54c2b666ba..4dd5934dd7 100644 --- a/chrome/browser/password_manager/password_generation_manager_unittest.cc +++ b/chrome/browser/password_manager/password_generation_manager_unittest.cc @@ -42,11 +42,6 @@ class TestPasswordGenerationManager : public PasswordGenerationManager { : PasswordGenerationManager(contents) {} virtual ~TestPasswordGenerationManager() {} - virtual void SendStateToRenderer(content::RenderViewHost* host, - bool enabled) OVERRIDE { - sent_states_.push_back(enabled); - } - virtual void SendAccountCreationFormsToRenderer( content::RenderViewHost* host, const std::vector<autofill::FormData>& forms) OVERRIDE { @@ -54,24 +49,15 @@ class TestPasswordGenerationManager : public PasswordGenerationManager { sent_account_creation_forms_.begin(), forms.begin(), forms.end()); } - const std::vector<bool>& GetSentStates() { - return sent_states_; - } - const std::vector<autofill::FormData>& GetSentAccountCreationForms() { return sent_account_creation_forms_; } - void ClearSentStates() { - sent_states_.clear(); - } - void ClearSentAccountCreationForms() { sent_account_creation_forms_.clear(); } private: - std::vector<bool> sent_states_; std::vector<autofill::FormData> sent_account_creation_forms_; DISALLOW_COPY_AND_ASSIGN(TestPasswordGenerationManager); @@ -91,8 +77,8 @@ class PasswordGenerationManagerTest : public ChromeRenderViewHostTestHarness { ChromeRenderViewHostTestHarness::TearDown(); } - void UpdateState(bool new_renderer) { - password_generation_manager_->UpdateState(NULL, new_renderer); + bool IsGenerationEnabled() { + return password_generation_manager_->IsGenerationEnabled(); } void DetectAccountCreationForms( @@ -115,7 +101,7 @@ class IncognitoPasswordGenerationManagerTest : } }; -TEST_F(PasswordGenerationManagerTest, UpdateState) { +TEST_F(PasswordGenerationManagerTest, IsGenerationEnabled) { PasswordManagerDelegateImpl::CreateForWebContents(web_contents()); PasswordManager::CreateForWebContentsAndDelegate( web_contents(), @@ -133,95 +119,39 @@ TEST_F(PasswordGenerationManagerTest, UpdateState) { preferred_set.Put(syncer::PASSWORDS); sync_service->ChangePreferredDataTypes(preferred_set); - // Enabled state remains false, should not sent. + // Pref is false, should not be enabled. prefs->SetBoolean(prefs::kPasswordGenerationEnabled, false); - UpdateState(false); - EXPECT_EQ(0u, password_generation_manager_->GetSentStates().size()); + EXPECT_FALSE(IsGenerationEnabled()); - // Enabled state from false to true, should sent true. + // Pref is true, should be enabled. prefs->SetBoolean(prefs::kPasswordGenerationEnabled, true); - UpdateState(false); - EXPECT_EQ(1u, password_generation_manager_->GetSentStates().size()); - EXPECT_TRUE(password_generation_manager_->GetSentStates()[0]); - password_generation_manager_->ClearSentStates(); + EXPECT_TRUE(IsGenerationEnabled()); - // Enabled states remains true, should not sent. - prefs->SetBoolean(prefs::kPasswordGenerationEnabled, true); - UpdateState(false); - EXPECT_EQ(0u, password_generation_manager_->GetSentStates().size()); + // Change syncing preferences to not include passwords. Generation should + // be disabled. + preferred_set.Put(syncer::EXTENSIONS); + preferred_set.Remove(syncer::PASSWORDS); + sync_service->ChangePreferredDataTypes(preferred_set); + EXPECT_FALSE(IsGenerationEnabled()); - // Enabled states from true to false, should sent false. - prefs->SetBoolean(prefs::kPasswordGenerationEnabled, false); - UpdateState(false); - EXPECT_EQ(1u, password_generation_manager_->GetSentStates().size()); - EXPECT_FALSE(password_generation_manager_->GetSentStates()[0]); - password_generation_manager_->ClearSentStates(); - - // When a new render_view is created, we send the state even if it's the - // same. - UpdateState(true); - EXPECT_EQ(1u, password_generation_manager_->GetSentStates().size()); - EXPECT_FALSE(password_generation_manager_->GetSentStates()[0]); - password_generation_manager_->ClearSentStates(); + // Disable syncing. Generation should also be disabled. + sync_service->DisableForUser(); + EXPECT_FALSE(IsGenerationEnabled()); } -TEST_F(PasswordGenerationManagerTest, UpdatePasswordSyncState) { +TEST_F(PasswordGenerationManagerTest, DetectAccountCreationForms) { + // Setup so that IsGenerationEnabled() returns true. PasswordManagerDelegateImpl::CreateForWebContents(web_contents()); PasswordManager::CreateForWebContentsAndDelegate( web_contents(), PasswordManagerDelegateImpl::FromWebContents(web_contents())); - PrefService* prefs = profile()->GetPrefs(); - - // Allow this test to control what should get synced. - prefs->SetBoolean(prefs::kSyncKeepEverythingSynced, false); - // Always set password generation enabled check box so we can test the - // behavior of password sync. - prefs->SetBoolean(prefs::kPasswordGenerationEnabled, true); - - // Sync some things, but not passwords. Shouldn't send anything since - // password generation is disabled by default. ProfileSyncService* sync_service = ProfileSyncServiceFactory::GetForProfile( profile()); sync_service->SetSyncSetupCompleted(); - syncer::ModelTypeSet preferred_set; - preferred_set.Put(syncer::EXTENSIONS); - preferred_set.Put(syncer::PREFERENCES); - sync_service->ChangePreferredDataTypes(preferred_set); - syncer::ModelTypeSet new_set = sync_service->GetActiveDataTypes(); - UpdateState(false); - EXPECT_EQ(0u, password_generation_manager_->GetSentStates().size()); - // Now sync passwords. - preferred_set.Put(syncer::PASSWORDS); - sync_service->ChangePreferredDataTypes(preferred_set); - UpdateState(false); - EXPECT_EQ(1u, password_generation_manager_->GetSentStates().size()); - EXPECT_TRUE(password_generation_manager_->GetSentStates()[0]); - password_generation_manager_->ClearSentStates(); - - // Add some additional synced state. Nothing should be sent. - preferred_set.Put(syncer::THEMES); - sync_service->ChangePreferredDataTypes(preferred_set); - UpdateState(false); - EXPECT_EQ(0u, password_generation_manager_->GetSentStates().size()); + profile()->GetPrefs()->SetBoolean(prefs::kPasswordGenerationEnabled, true); - // Disable syncing. This should disable the feature. - sync_service->DisableForUser(); - UpdateState(false); - EXPECT_EQ(1u, password_generation_manager_->GetSentStates().size()); - EXPECT_FALSE(password_generation_manager_->GetSentStates()[0]); - password_generation_manager_->ClearSentStates(); - - // When a new render_view is created, we send the state even if it's the - // same. - UpdateState(true); - EXPECT_EQ(1u, password_generation_manager_->GetSentStates().size()); - EXPECT_FALSE(password_generation_manager_->GetSentStates()[0]); - password_generation_manager_->ClearSentStates(); -} - -TEST_F(PasswordGenerationManagerTest, DetectAccountCreationForms) { autofill::FormData login_form; login_form.origin = GURL("http://www.yahoo.com/login/"); autofill::FormFieldData username; @@ -273,8 +203,8 @@ TEST_F(PasswordGenerationManagerTest, DetectAccountCreationForms) { TEST_F(IncognitoPasswordGenerationManagerTest, UpdatePasswordSyncStateIncognito) { - // Disable password manager by going incognito, and enable syncing. The - // feature should still be disabled, and nothing will be sent. + // Disable password manager by going incognito. Even though syncing is + // enabled, generation should still be disabled. PasswordManagerDelegateImpl::CreateForWebContents(web_contents()); PasswordManager::CreateForWebContentsAndDelegate( web_contents(), @@ -290,6 +220,5 @@ TEST_F(IncognitoPasswordGenerationManagerTest, browser_sync::SyncPrefs sync_prefs(profile()->GetPrefs()); sync_prefs.SetSyncSetupCompleted(); - UpdateState(false); - EXPECT_EQ(0u, password_generation_manager_->GetSentStates().size()); + EXPECT_FALSE(IsGenerationEnabled()); } diff --git a/chrome/browser/password_manager/password_store.h b/chrome/browser/password_manager/password_store.h index 266d3a60a6..c1446705ee 100644 --- a/chrome/browser/password_manager/password_store.h +++ b/chrome/browser/password_manager/password_store.h @@ -18,6 +18,7 @@ class PasswordStore; class PasswordStoreConsumer; +class PasswordSyncableService; class Task; namespace autofill { @@ -133,10 +134,15 @@ class PasswordStore protected: friend class base::RefCountedThreadSafe<PasswordStore>; + // Sync's interaction with password store needs to be syncrhonous. + // Since the synchronous methods are private these classes are made + // as friends. This can be fixed by moving the private impl to a new + // class. See http://crbug.com/307750 friend class browser_sync::PasswordChangeProcessor; friend class browser_sync::PasswordDataTypeController; friend class browser_sync::PasswordModelAssociator; friend class browser_sync::PasswordModelWorker; + friend class PasswordSyncableService; friend void passwords_helper::AddLogin(PasswordStore*, const autofill::PasswordForm&); friend void passwords_helper::RemoveLogin(PasswordStore*, diff --git a/chrome/browser/password_manager/password_syncable_service.cc b/chrome/browser/password_manager/password_syncable_service.cc index 0db6b2fa2e..67355e2a96 100644 --- a/chrome/browser/password_manager/password_syncable_service.cc +++ b/chrome/browser/password_manager/password_syncable_service.cc @@ -5,9 +5,83 @@ #include "chrome/browser/password_manager/password_syncable_service.h" #include "base/location.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/password_manager/password_store.h" +#include "components/autofill/core/common/password_form.h" +#include "net/base/escape.h" #include "sync/api/sync_error_factory.h" -PasswordSyncableService::PasswordSyncableService() { +namespace { + +// Converts the |PasswordSpecifics| obtained from sync to an +// object of type |PasswordForm|. +void ExtractPasswordFromSpecifics( + const sync_pb::PasswordSpecificsData& password, + autofill::PasswordForm* new_password) { + new_password->scheme = + static_cast<autofill::PasswordForm::Scheme>(password.scheme()); + new_password->signon_realm = password.signon_realm(); + new_password->origin = GURL(password.origin()); + new_password->action = GURL(password.action()); + new_password->username_element = + UTF8ToUTF16(password.username_element()); + new_password->password_element = + UTF8ToUTF16(password.password_element()); + new_password->username_value = + UTF8ToUTF16(password.username_value()); + new_password->password_value = + UTF8ToUTF16(password.password_value()); + new_password->ssl_valid = password.ssl_valid(); + new_password->preferred = password.preferred(); + new_password->date_created = + base::Time::FromInternalValue(password.date_created()); + new_password->blacklisted_by_user = + password.blacklisted(); +} + +// Merges the sync password (obtained from the password specifics) and +// local password and stores the output in the |new_password_form| pointer. +bool MergeLocalAndSyncPasswords( + const sync_pb::PasswordSpecificsData& password_specifics, + const autofill::PasswordForm& password_form, + autofill::PasswordForm* new_password_form) { + if (password_specifics.scheme() == password_form.scheme && + password_form.signon_realm == password_specifics.signon_realm() && + password_form.origin.spec() == password_specifics.origin() && + password_form.action.spec() == password_specifics.action() && + UTF16ToUTF8(password_form.username_element) == + password_specifics.username_element() && + UTF16ToUTF8(password_form.password_element) == + password_specifics.password_element() && + UTF16ToUTF8(password_form.username_value) == + password_specifics.username_value() && + UTF16ToUTF8(password_form.password_value) == + password_specifics.password_value() && + password_specifics.ssl_valid() == password_form.ssl_valid && + password_specifics.preferred() == password_form.preferred && + password_specifics.date_created() == + password_form.date_created.ToInternalValue() && + password_specifics.blacklisted() == + password_form.blacklisted_by_user) { + return false; + } + + // If the passwords differ, take the one that was created more recently. + if (base::Time::FromInternalValue(password_specifics.date_created()) <= + password_form.date_created) { + *new_password_form = password_form; + } else { + ExtractPasswordFromSpecifics(password_specifics, new_password_form); + } + + return true; +} + +} // namespace + +PasswordSyncableService::PasswordSyncableService( + scoped_refptr<PasswordStore> password_store) + : password_store_(password_store) { } PasswordSyncableService::~PasswordSyncableService() {} @@ -47,3 +121,88 @@ syncer::SyncError PasswordSyncableService::ProcessSyncChanges( return error; } +void PasswordSyncableService::WriteToPasswordStore( + PasswordForms* new_entries, + PasswordForms* updated_entries) { + for (std::vector<autofill::PasswordForm*>::const_iterator it = + new_entries->begin(); + it != new_entries->end(); + ++it) { + password_store_->AddLoginImpl(**it); + } + + for (std::vector<autofill::PasswordForm*>::const_iterator it = + updated_entries->begin(); + it != updated_entries->end(); + ++it) { + password_store_->UpdateLoginImpl(**it); + } + + if (!new_entries->empty() || !updated_entries->empty()) { + // We have to notify password store observers of the change by hand since + // we use internal password store interfaces to make changes synchronously. + password_store_->PostNotifyLoginsChanged(); + } +} + +syncer::SyncData PasswordSyncableService::CreateSyncData( + const autofill::PasswordForm& password_form) { + sync_pb::EntitySpecifics password_data; + sync_pb::PasswordSpecificsData* password_specifics = + password_data.mutable_password()->mutable_client_only_encrypted_data(); + password_specifics->set_scheme(password_form.scheme); + password_specifics->set_signon_realm(password_form.signon_realm); + password_specifics->set_origin(password_form.origin.spec()); + password_specifics->set_action(password_form.action.spec()); + password_specifics->set_username_element( + UTF16ToUTF8(password_form.username_element)); + password_specifics->set_password_element( + UTF16ToUTF8(password_form.password_element)); + password_specifics->set_username_value( + UTF16ToUTF8(password_form.username_value)); + password_specifics->set_password_value( + UTF16ToUTF8(password_form.password_value)); + password_specifics->set_ssl_valid(password_form.ssl_valid); + password_specifics->set_preferred(password_form.preferred); + password_specifics->set_date_created( + password_form.date_created.ToInternalValue()); + password_specifics->set_blacklisted(password_form.blacklisted_by_user); + + std::string tag = MakeTag(*password_specifics); + return syncer::SyncData::CreateLocalData(tag, tag, password_data); +} + +// static +std::string PasswordSyncableService::MakeTag( + const std::string& origin_url, + const std::string& username_element, + const std::string& username_value, + const std::string& password_element, + const std::string& signon_realm) { + return net::EscapePath(origin_url) + "|" + + net::EscapePath(username_element) + "|" + + net::EscapePath(username_value) + "|" + + net::EscapePath(password_element) + "|" + + net::EscapePath(signon_realm); +} + +// static +std::string PasswordSyncableService::MakeTag( + const autofill::PasswordForm& password) { + return MakeTag(password.origin.spec(), + UTF16ToUTF8(password.username_element), + UTF16ToUTF8(password.username_value), + UTF16ToUTF8(password.password_element), + password.signon_realm); +} + +// static +std::string PasswordSyncableService::MakeTag( + const sync_pb::PasswordSpecificsData& password) { + return MakeTag(password.origin(), + password.username_element(), + password.username_value(), + password.password_element(), + password.signon_realm()); +} + diff --git a/chrome/browser/password_manager/password_syncable_service.h b/chrome/browser/password_manager/password_syncable_service.h index 4b9c27edec..dd32b2c323 100644 --- a/chrome/browser/password_manager/password_syncable_service.h +++ b/chrome/browser/password_manager/password_syncable_service.h @@ -5,21 +5,36 @@ #ifndef CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_SYNCABLE_SERVICE_H__ #define CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_SYNCABLE_SERVICE_H__ +#include <string> +#include <vector> + #include "base/memory/scoped_ptr.h" #include "sync/api/sync_change.h" #include "sync/api/sync_data.h" #include "sync/api/sync_error.h" #include "sync/api/syncable_service.h" +#include "sync/protocol/password_specifics.pb.h" +#include "sync/protocol/sync.pb.h" + +namespace autofill { +struct PasswordForm; +} namespace syncer { class SyncErrorFactory; -} // namespace syncer +} class PasswordStore; class PasswordSyncableService : public syncer::SyncableService { public: - PasswordSyncableService(); + // TODO(lipalani) - The |PasswordStore| should outlive + // |PasswordSyncableService| and there should be a code + // guarantee to that effect. Currently this object is not instantiated. + // When this class is completed and instantiated the object lifetime + // guarantee will be implemented. + explicit PasswordSyncableService( + scoped_refptr<PasswordStore> password_store); virtual ~PasswordSyncableService(); // syncer::SyncableServiceImplementations @@ -35,9 +50,35 @@ class PasswordSyncableService : public syncer::SyncableService { const tracked_objects::Location& from_here, const syncer::SyncChangeList& change_list) OVERRIDE; + // Returns the unique tag that will serve as the sync identifier for the + // |password| entry. + static std::string MakeTag(const autofill::PasswordForm& password); + static std::string MakeTag(const sync_pb::PasswordSpecificsData& password); + static std::string MakeTag(const std::string& origin_url, + const std::string& username_element, + const std::string& username_value, + const std::string& password_element, + const std::string& signon_realm); + private: + typedef std::vector<autofill::PasswordForm*> PasswordForms; + + // Use the |PasswordStore| APIs to add and update entries. + void WriteToPasswordStore(PasswordForms* new_entries, + PasswordForms* udpated_entries); + + // Converts the |PasswordForm| to |SyncData| suitable for syncing. + syncer::SyncData CreateSyncData(const autofill::PasswordForm& password); + + // The factory that creates sync errors. |SyncError| has rich data + // suitable for debugging. scoped_ptr<syncer::SyncErrorFactory> sync_error_factory_; + + // |SyncProcessor| will mirror the |PasswordStore| changes in the sync db. scoped_ptr<syncer::SyncChangeProcessor> sync_processor_; + + // The password store that adds/updates/deletes password entries. + scoped_refptr<PasswordStore> password_store_; }; #endif // CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_SYNCABLE_SERVICE_H__ diff --git a/chrome/browser/plugins/chrome_plugin_service_filter.cc b/chrome/browser/plugins/chrome_plugin_service_filter.cc index c4663801ad..992cd2b2e1 100644 --- a/chrome/browser/plugins/chrome_plugin_service_filter.cc +++ b/chrome/browser/plugins/chrome_plugin_service_filter.cc @@ -67,6 +67,12 @@ void ChromePluginServiceFilter::UnrestrictPlugin( restricted_plugins_.erase(plugin_path); } +bool ChromePluginServiceFilter::IsPluginRestricted( + const base::FilePath& plugin_path) { + base::AutoLock auto_lock(lock_); + return restricted_plugins_.find(plugin_path) != restricted_plugins_.end(); +} + bool ChromePluginServiceFilter::IsPluginAvailable( int render_process_id, int render_view_id, diff --git a/chrome/browser/plugins/chrome_plugin_service_filter.h b/chrome/browser/plugins/chrome_plugin_service_filter.h index 513b1ff49b..ed53fdba96 100644 --- a/chrome/browser/plugins/chrome_plugin_service_filter.h +++ b/chrome/browser/plugins/chrome_plugin_service_filter.h @@ -58,6 +58,9 @@ class ChromePluginServiceFilter : public content::PluginServiceFilter, // Authorizes all plug-ins for a given process. void AuthorizeAllPlugins(int render_process_id); + // Returns whether the plugin is found in restricted_plugins_. + bool IsPluginRestricted(const base::FilePath& plugin_path); + // PluginServiceFilter implementation: virtual bool IsPluginAvailable( int render_process_id, diff --git a/chrome/browser/plugins/plugin_info_message_filter.cc b/chrome/browser/plugins/plugin_info_message_filter.cc index 92be4e9190..d75fe336a0 100644 --- a/chrome/browser/plugins/plugin_info_message_filter.cc +++ b/chrome/browser/plugins/plugin_info_message_filter.cc @@ -254,7 +254,9 @@ void PluginInfoMessageFilter::Context::DecidePluginStatus( plugin_setting != CONTENT_SETTING_BLOCK && uses_default_content_setting && plugin_policy != PluginPrefs::POLICY_ENABLED && - group_policy != PluginPrefs::POLICY_ENABLED) { + group_policy != PluginPrefs::POLICY_ENABLED && + !ChromePluginServiceFilter::GetInstance()->IsPluginRestricted( + plugin.path)) { status->value = ChromeViewHostMsg_GetPluginInfo_Status::kBlocked; return; } diff --git a/chrome/browser/policy/DEPS b/chrome/browser/policy/DEPS index 31e99bfee6..0532c01027 100644 --- a/chrome/browser/policy/DEPS +++ b/chrome/browser/policy/DEPS @@ -44,16 +44,8 @@ specific_include_rules = { r"configuration_policy_handler\.cc": [ "+chrome/browser/chrome_notification_types.h", - "+chrome/browser/download/download_prefs.h", - "+chrome/browser/extensions/external_policy_loader.h", "+chrome/browser/policy/policy_path_parser.h", - "+chrome/browser/prefs/proxy_config_dictionary.h", - "+chrome/browser/prefs/proxy_prefs.h", "+chrome/browser/prefs/session_startup_pref.h", - "+chrome/browser/search_engines/search_terms_data.h", - "+chrome/browser/search_engines/template_url.h", - "+chrome/common/extensions/extension.h", - "+content/public/browser/notification_service.h", ], r"configuration_policy_handler\.h": [ @@ -61,13 +53,8 @@ specific_include_rules = { "+chrome/common/content_settings.h", ], - r"configuration_policy_handler_unittest\.cc": [ - "+chrome/browser/extensions/external_policy_loader.h", - ], - r"configuration_policy_pref_store_unittest\.cc": [ "+chrome/browser/prefs/incognito_mode_prefs.h", - "+chrome/browser/prefs/proxy_config_dictionary.h", "+chrome/common/content_settings.h", "+chrome/common/pref_names.h", ], diff --git a/chrome/browser/policy/browser_policy_connector.cc b/chrome/browser/policy/browser_policy_connector.cc index 6eb74685bf..4d99d09dda 100644 --- a/chrome/browser/policy/browser_policy_connector.cc +++ b/chrome/browser/policy/browser_policy_connector.cc @@ -17,11 +17,13 @@ #include "base/path_service.h" #include "base/prefs/pref_registry_simple.h" #include "base/prefs/pref_service.h" +#include "base/sequenced_task_runner.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/sys_info.h" +#include "base/threading/sequenced_worker_pool.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/policy/async_policy_provider.h" #include "chrome/browser/policy/cloud/cloud_policy_client.h" @@ -36,6 +38,7 @@ #include "chrome/common/chrome_switches.h" #include "chrome/common/chrome_version_info.h" #include "chrome/common/pref_names.h" +#include "components/policy/core/common/policy_pref_names.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/content_client.h" #include "google_apis/gaia/gaia_auth_util.h" @@ -70,7 +73,7 @@ #include "chrome/browser/chromeos/settings/device_settings_service.h" #include "chromeos/chromeos_paths.h" #include "chromeos/chromeos_switches.h" -#include "chromeos/cryptohome/cryptohome_library.h" +#include "chromeos/cryptohome/system_salt_getter.h" #include "chromeos/dbus/cryptohome_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/network/network_handler.h" @@ -98,6 +101,14 @@ const char kDefaultDeviceManagementServerUrl[] = // Used in BrowserPolicyConnector::SetPolicyProviderForTesting. ConfigurationPolicyProvider* g_testing_provider = NULL; +// Helper that returns a new SequencedTaskRunner backed by the blocking pool. +// Each SequencedTaskRunner returned is independent from the others. +scoped_refptr<base::SequencedTaskRunner> GetBackgroundTaskRunner() { + base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool(); + CHECK(pool); + return pool->GetSequencedTaskRunnerWithShutdownBehavior( + pool->GetSequenceToken(), base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); +} #if defined(OS_MACOSX) && !defined(OS_IOS) base::FilePath GetManagedPolicyPath() { @@ -198,11 +209,11 @@ BrowserPolicyConnector::BrowserPolicyConnector() platform_provider_.reset(CreatePlatformProvider()); #if defined(OS_CHROMEOS) - // CryptohomeLibrary or DBusThreadManager may be uninitialized on unit tests. + // SystemSaltGetter or DBusThreadManager may be uninitialized on unit tests. - // TODO(satorux): Remove CryptohomeLibrary::IsInitialized() when it's ready + // TODO(satorux): Remove SystemSaltGetter::IsInitialized() when it's ready // (removing it now breaks tests). crbug.com/141016. - if (chromeos::CryptohomeLibrary::IsInitialized() && + if (chromeos::SystemSaltGetter::IsInitialized() && chromeos::DBusThreadManager::IsInitialized()) { chromeos::CryptohomeClient* cryptohome_client = chromeos::DBusThreadManager::Get()->GetCryptohomeClient(); @@ -216,11 +227,13 @@ BrowserPolicyConnector::BrowserPolicyConnector() scoped_ptr<DeviceCloudPolicyStoreChromeOS> device_cloud_policy_store( new DeviceCloudPolicyStoreChromeOS( chromeos::DeviceSettingsService::Get(), - install_attributes_.get())); + install_attributes_.get(), + GetBackgroundTaskRunner())); device_cloud_policy_manager_.reset( new DeviceCloudPolicyManagerChromeOS( device_cloud_policy_store.Pass(), base::MessageLoopProxy::current(), + GetBackgroundTaskRunner(), install_attributes_.get())); } #endif @@ -281,7 +294,9 @@ void BrowserPolicyConnector::Init( new DeviceLocalAccountPolicyService( chromeos::DBusThreadManager::Get()->GetSessionManagerClient(), chromeos::DeviceSettingsService::Get(), - chromeos::CrosSettings::Get())); + chromeos::CrosSettings::Get(), + GetBackgroundTaskRunner(), + GetBackgroundTaskRunner())); device_local_account_policy_service_->Connect( device_management_service_.get()); } @@ -502,7 +517,7 @@ bool BrowserPolicyConnector::IsNonEnterpriseUser(const std::string& username) { // static void BrowserPolicyConnector::RegisterPrefs(PrefRegistrySimple* registry) { registry->RegisterIntegerPref( - prefs::kUserPolicyRefreshRate, + policy_prefs::kUserPolicyRefreshRate, CloudPolicyRefreshScheduler::kDefaultRefreshDelayMs); #if defined(OS_CHROMEOS) registry->RegisterIntegerPref( diff --git a/chrome/browser/policy/cloud/DEPS b/chrome/browser/policy/cloud/DEPS index e806e8000c..cab579f5e9 100644 --- a/chrome/browser/policy/cloud/DEPS +++ b/chrome/browser/policy/cloud/DEPS @@ -37,27 +37,8 @@ specific_include_rules = { ], # TODO(joaodasilva): remove these exceptions. - r"cloud_external_data_manager_base_unittest\.cc": [ - "+content/public/test/test_browser_thread_bundle.h", - ], - - r"cloud_policy_client_registration_helper\.cc": [ - "+chrome/browser/signin/android_profile_oauth2_token_service.h", - "+chrome/browser/signin/oauth2_token_service.h", - ], - - r"cloud_policy_constants\.cc": [ - "+chrome/common/chrome_switches.h", - ], - - r"cloud_policy_core_unittest\.cc": [ - "+chrome/browser/prefs/browser_prefs.h", - "+chrome/common/pref_names.h", - ], - r"cloud_policy_invalidator\.cc": [ "+chrome/browser/invalidation/invalidation_service.h", - "+chrome/common/chrome_switches.h", ], r"cloud_policy_invalidator_unittest\.cc": [ @@ -68,32 +49,7 @@ specific_include_rules = { "+chrome/browser/invalidation/fake_invalidation_service.h", ], - r"cloud_policy_refresh_scheduler\.cc": [ - "+chrome/browser/chrome_notification_types.h", - "+chrome/common/chrome_switches.h", - "+content/public/browser/notification_details.h", - ], - - r"cloud_policy_refresh_scheduler_unittest\.cc": [ - "+chrome/browser/prefs/browser_prefs.h", - ], - - r"cloud_policy_validator\.cc": [ - "+content/public/browser/browser_thread.h", - ], - - r"cloud_policy_validator_unittest\.cc": [ - "+content/public/test/test_browser_thread.h", - ], - - r"component_cloud_policy_service_unittest\.cc": [ - "+content/public/browser/browser_thread.h", - "+content/public/test/test_browser_thread.h", - ], - r"test_request_interceptor\.cc": [ - "+content/public/browser/browser_thread.h", - "+content/public/test/test_utils.h", "+content/test/net/url_request_mock_http_job.h", ], @@ -106,7 +62,6 @@ specific_include_rules = { r"user_cloud_policy_invalidator_factory\.cc": [ "+chrome/browser/invalidation/invalidation_service_factory.h", "+chrome/browser/profiles/profile.h", - "+chrome/common/chrome_switches.h", "+chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h", "+chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.h", ], @@ -116,20 +71,14 @@ specific_include_rules = { "+content/public/browser/notification_registrar.h", ], - r"user_cloud_policy_manager\.cc": [ - "+chrome/common/pref_names.h", - ], - r"user_cloud_policy_manager_factory\.cc": [ "+chrome/browser/profiles/profile.h", - "+chrome/common/chrome_switches.h", ], r"user_cloud_policy_store\.cc": [ "+chrome/browser/profiles/profile.h", "+chrome/browser/signin/signin_manager.h", "+chrome/browser/signin/signin_manager_factory.h", - "+content/public/browser/browser_thread.h", ], r"user_cloud_policy_store_unittest\.cc": [ @@ -138,6 +87,5 @@ specific_include_rules = { "+chrome/browser/signin/signin_manager_factory.h", "+chrome/common/pref_names.h", "+chrome/test/base/testing_profile.h", - "+content/public/test/test_browser_thread.h", ], } diff --git a/chrome/browser/policy/cloud/cloud_policy_constants.cc b/chrome/browser/policy/cloud/cloud_policy_constants.cc index 33c8b4f49e..bc45143e5a 100644 --- a/chrome/browser/policy/cloud/cloud_policy_constants.cc +++ b/chrome/browser/policy/cloud/cloud_policy_constants.cc @@ -5,7 +5,7 @@ #include "chrome/browser/policy/cloud/cloud_policy_constants.h" #include "base/command_line.h" -#include "chrome/common/chrome_switches.h" +#include "components/policy/core/common/policy_switches.h" namespace policy { diff --git a/chrome/browser/policy/cloud/cloud_policy_core_unittest.cc b/chrome/browser/policy/cloud/cloud_policy_core_unittest.cc index ef0313ed60..7d57735141 100644 --- a/chrome/browser/policy/cloud/cloud_policy_core_unittest.cc +++ b/chrome/browser/policy/cloud/cloud_policy_core_unittest.cc @@ -6,13 +6,13 @@ #include "base/basictypes.h" #include "base/message_loop/message_loop.h" +#include "base/prefs/pref_registry_simple.h" #include "base/prefs/testing_pref_service.h" #include "chrome/browser/policy/cloud/cloud_policy_constants.h" #include "chrome/browser/policy/cloud/cloud_policy_refresh_scheduler.h" #include "chrome/browser/policy/cloud/mock_cloud_policy_client.h" #include "chrome/browser/policy/cloud/mock_cloud_policy_store.h" -#include "chrome/browser/prefs/browser_prefs.h" -#include "chrome/common/pref_names.h" +#include "components/policy/core/common/policy_pref_names.h" #include "testing/gtest/include/gtest/gtest.h" namespace policy { @@ -29,7 +29,9 @@ class CloudPolicyCoreTest : public testing::Test, refresh_scheduler_started_callback_count_(0), core_disconnecting_callback_count_(0), bad_callback_count_(0) { - chrome::RegisterLocalState(prefs_.registry()); + prefs_.registry()->RegisterIntegerPref( + policy_prefs::kUserPolicyRefreshRate, + CloudPolicyRefreshScheduler::kDefaultRefreshDelayMs); core_.AddObserver(this); } @@ -120,11 +122,11 @@ TEST_F(CloudPolicyCoreTest, RefreshScheduler) { int default_refresh_delay = core_.refresh_scheduler()->refresh_delay(); const int kRefreshRate = 1000 * 60 * 60; - prefs_.SetInteger(prefs::kUserPolicyRefreshRate, kRefreshRate); - core_.TrackRefreshDelayPref(&prefs_, prefs::kUserPolicyRefreshRate); + prefs_.SetInteger(policy_prefs::kUserPolicyRefreshRate, kRefreshRate); + core_.TrackRefreshDelayPref(&prefs_, policy_prefs::kUserPolicyRefreshRate); EXPECT_EQ(kRefreshRate, core_.refresh_scheduler()->refresh_delay()); - prefs_.ClearPref(prefs::kUserPolicyRefreshRate); + prefs_.ClearPref(policy_prefs::kUserPolicyRefreshRate); EXPECT_EQ(default_refresh_delay, core_.refresh_scheduler()->refresh_delay()); EXPECT_EQ(1, core_connected_callback_count_); diff --git a/chrome/browser/policy/cloud/cloud_policy_invalidator.cc b/chrome/browser/policy/cloud/cloud_policy_invalidator.cc index c55409b40d..b71b934042 100644 --- a/chrome/browser/policy/cloud/cloud_policy_invalidator.cc +++ b/chrome/browser/policy/cloud/cloud_policy_invalidator.cc @@ -18,7 +18,7 @@ #include "chrome/browser/policy/cloud/cloud_policy_client.h" #include "chrome/browser/policy/cloud/cloud_policy_refresh_scheduler.h" #include "chrome/browser/policy/cloud/enterprise_metrics.h" -#include "chrome/common/chrome_switches.h" +#include "components/policy/core/common/policy_switches.h" #include "policy/policy_constants.h" #include "sync/notifier/object_id_invalidation_map.h" diff --git a/chrome/browser/policy/cloud/cloud_policy_manager_browsertest.cc b/chrome/browser/policy/cloud/cloud_policy_manager_browsertest.cc index 8c4967cd95..7e0085c5a4 100644 --- a/chrome/browser/policy/cloud/cloud_policy_manager_browsertest.cc +++ b/chrome/browser/policy/cloud/cloud_policy_manager_browsertest.cc @@ -16,6 +16,7 @@ #include "chrome/browser/ui/browser.h" #include "chrome/common/chrome_switches.h" #include "chrome/test/base/in_process_browser_test.h" +#include "content/public/browser/browser_thread.h" #include "net/base/net_errors.h" #include "net/url_request/url_request_context_getter.h" #include "testing/gmock/include/gmock/gmock.h" @@ -31,6 +32,7 @@ #include "chrome/browser/signin/signin_manager_factory.h" #endif +using content::BrowserThread; using testing::AnyNumber; using testing::InvokeWithoutArgs; using testing::Mock; @@ -57,7 +59,9 @@ class CloudPolicyManagerTest : public InProcessBrowserTest { ASSERT_TRUE(PolicyServiceIsEmpty(g_browser_process->policy_service())) << "Pre-existing policies in this machine will make this test fail."; - interceptor_.reset(new TestRequestInterceptor("localhost")); + interceptor_.reset(new TestRequestInterceptor( + "localhost", + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))); BrowserPolicyConnector* connector = g_browser_process->browser_policy_connector(); diff --git a/chrome/browser/policy/cloud/cloud_policy_refresh_scheduler.cc b/chrome/browser/policy/cloud/cloud_policy_refresh_scheduler.cc index 1af14990d6..aaf1c2964c 100644 --- a/chrome/browser/policy/cloud/cloud_policy_refresh_scheduler.cc +++ b/chrome/browser/policy/cloud/cloud_policy_refresh_scheduler.cc @@ -14,10 +14,8 @@ #include "base/sequenced_task_runner.h" #include "base/time/default_tick_clock.h" #include "base/time/tick_clock.h" -#include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/policy/cloud/cloud_policy_constants.h" -#include "chrome/common/chrome_switches.h" -#include "content/public/browser/notification_details.h" +#include "components/policy/core/common/policy_switches.h" namespace policy { diff --git a/chrome/browser/policy/cloud/cloud_policy_refresh_scheduler.h b/chrome/browser/policy/cloud/cloud_policy_refresh_scheduler.h index 675f964890..a3d121b246 100644 --- a/chrome/browser/policy/cloud/cloud_policy_refresh_scheduler.h +++ b/chrome/browser/policy/cloud/cloud_policy_refresh_scheduler.h @@ -37,7 +37,7 @@ class CloudPolicyRefreshScheduler static const int64 kRefreshDelayMinMs; static const int64 kRefreshDelayMaxMs; - // |client|, |store| and |prefs| pointers must stay valid throughout the + // |client| and |store| pointers must stay valid throughout the // lifetime of CloudPolicyRefreshScheduler. CloudPolicyRefreshScheduler( CloudPolicyClient* client, diff --git a/chrome/browser/policy/cloud/cloud_policy_refresh_scheduler_unittest.cc b/chrome/browser/policy/cloud/cloud_policy_refresh_scheduler_unittest.cc index 6f5cc3529c..85a256dce2 100644 --- a/chrome/browser/policy/cloud/cloud_policy_refresh_scheduler_unittest.cc +++ b/chrome/browser/policy/cloud/cloud_policy_refresh_scheduler_unittest.cc @@ -12,7 +12,6 @@ #include "chrome/browser/policy/cloud/cloud_policy_refresh_scheduler.h" #include "chrome/browser/policy/cloud/mock_cloud_policy_client.h" #include "chrome/browser/policy/cloud/mock_cloud_policy_store.h" -#include "chrome/browser/prefs/browser_prefs.h" #include "policy/policy_constants.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chrome/browser/policy/cloud/cloud_policy_validator.cc b/chrome/browser/policy/cloud/cloud_policy_validator.cc index b45392e201..71649cea9a 100644 --- a/chrome/browser/policy/cloud/cloud_policy_validator.cc +++ b/chrome/browser/policy/cloud/cloud_policy_validator.cc @@ -6,10 +6,10 @@ #include "base/bind_helpers.h" #include "base/message_loop/message_loop.h" +#include "base/sequenced_task_runner.h" #include "base/stl_util.h" #include "chrome/browser/policy/cloud/cloud_policy_constants.h" #include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h" -#include "content/public/browser/browser_thread.h" #include "crypto/signature_verifier.h" #include "google_apis/gaia/gaia_auth_util.h" @@ -114,7 +114,8 @@ void CloudPolicyValidatorBase::ValidateAgainstCurrentPolicy( CloudPolicyValidatorBase::CloudPolicyValidatorBase( scoped_ptr<em::PolicyFetchResponse> policy_response, - google::protobuf::MessageLite* payload) + google::protobuf::MessageLite* payload, + scoped_refptr<base::SequencedTaskRunner> background_task_runner) : status_(VALIDATION_OK), policy_(policy_response.Pass()), payload_(payload), @@ -123,12 +124,13 @@ CloudPolicyValidatorBase::CloudPolicyValidatorBase( timestamp_not_after_(0), timestamp_option_(TIMESTAMP_REQUIRED), dm_token_option_(DM_TOKEN_REQUIRED), - allow_key_rotation_(false) {} + allow_key_rotation_(false), + background_task_runner_(background_task_runner) {} void CloudPolicyValidatorBase::PostValidationTask( const base::Closure& completion_callback) { - content::BrowserThread::PostTask( - content::BrowserThread::FILE, FROM_HERE, + background_task_runner_->PostTask( + FROM_HERE, base::Bind(&CloudPolicyValidatorBase::PerformValidation, base::Passed(scoped_ptr<CloudPolicyValidatorBase>(this)), base::MessageLoop::current()->message_loop_proxy(), diff --git a/chrome/browser/policy/cloud/cloud_policy_validator.h b/chrome/browser/policy/cloud/cloud_policy_validator.h index 2635e4a29a..5b6ed4bd4f 100644 --- a/chrome/browser/policy/cloud/cloud_policy_validator.h +++ b/chrome/browser/policy/cloud/cloud_policy_validator.h @@ -13,6 +13,7 @@ #include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/sequenced_task_runner.h" #include "base/time/time.h" #include "chrome/browser/policy/proto/cloud/chrome_extension_policy.pb.h" #include "policy/proto/cloud_policy.pb.h" @@ -35,7 +36,7 @@ class PolicyFetchResponse; namespace policy { // Helper class that implements the gory details of validating a policy blob. -// Since signature checks are expensive, validation can happen on the FILE +// Since signature checks are expensive, validation can happen on a background // thread. The pattern is to create a validator, configure its behavior through // the ValidateXYZ() functions, and then call StartValidation(). Alternatively, // RunValidation() can be used to perform validation on the current thread. @@ -171,7 +172,8 @@ class CloudPolicyValidatorBase { // valid for the lifetime of the validator. CloudPolicyValidatorBase( scoped_ptr<enterprise_management::PolicyFetchResponse> policy_response, - google::protobuf::MessageLite* payload); + google::protobuf::MessageLite* payload, + scoped_refptr<base::SequencedTaskRunner> background_task_runner); // Posts an asynchronous calls to PerformValidation, which will eventually // report its result via |completion_callback|. @@ -237,6 +239,7 @@ class CloudPolicyValidatorBase { std::string settings_entity_id_; std::string key_; bool allow_key_rotation_; + scoped_refptr<base::SequencedTaskRunner> background_task_runner_; DISALLOW_COPY_AND_ASSIGN(CloudPolicyValidatorBase); }; @@ -252,11 +255,15 @@ class CloudPolicyValidator : public CloudPolicyValidatorBase { virtual ~CloudPolicyValidator() {} // Creates a new validator. + // |background_task_runner| is optional; if RunValidation() is used directly + // and StartValidation() is not used then it can be NULL. static CloudPolicyValidator<PayloadProto>* Create( - scoped_ptr<enterprise_management::PolicyFetchResponse> policy_response) { + scoped_ptr<enterprise_management::PolicyFetchResponse> policy_response, + scoped_refptr<base::SequencedTaskRunner> background_task_runner) { return new CloudPolicyValidator( policy_response.Pass(), - scoped_ptr<PayloadProto>(new PayloadProto())); + scoped_ptr<PayloadProto>(new PayloadProto()), + background_task_runner); } scoped_ptr<PayloadProto>& payload() { @@ -274,8 +281,11 @@ class CloudPolicyValidator : public CloudPolicyValidatorBase { private: CloudPolicyValidator( scoped_ptr<enterprise_management::PolicyFetchResponse> policy_response, - scoped_ptr<PayloadProto> payload) - : CloudPolicyValidatorBase(policy_response.Pass(), payload.get()), + scoped_ptr<PayloadProto> payload, + scoped_refptr<base::SequencedTaskRunner> background_task_runner) + : CloudPolicyValidatorBase(policy_response.Pass(), + payload.get(), + background_task_runner), payload_(payload.Pass()) {} scoped_ptr<PayloadProto> payload_; diff --git a/chrome/browser/policy/cloud/cloud_policy_validator_unittest.cc b/chrome/browser/policy/cloud/cloud_policy_validator_unittest.cc index 1efe9a6c30..88f9f48440 100644 --- a/chrome/browser/policy/cloud/cloud_policy_validator_unittest.cc +++ b/chrome/browser/policy/cloud/cloud_policy_validator_unittest.cc @@ -7,11 +7,11 @@ #include "base/bind.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" +#include "base/message_loop/message_loop_proxy.h" #include "base/strings/string_util.h" #include "chrome/browser/policy/cloud/cloud_policy_constants.h" #include "chrome/browser/policy/cloud/cloud_policy_validator.h" #include "chrome/browser/policy/cloud/policy_builder.h" -#include "content/public/test/test_browser_thread.h" #include "crypto/rsa_private_key.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -39,8 +39,7 @@ class CloudPolicyValidatorTest : public testing::Test { timestamp_option_(CloudPolicyValidatorBase::TIMESTAMP_REQUIRED), ignore_missing_dm_token_(CloudPolicyValidatorBase::DM_TOKEN_REQUIRED), allow_key_rotation_(true), - existing_dm_token_(PolicyBuilder::kFakeToken), - file_thread_(content::BrowserThread::FILE, &loop_) { + existing_dm_token_(PolicyBuilder::kFakeToken) { policy_.SetDefaultNewSigningKey(); } @@ -64,8 +63,8 @@ class CloudPolicyValidatorTest : public testing::Test { PolicyBuilder::CreateTestSigningKey()->ExportPublicKey(&public_key)); policy_.Build(); - UserCloudPolicyValidator* validator = - UserCloudPolicyValidator::Create(policy_.GetCopy()); + UserCloudPolicyValidator* validator = UserCloudPolicyValidator::Create( + policy_.GetCopy(), base::MessageLoopProxy::current()); validator->ValidateTimestamp(timestamp_, timestamp_, timestamp_option_); validator->ValidateUsername(PolicyBuilder::kFakeUsername); @@ -103,8 +102,6 @@ class CloudPolicyValidatorTest : public testing::Test { private: MOCK_METHOD1(ValidationCompletion, void(UserCloudPolicyValidator* validator)); - content::TestBrowserThread file_thread_; - DISALLOW_COPY_AND_ASSIGN(CloudPolicyValidatorTest); }; diff --git a/chrome/browser/policy/cloud/component_cloud_policy_service_unittest.cc b/chrome/browser/policy/cloud/component_cloud_policy_service_unittest.cc index deb7724cd7..b4fa459e80 100644 --- a/chrome/browser/policy/cloud/component_cloud_policy_service_unittest.cc +++ b/chrome/browser/policy/cloud/component_cloud_policy_service_unittest.cc @@ -25,8 +25,6 @@ #include "chrome/browser/policy/proto/cloud/chrome_extension_policy.pb.h" #include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h" #include "components/policy/core/common/schema.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/test/test_browser_thread.h" #include "net/url_request/test_url_fetcher_factory.h" #include "net/url_request/url_fetcher_delegate.h" #include "net/url_request/url_request_context.h" @@ -99,9 +97,7 @@ class TestURLRequestContextGetter : public net::URLRequestContextGetter { class ComponentCloudPolicyServiceTest : public testing::Test { protected: - ComponentCloudPolicyServiceTest() - : ui_thread_(content::BrowserThread::UI, &loop_), - file_thread_(content::BrowserThread::FILE, &loop_) {} + ComponentCloudPolicyServiceTest() {} virtual void SetUp() OVERRIDE { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); @@ -200,8 +196,6 @@ class ComponentCloudPolicyServiceTest : public testing::Test { } base::MessageLoop loop_; - content::TestBrowserThread ui_thread_; - content::TestBrowserThread file_thread_; base::ScopedTempDir temp_dir_; scoped_refptr<TestURLRequestContextGetter> request_context_; net::TestURLFetcherFactory fetcher_factory_; diff --git a/chrome/browser/policy/cloud/component_cloud_policy_store.cc b/chrome/browser/policy/cloud/component_cloud_policy_store.cc index 334f9d6dc2..e049c3b011 100644 --- a/chrome/browser/policy/cloud/component_cloud_policy_store.cc +++ b/chrome/browser/policy/cloud/component_cloud_policy_store.cc @@ -261,7 +261,8 @@ bool ComponentCloudPolicyStore::ValidateProto( return false; scoped_ptr<ComponentCloudPolicyValidator> validator( - ComponentCloudPolicyValidator::Create(proto.Pass())); + ComponentCloudPolicyValidator::Create( + proto.Pass(), scoped_refptr<base::SequencedTaskRunner>())); validator->ValidateUsername(username_); validator->ValidateDMToken(dm_token_, ComponentCloudPolicyValidator::DM_TOKEN_REQUIRED); diff --git a/chrome/browser/policy/cloud/device_management_service_browsertest.cc b/chrome/browser/policy/cloud/device_management_service_browsertest.cc index 478629d2a2..1319048f82 100644 --- a/chrome/browser/policy/cloud/device_management_service_browsertest.cc +++ b/chrome/browser/policy/cloud/device_management_service_browsertest.cc @@ -98,7 +98,9 @@ class DeviceManagementServiceIntegrationTest const em::DeviceManagementResponse&)); std::string InitCannedResponse() { - interceptor_.reset(new TestRequestInterceptor("localhost")); + interceptor_.reset(new TestRequestInterceptor( + "localhost", + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))); return "http://localhost"; } diff --git a/chrome/browser/policy/cloud/mock_user_cloud_policy_store.cc b/chrome/browser/policy/cloud/mock_user_cloud_policy_store.cc index 9c687ecb1a..75b5366192 100644 --- a/chrome/browser/policy/cloud/mock_user_cloud_policy_store.cc +++ b/chrome/browser/policy/cloud/mock_user_cloud_policy_store.cc @@ -7,7 +7,9 @@ namespace policy { MockUserCloudPolicyStore::MockUserCloudPolicyStore() - : UserCloudPolicyStore(NULL, base::FilePath()) {} + : UserCloudPolicyStore(NULL, + base::FilePath(), + scoped_refptr<base::SequencedTaskRunner>()) {} MockUserCloudPolicyStore::~MockUserCloudPolicyStore() {} diff --git a/chrome/browser/policy/cloud/test_request_interceptor.cc b/chrome/browser/policy/cloud/test_request_interceptor.cc index 9e254a4a94..55492c06df 100644 --- a/chrome/browser/policy/cloud/test_request_interceptor.cc +++ b/chrome/browser/policy/cloud/test_request_interceptor.cc @@ -10,8 +10,8 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/memory/scoped_ptr.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/test/test_utils.h" +#include "base/run_loop.h" +#include "base/sequenced_task_runner.h" #include "content/test/net/url_request_mock_http_job.h" #include "net/base/net_errors.h" #include "net/base/upload_bytes_element_reader.h" @@ -29,12 +29,6 @@ namespace policy { namespace { -// Helper to execute a |task| on IO, and return only after it has completed. -void PostToIOAndWait(const base::Closure& task) { - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, task); - content::RunAllPendingInMessageLoop(content::BrowserThread::IO); -} - // Helper callback for jobs that should fail with a network |error|. net::URLRequestJob* ErrorJobCallback(int error, net::URLRequest* request, @@ -154,7 +148,8 @@ net::URLRequestJob* RegisterJobCallback( class TestRequestInterceptor::Delegate : public net::URLRequestJobFactory::ProtocolHandler { public: - explicit Delegate(const std::string& hostname); + Delegate(const std::string& hostname, + scoped_refptr<base::SequencedTaskRunner> io_task_runner); virtual ~Delegate(); // ProtocolHandler implementation: @@ -167,6 +162,7 @@ class TestRequestInterceptor::Delegate private: const std::string hostname_; + scoped_refptr<base::SequencedTaskRunner> io_task_runner_; // The queue of pending callbacks. 'mutable' because MaybeCreateJob() is a // const method; it can't reenter though, because it runs exclusively on @@ -174,15 +170,17 @@ class TestRequestInterceptor::Delegate mutable std::queue<JobCallback> pending_job_callbacks_; }; -TestRequestInterceptor::Delegate::Delegate(const std::string& hostname) - : hostname_(hostname) {} +TestRequestInterceptor::Delegate::Delegate( + const std::string& hostname, + scoped_refptr<base::SequencedTaskRunner> io_task_runner) + : hostname_(hostname), io_task_runner_(io_task_runner) {} TestRequestInterceptor::Delegate::~Delegate() {} net::URLRequestJob* TestRequestInterceptor::Delegate::MaybeCreateJob( net::URLRequest* request, net::NetworkDelegate* network_delegate) const { - CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + CHECK(io_task_runner_->RunsTasksOnCurrentThread()); if (request->url().host() != hostname_) { // Reject requests to other servers. @@ -202,19 +200,21 @@ net::URLRequestJob* TestRequestInterceptor::Delegate::MaybeCreateJob( void TestRequestInterceptor::Delegate::GetPendingSize( size_t* pending_size) const { - CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + CHECK(io_task_runner_->RunsTasksOnCurrentThread()); *pending_size = pending_job_callbacks_.size(); } void TestRequestInterceptor::Delegate::PushJobCallback( const JobCallback& callback) { - CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + CHECK(io_task_runner_->RunsTasksOnCurrentThread()); pending_job_callbacks_.push(callback); } -TestRequestInterceptor::TestRequestInterceptor(const std::string& hostname) - : hostname_(hostname) { - delegate_ = new Delegate(hostname_); +TestRequestInterceptor::TestRequestInterceptor(const std::string& hostname, + scoped_refptr<base::SequencedTaskRunner> io_task_runner) + : hostname_(hostname), + io_task_runner_(io_task_runner) { + delegate_ = new Delegate(hostname_, io_task_runner_); scoped_ptr<net::URLRequestJobFactory::ProtocolHandler> handler(delegate_); PostToIOAndWait( base::Bind(&net::URLRequestFilter::AddHostnameProtocolHandler, @@ -232,7 +232,7 @@ TestRequestInterceptor::~TestRequestInterceptor() { "http", hostname_)); } -size_t TestRequestInterceptor::GetPendingSize() const { +size_t TestRequestInterceptor::GetPendingSize() { size_t pending_size = std::numeric_limits<size_t>::max(); PostToIOAndWait(base::Bind(&Delegate::GetPendingSize, base::Unretained(delegate_), @@ -270,4 +270,17 @@ TestRequestInterceptor::JobCallback TestRequestInterceptor::FileJob( return base::Bind(&FileJobCallback, file_path); } +void TestRequestInterceptor::PostToIOAndWait(const base::Closure& task) { + io_task_runner_->PostTask(FROM_HERE, task); + base::RunLoop run_loop; + io_task_runner_->PostTask( + FROM_HERE, + base::Bind( + base::IgnoreResult(&base::MessageLoopProxy::PostTask), + base::MessageLoopProxy::current(), + FROM_HERE, + run_loop.QuitClosure())); + run_loop.Run(); +} + } // namespace policy diff --git a/chrome/browser/policy/cloud/test_request_interceptor.h b/chrome/browser/policy/cloud/test_request_interceptor.h index 7838587fc1..ec22353087 100644 --- a/chrome/browser/policy/cloud/test_request_interceptor.h +++ b/chrome/browser/policy/cloud/test_request_interceptor.h @@ -9,10 +9,12 @@ #include "base/basictypes.h" #include "base/callback.h" +#include "base/memory/ref_counted.h" #include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h" namespace base { class FilePath; +class SequencedTaskRunner; } namespace net { @@ -34,11 +36,13 @@ class TestRequestInterceptor { net::URLRequestJob*(net::URLRequest*, net::NetworkDelegate*)> JobCallback; // Will intercept request to |hostname| made over HTTP. - explicit TestRequestInterceptor(const std::string& hostname); + TestRequestInterceptor( + const std::string& hostname, + scoped_refptr<base::SequencedTaskRunner> io_task_runner); virtual ~TestRequestInterceptor(); // Returns the number of pending callback jobs that haven't been used yet. - size_t GetPendingSize() const; + size_t GetPendingSize(); // Queues |callback| to handle a request to |hostname_|. Each callback is // used only once, and in the order that they're pushed. @@ -66,8 +70,13 @@ class TestRequestInterceptor { private: class Delegate; + // Helper to execute a |task| on IO, and return only after it has completed. + void PostToIOAndWait(const base::Closure& task); + const std::string hostname_; + scoped_refptr<base::SequencedTaskRunner> io_task_runner_; + // Owned by URLRequestFilter. This handle is valid on IO and only while the // interceptor is valid. Delegate* delegate_; diff --git a/chrome/browser/policy/cloud/user_cloud_policy_invalidator_factory.cc b/chrome/browser/policy/cloud/user_cloud_policy_invalidator_factory.cc index c96a160c35..b73fa05eee 100644 --- a/chrome/browser/policy/cloud/user_cloud_policy_invalidator_factory.cc +++ b/chrome/browser/policy/cloud/user_cloud_policy_invalidator_factory.cc @@ -8,8 +8,8 @@ #include "chrome/browser/invalidation/invalidation_service_factory.h" #include "chrome/browser/policy/cloud/user_cloud_policy_invalidator.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/common/chrome_switches.h" #include "components/browser_context_keyed_service/browser_context_dependency_manager.h" +#include "components/policy/core/common/policy_switches.h" #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h" #include "chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.h" diff --git a/chrome/browser/policy/cloud/user_cloud_policy_manager.cc b/chrome/browser/policy/cloud/user_cloud_policy_manager.cc index 03a00da4aa..6cc03b2c37 100644 --- a/chrome/browser/policy/cloud/user_cloud_policy_manager.cc +++ b/chrome/browser/policy/cloud/user_cloud_policy_manager.cc @@ -13,7 +13,7 @@ #include "chrome/browser/policy/cloud/user_cloud_policy_manager_factory.h" #include "chrome/browser/policy/cloud/user_cloud_policy_store.h" #include "chrome/browser/policy/policy_types.h" -#include "chrome/common/pref_names.h" +#include "components/policy/core/common/policy_pref_names.h" #include "net/url_request/url_request_context_getter.h" namespace em = enterprise_management; @@ -52,7 +52,8 @@ void UserCloudPolicyManager::Connect( scoped_ptr<CloudPolicyClient> client) { core()->Connect(client.Pass()); core()->StartRefreshScheduler(); - core()->TrackRefreshDelayPref(local_state, prefs::kUserPolicyRefreshRate); + core()->TrackRefreshDelayPref(local_state, + policy_prefs::kUserPolicyRefreshRate); if (external_data_manager_) external_data_manager_->Connect(request_context); } diff --git a/chrome/browser/policy/cloud/user_cloud_policy_manager_factory.cc b/chrome/browser/policy/cloud/user_cloud_policy_manager_factory.cc index bad6ef8d9a..0279328446 100644 --- a/chrome/browser/policy/cloud/user_cloud_policy_manager_factory.cc +++ b/chrome/browser/policy/cloud/user_cloud_policy_manager_factory.cc @@ -4,14 +4,13 @@ #include "chrome/browser/policy/cloud/user_cloud_policy_manager_factory.h" -#include "base/command_line.h" #include "base/logging.h" #include "base/message_loop/message_loop_proxy.h" +#include "base/sequenced_task_runner.h" #include "chrome/browser/policy/cloud/cloud_external_data_manager.h" #include "chrome/browser/policy/cloud/user_cloud_policy_manager.h" #include "chrome/browser/policy/cloud/user_cloud_policy_store.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/common/chrome_switches.h" #include "components/browser_context_keyed_service/browser_context_dependency_manager.h" namespace policy { @@ -29,9 +28,12 @@ UserCloudPolicyManager* UserCloudPolicyManagerFactory::GetForProfile( // static scoped_ptr<UserCloudPolicyManager> - UserCloudPolicyManagerFactory::CreateForProfile(Profile* profile, - bool force_immediate_load) { - return GetInstance()->CreateManagerForProfile(profile, force_immediate_load); +UserCloudPolicyManagerFactory::CreateForProfile( + Profile* profile, + bool force_immediate_load, + scoped_refptr<base::SequencedTaskRunner> background_task_runner) { + return GetInstance()->CreateManagerForProfile( + profile, force_immediate_load, background_task_runner); } UserCloudPolicyManagerFactory::UserCloudPolicyManagerFactory() @@ -52,12 +54,10 @@ UserCloudPolicyManager* UserCloudPolicyManagerFactory::GetManagerForProfile( scoped_ptr<UserCloudPolicyManager> UserCloudPolicyManagerFactory::CreateManagerForProfile( Profile* profile, - bool force_immediate_load) { - if (CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableCloudPolicyOnSignin)) { - return scoped_ptr<UserCloudPolicyManager>(); - } - scoped_ptr<UserCloudPolicyStore> store(UserCloudPolicyStore::Create(profile)); + bool force_immediate_load, + scoped_refptr<base::SequencedTaskRunner> background_task_runner) { + scoped_ptr<UserCloudPolicyStore> store( + UserCloudPolicyStore::Create(profile, background_task_runner)); if (force_immediate_load) store->LoadImmediately(); scoped_ptr<UserCloudPolicyManager> manager( diff --git a/chrome/browser/policy/cloud/user_cloud_policy_manager_factory.h b/chrome/browser/policy/cloud/user_cloud_policy_manager_factory.h index b1690a9492..ec9c2515b3 100644 --- a/chrome/browser/policy/cloud/user_cloud_policy_manager_factory.h +++ b/chrome/browser/policy/cloud/user_cloud_policy_manager_factory.h @@ -8,11 +8,16 @@ #include <map> #include "base/basictypes.h" +#include "base/memory/ref_counted.h" #include "base/memory/singleton.h" #include "components/browser_context_keyed_service/browser_context_keyed_base_factory.h" class Profile; +namespace base { +class SequencedTaskRunner; +} + namespace policy { class UserCloudPolicyManager; @@ -47,7 +52,8 @@ class UserCloudPolicyManagerFactory : public BrowserContextKeyedBaseFactory { // UserCloudPolicyStore at startup. static scoped_ptr<UserCloudPolicyManager> CreateForProfile( Profile* profile, - bool force_immediate_load); + bool force_immediate_load, + scoped_refptr<base::SequencedTaskRunner> background_task_runner); private: friend class UserCloudPolicyManager; @@ -60,7 +66,8 @@ class UserCloudPolicyManagerFactory : public BrowserContextKeyedBaseFactory { UserCloudPolicyManager* GetManagerForProfile(Profile* profile); scoped_ptr<UserCloudPolicyManager> CreateManagerForProfile( Profile* profile, - bool force_immediate_load); + bool force_immediate_load, + scoped_refptr<base::SequencedTaskRunner> background_task_runner); // BrowserContextKeyedBaseFactory: virtual void BrowserContextShutdown( diff --git a/chrome/browser/policy/cloud/user_cloud_policy_store.cc b/chrome/browser/policy/cloud/user_cloud_policy_store.cc index 981e87e1c4..cda8fb7f1c 100644 --- a/chrome/browser/policy/cloud/user_cloud_policy_store.cc +++ b/chrome/browser/policy/cloud/user_cloud_policy_store.cc @@ -6,12 +6,13 @@ #include "base/bind.h" #include "base/file_util.h" +#include "base/location.h" +#include "base/task_runner_util.h" #include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h" #include "chrome/browser/policy/proto/cloud/device_management_local.pb.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/signin_manager.h" #include "chrome/browser/signin/signin_manager_factory.h" -#include "content/public/browser/browser_thread.h" #include "policy/policy_constants.h" #include "policy/proto/cloud_policy.pb.h" @@ -68,11 +69,11 @@ policy::PolicyLoadResult LoadPolicyFromDisk(const base::FilePath& path) { } // Stores policy to the backing file (must be called via a task on -// the FILE thread). -void StorePolicyToDiskOnFileThread(const base::FilePath& path, - const em::PolicyFetchResponse& policy) { +// the background thread). +void StorePolicyToDiskOnBackgroundThread( + const base::FilePath& path, + const em::PolicyFetchResponse& policy) { DVLOG(1) << "Storing policy to " << path.value(); - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); std::string data; if (!policy.SerializeToString(&data)) { DLOG(WARNING) << "Failed to serialize policy data"; @@ -92,21 +93,25 @@ void StorePolicyToDiskOnFileThread(const base::FilePath& path, } // namespace -UserCloudPolicyStore::UserCloudPolicyStore(Profile* profile, - const base::FilePath& path) - : weak_factory_(this), +UserCloudPolicyStore::UserCloudPolicyStore( + Profile* profile, + const base::FilePath& path, + scoped_refptr<base::SequencedTaskRunner> background_task_runner) + : UserCloudPolicyStoreBase(background_task_runner), + weak_factory_(this), profile_(profile), - backing_file_path_(path) { -} + backing_file_path_(path) {} UserCloudPolicyStore::~UserCloudPolicyStore() {} // static scoped_ptr<UserCloudPolicyStore> UserCloudPolicyStore::Create( - Profile* profile) { + Profile* profile, + scoped_refptr<base::SequencedTaskRunner> background_task_runner) { base::FilePath path = profile->GetPath().Append(kPolicyDir).Append(kPolicyCacheFile); - return make_scoped_ptr(new UserCloudPolicyStore(profile, path)); + return make_scoped_ptr( + new UserCloudPolicyStore(profile, path, background_task_runner)); } void UserCloudPolicyStore::LoadImmediately() { @@ -120,11 +125,10 @@ void UserCloudPolicyStore::LoadImmediately() { } void UserCloudPolicyStore::Clear() { - content::BrowserThread::PostTask( - content::BrowserThread::FILE, FROM_HERE, - base::Bind(base::IgnoreResult(&base::DeleteFile), - backing_file_path_, - false)); + background_task_runner()->PostTask( + FROM_HERE, + base::Bind( + base::IgnoreResult(&base::DeleteFile), backing_file_path_, false)); policy_.reset(); policy_map_.Clear(); NotifyStoreLoaded(); @@ -137,8 +141,9 @@ void UserCloudPolicyStore::Load() { // Start a new Load operation and have us get called back when it is // complete. - content::BrowserThread::PostTaskAndReplyWithResult( - content::BrowserThread::FILE, FROM_HERE, + base::PostTaskAndReplyWithResult( + background_task_runner(), + FROM_HERE, base::Bind(&LoadPolicyFromDisk, backing_file_path_), base::Bind(&UserCloudPolicyStore::PolicyLoaded, weak_factory_.GetWeakPtr(), true)); @@ -146,7 +151,6 @@ void UserCloudPolicyStore::Load() { void UserCloudPolicyStore::PolicyLoaded(bool validate_in_background, PolicyLoadResult result) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); switch (result.status) { case LOAD_RESULT_LOAD_ERROR: status_ = STATUS_LOAD_ERROR; @@ -248,9 +252,9 @@ void UserCloudPolicyStore::StorePolicyAfterValidation( // Persist the validated policy (just fire a task - don't bother getting a // reply because we can't do anything if it fails). - content::BrowserThread::PostTask( - content::BrowserThread::FILE, FROM_HERE, - base::Bind(&StorePolicyToDiskOnFileThread, + background_task_runner()->PostTask( + FROM_HERE, + base::Bind(&StorePolicyToDiskOnBackgroundThread, backing_file_path_, *validator->policy())); InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass()); status_ = STATUS_OK; diff --git a/chrome/browser/policy/cloud/user_cloud_policy_store.h b/chrome/browser/policy/cloud/user_cloud_policy_store.h index 892fc23754..b1fcdbe5f9 100644 --- a/chrome/browser/policy/cloud/user_cloud_policy_store.h +++ b/chrome/browser/policy/cloud/user_cloud_policy_store.h @@ -15,6 +15,10 @@ class Profile; +namespace base { +class SequencedTaskRunner; +} + namespace policy { // Implements a cloud policy store that is stored in a simple file in the user's @@ -24,11 +28,16 @@ class UserCloudPolicyStore : public UserCloudPolicyStoreBase { public: // Creates a policy store associated with the user signed in to this // |profile|. - UserCloudPolicyStore(Profile* profile, const base::FilePath& policy_file); + UserCloudPolicyStore( + Profile* profile, + const base::FilePath& policy_file, + scoped_refptr<base::SequencedTaskRunner> background_task_runner); virtual ~UserCloudPolicyStore(); // Factory method for creating a UserCloudPolicyStore for |profile|. - static scoped_ptr<UserCloudPolicyStore> Create(Profile* profile); + static scoped_ptr<UserCloudPolicyStore> Create( + Profile* profile, + scoped_refptr<base::SequencedTaskRunner> background_task_runner); // Loads policy immediately on the current thread. Virtual for mocks. virtual void LoadImmediately(); diff --git a/chrome/browser/policy/cloud/user_cloud_policy_store_base.cc b/chrome/browser/policy/cloud/user_cloud_policy_store_base.cc index 2d097ba7e0..ee1daa6085 100644 --- a/chrome/browser/policy/cloud/user_cloud_policy_store_base.cc +++ b/chrome/browser/policy/cloud/user_cloud_policy_store_base.cc @@ -17,8 +17,9 @@ void DecodePolicy(const enterprise_management::CloudPolicySettings& policy, base::WeakPtr<CloudExternalDataManager> external_data_manager, PolicyMap* policies); -UserCloudPolicyStoreBase::UserCloudPolicyStoreBase() { -} +UserCloudPolicyStoreBase::UserCloudPolicyStoreBase( + scoped_refptr<base::SequencedTaskRunner> background_task_runner) + : background_task_runner_(background_task_runner) {} UserCloudPolicyStoreBase::~UserCloudPolicyStoreBase() { } @@ -28,7 +29,7 @@ scoped_ptr<UserCloudPolicyValidator> UserCloudPolicyStoreBase::CreateValidator( CloudPolicyValidatorBase::ValidateTimestampOption timestamp_option) { // Configure the validator. UserCloudPolicyValidator* validator = - UserCloudPolicyValidator::Create(policy.Pass()); + UserCloudPolicyValidator::Create(policy.Pass(), background_task_runner_); validator->ValidatePolicyType(GetChromeUserPolicyType()); validator->ValidateAgainstCurrentPolicy( policy_.get(), diff --git a/chrome/browser/policy/cloud/user_cloud_policy_store_base.h b/chrome/browser/policy/cloud/user_cloud_policy_store_base.h index e00a3d29a3..068002ecf0 100644 --- a/chrome/browser/policy/cloud/user_cloud_policy_store_base.h +++ b/chrome/browser/policy/cloud/user_cloud_policy_store_base.h @@ -8,18 +8,24 @@ #include <string> #include "base/basictypes.h" +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "chrome/browser/policy/cloud/cloud_policy_store.h" #include "chrome/browser/policy/cloud/cloud_policy_validator.h" +namespace base { +class SequencedTaskRunner; +} + namespace policy { // Base class that implements common cross-platform UserCloudPolicyStore // functionality. class UserCloudPolicyStoreBase : public CloudPolicyStore { public: - UserCloudPolicyStoreBase(); + explicit UserCloudPolicyStoreBase( + scoped_refptr<base::SequencedTaskRunner> background_task_runner); virtual ~UserCloudPolicyStoreBase(); protected: @@ -34,7 +40,14 @@ class UserCloudPolicyStoreBase : public CloudPolicyStore { scoped_ptr<enterprise_management::PolicyData> policy_data, scoped_ptr<enterprise_management::CloudPolicySettings> payload); + scoped_refptr<base::SequencedTaskRunner> background_task_runner() const { + return background_task_runner_; + } + private: + // Task runner for background file operations. + scoped_refptr<base::SequencedTaskRunner> background_task_runner_; + DISALLOW_COPY_AND_ASSIGN(UserCloudPolicyStoreBase); }; diff --git a/chrome/browser/policy/cloud/user_cloud_policy_store_unittest.cc b/chrome/browser/policy/cloud/user_cloud_policy_store_unittest.cc index e3f378f10d..2c2876cca0 100644 --- a/chrome/browser/policy/cloud/user_cloud_policy_store_unittest.cc +++ b/chrome/browser/policy/cloud/user_cloud_policy_store_unittest.cc @@ -7,6 +7,7 @@ #include "base/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/message_loop/message_loop.h" +#include "base/message_loop/message_loop_proxy.h" #include "base/prefs/pref_service.h" #include "base/run_loop.h" #include "chrome/browser/policy/cloud/mock_cloud_external_data_manager.h" @@ -17,7 +18,6 @@ #include "chrome/browser/signin/signin_manager_factory.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/testing_profile.h" -#include "content/public/test/test_browser_thread.h" #include "net/url_request/url_request_context_getter.h" #include "policy/policy_constants.h" #include "testing/gmock/include/gmock/gmock.h" @@ -42,8 +42,6 @@ class UserCloudPolicyStoreTest : public testing::Test { public: UserCloudPolicyStoreTest() : loop_(base::MessageLoop::TYPE_UI), - ui_thread_(content::BrowserThread::UI, &loop_), - file_thread_(content::BrowserThread::FILE, &loop_), profile_(new TestingProfile()) {} virtual void SetUp() OVERRIDE { @@ -54,7 +52,8 @@ class UserCloudPolicyStoreTest : public testing::Test { profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername, PolicyBuilder::kFakeUsername); signin->Initialize(profile_.get(), NULL); - store_.reset(new UserCloudPolicyStore(profile_.get(), policy_file())); + store_.reset(new UserCloudPolicyStore( + profile_.get(), policy_file(), loop_.message_loop_proxy())); external_data_manager_.reset(new MockCloudExternalDataManager); external_data_manager_->SetPolicyStore(store_.get()); store_->AddObserver(&observer_); @@ -104,8 +103,6 @@ class UserCloudPolicyStoreTest : public testing::Test { // |ui_thread_| and |file_thread_| share the same MessageLoop |loop_| so // callers can use RunLoop to manage both virtual threads. base::MessageLoop loop_; - content::TestBrowserThread ui_thread_; - content::TestBrowserThread file_thread_; scoped_ptr<TestingProfile> profile_; base::ScopedTempDir tmp_dir_; @@ -280,8 +277,8 @@ TEST_F(UserCloudPolicyStoreTest, StoreThenLoad) { RunUntilIdle(); // Now, make sure the policy can be read back in from a second store. - scoped_ptr<UserCloudPolicyStore> store2( - new UserCloudPolicyStore(profile_.get(), policy_file())); + scoped_ptr<UserCloudPolicyStore> store2(new UserCloudPolicyStore( + profile_.get(), policy_file(), loop_.message_loop_proxy())); store2->AddObserver(&observer_); EXPECT_CALL(observer_, OnStoreLoaded(store2.get())); store2->Load(); @@ -305,8 +302,8 @@ TEST_F(UserCloudPolicyStoreTest, StoreThenLoadImmediately) { RunUntilIdle(); // Now, make sure the policy can be read back in from a second store. - scoped_ptr<UserCloudPolicyStore> store2( - new UserCloudPolicyStore(profile_.get(), policy_file())); + scoped_ptr<UserCloudPolicyStore> store2(new UserCloudPolicyStore( + profile_.get(), policy_file(), loop_.message_loop_proxy())); store2->AddObserver(&observer_); EXPECT_CALL(observer_, OnStoreLoaded(store2.get())); store2->LoadImmediately(); // Should load without running the message loop. @@ -345,8 +342,8 @@ TEST_F(UserCloudPolicyStoreTest, LoadValidationError) { SigninManagerFactory::GetForProfile(profile_.get())->SetAuthenticatedUsername( "foobar@foobar.com"); - scoped_ptr<UserCloudPolicyStore> store2( - new UserCloudPolicyStore(profile_.get(), policy_file())); + scoped_ptr<UserCloudPolicyStore> store2(new UserCloudPolicyStore( + profile_.get(), policy_file(), loop_.message_loop_proxy())); store2->AddObserver(&observer_); ExpectError(store2.get(), CloudPolicyStore::STATUS_VALIDATION_ERROR); store2->Load(); @@ -358,8 +355,8 @@ TEST_F(UserCloudPolicyStoreTest, LoadValidationError) { // Sign out - we should be able to load the policy (don't check usernames // when signed out). SigninManagerFactory::GetForProfile(profile_.get())->SignOut(); - scoped_ptr<UserCloudPolicyStore> store3( - new UserCloudPolicyStore(profile_.get(), policy_file())); + scoped_ptr<UserCloudPolicyStore> store3(new UserCloudPolicyStore( + profile_.get(), policy_file(), loop_.message_loop_proxy())); store3->AddObserver(&observer_); EXPECT_CALL(observer_, OnStoreLoaded(store3.get())); store3->Load(); @@ -373,8 +370,8 @@ TEST_F(UserCloudPolicyStoreTest, LoadValidationError) { SigninManagerFactory::GetForProfile(profile_.get())); signin->set_auth_in_progress("foobar@foobar.com"); - scoped_ptr<UserCloudPolicyStore> store4( - new UserCloudPolicyStore(profile_.get(), policy_file())); + scoped_ptr<UserCloudPolicyStore> store4(new UserCloudPolicyStore( + profile_.get(), policy_file(), loop_.message_loop_proxy())); store4->AddObserver(&observer_); ExpectError(store4.get(), CloudPolicyStore::STATUS_VALIDATION_ERROR); store4->Load(); diff --git a/chrome/browser/policy/cloud/user_policy_signin_service.cc b/chrome/browser/policy/cloud/user_policy_signin_service.cc index 7075d1444a..db1349a20a 100644 --- a/chrome/browser/policy/cloud/user_policy_signin_service.cc +++ b/chrome/browser/policy/cloud/user_policy_signin_service.cc @@ -7,7 +7,6 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/callback.h" -#include "base/prefs/pref_service.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/policy/cloud/cloud_policy_client_registration_helper.h" @@ -17,7 +16,6 @@ #include "chrome/browser/signin/profile_oauth2_token_service.h" #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" #include "chrome/browser/signin/signin_manager.h" -#include "chrome/common/pref_names.h" #include "content/public/browser/notification_details.h" #include "content/public/browser/notification_source.h" #include "google_apis/gaia/gaia_constants.h" @@ -36,9 +34,6 @@ UserPolicySigninService::UserPolicySigninService( request_context, device_management_service), oauth2_token_service_(token_service) { - if (profile->GetPrefs()->GetBoolean(prefs::kDisableCloudPolicyOnSignin)) - return; - // ProfileOAuth2TokenService should not yet have loaded its tokens since this // happens in the background after PKS initialization - so this service // should always be created before the oauth token is available. diff --git a/chrome/browser/policy/cloud/user_policy_signin_service_android.cc b/chrome/browser/policy/cloud/user_policy_signin_service_android.cc index 8c42603ead..9035159eae 100644 --- a/chrome/browser/policy/cloud/user_policy_signin_service_android.cc +++ b/chrome/browser/policy/cloud/user_policy_signin_service_android.cc @@ -18,8 +18,8 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" #include "chrome/browser/signin/signin_manager.h" -#include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" +#include "components/policy/core/common/policy_switches.h" #include "net/base/network_change_notifier.h" #include "net/url_request/url_request_context_getter.h" diff --git a/chrome/browser/policy/cloud/user_policy_signin_service_base.cc b/chrome/browser/policy/cloud/user_policy_signin_service_base.cc index 324d5c1e8c..86127d67f3 100644 --- a/chrome/browser/policy/cloud/user_policy_signin_service_base.cc +++ b/chrome/browser/policy/cloud/user_policy_signin_service_base.cc @@ -7,17 +7,14 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/message_loop/message_loop.h" -#include "base/prefs/pref_service.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/policy/browser_policy_connector.h" #include "chrome/browser/policy/cloud/device_management_service.h" #include "chrome/browser/policy/cloud/user_cloud_policy_manager.h" #include "chrome/browser/policy/cloud/user_cloud_policy_manager_factory.h" -#include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/signin_manager.h" #include "chrome/browser/signin/signin_manager_factory.h" #include "chrome/common/chrome_switches.h" -#include "chrome/common/pref_names.h" #include "content/public/browser/notification_source.h" #include "net/url_request/url_request_context_getter.h" @@ -33,9 +30,6 @@ UserPolicySigninServiceBase::UserPolicySigninServiceBase( request_context_(request_context), device_management_service_(device_management_service), weak_factory_(this) { - if (profile_->GetPrefs()->GetBoolean(prefs::kDisableCloudPolicyOnSignin)) - return; - // Initialize/shutdown the UserCloudPolicyManager when the user signs out. registrar_.Add(this, chrome::NOTIFICATION_GOOGLE_SIGNED_OUT, @@ -175,9 +169,6 @@ scoped_ptr<CloudPolicyClient> UserPolicySigninServiceBase::PrepareToRegister( bool UserPolicySigninServiceBase::ShouldLoadPolicyForUser( const std::string& username) { - if (profile_->GetPrefs()->GetBoolean(prefs::kDisableCloudPolicyOnSignin)) - return false; // Cloud policy is disabled. - if (username.empty()) return false; // Not signed in. diff --git a/chrome/browser/policy/cloud/user_policy_signin_service_factory.cc b/chrome/browser/policy/cloud/user_policy_signin_service_factory.cc index 06b0150411..de102809f6 100644 --- a/chrome/browser/policy/cloud/user_policy_signin_service_factory.cc +++ b/chrome/browser/policy/cloud/user_policy_signin_service_factory.cc @@ -89,10 +89,6 @@ UserPolicySigninServiceFactory::ServiceIsCreatedWithBrowserContext() const { void UserPolicySigninServiceFactory::RegisterProfilePrefs( user_prefs::PrefRegistrySyncable* user_prefs) { - user_prefs->RegisterBooleanPref( - prefs::kDisableCloudPolicyOnSignin, - false, - user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); #if defined(OS_ANDROID) user_prefs->RegisterInt64Pref( prefs::kLastPolicyCheckTime, diff --git a/chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc b/chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc index 965635dbca..14de910609 100644 --- a/chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc +++ b/chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc @@ -58,12 +58,14 @@ namespace { const char kTestUser[] = "testuser@test.com"; +#if !defined(OS_ANDROID) const char kValidTokenResponse[] = "{" " \"access_token\": \"at1\"," " \"expires_in\": 3600," " \"token_type\": \"Bearer\"" "}"; +#endif const char kHostedDomainResponse[] = "{" diff --git a/chrome/browser/policy/configuration_policy_handler.cc b/chrome/browser/policy/configuration_policy_handler.cc index a708757e5d..b1565be098 100644 --- a/chrome/browser/policy/configuration_policy_handler.cc +++ b/chrome/browser/policy/configuration_policy_handler.cc @@ -11,24 +11,15 @@ #include "base/json/json_writer.h" #include "base/logging.h" #include "base/prefs/pref_value_map.h" -#include "base/stl_util.h" #include "base/strings/string16.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "chrome/browser/chrome_notification_types.h" -#include "chrome/browser/download/download_prefs.h" -#include "chrome/browser/extensions/external_policy_loader.h" #include "chrome/browser/policy/configuration_policy_pref_store.h" #include "chrome/browser/policy/external_data_fetcher.h" #include "chrome/browser/policy/policy_error_map.h" #include "chrome/browser/policy/policy_map.h" -#include "chrome/browser/prefs/proxy_config_dictionary.h" -#include "chrome/browser/prefs/proxy_prefs.h" #include "chrome/browser/prefs/session_startup_pref.h" -#include "chrome/browser/search_engines/search_terms_data.h" -#include "chrome/browser/search_engines/template_url.h" -#include "chrome/common/extensions/extension.h" -#include "content/public/browser/notification_service.h" #include "grit/generated_resources.h" #include "policy/policy_constants.h" #include "url/gurl.h" @@ -41,40 +32,6 @@ namespace policy { namespace { -// Helper classes -------------------------------------------------------------- - -// This is used to check whether for a given ProxyMode value, the ProxyPacUrl, -// the ProxyBypassList and the ProxyServer policies are allowed to be specified. -// |error_message_id| is the message id of the localized error message to show -// when the policies are not specified as allowed. Each value of ProxyMode -// has a ProxyModeValidationEntry in the |kProxyModeValidationMap| below. -struct ProxyModeValidationEntry { - const char* mode_value; - bool pac_url_allowed; - bool bypass_list_allowed; - bool server_allowed; - int error_message_id; -}; - - -// Static data ----------------------------------------------------------------- - -// List of entries determining which proxy policies can be specified, depending -// on the ProxyMode. -const ProxyModeValidationEntry kProxyModeValidationMap[] = { - { ProxyPrefs::kDirectProxyModeName, - false, false, false, IDS_POLICY_PROXY_MODE_DISABLED_ERROR }, - { ProxyPrefs::kAutoDetectProxyModeName, - false, false, false, IDS_POLICY_PROXY_MODE_AUTO_DETECT_ERROR }, - { ProxyPrefs::kPacScriptProxyModeName, - true, false, false, IDS_POLICY_PROXY_MODE_PAC_URL_ERROR }, - { ProxyPrefs::kFixedServersProxyModeName, - false, true, true, IDS_POLICY_PROXY_MODE_FIXED_SERVERS_ERROR }, - { ProxyPrefs::kSystemProxyModeName, - false, false, false, IDS_POLICY_PROXY_MODE_SYSTEM_ERROR }, -}; - - // Helper function ------------------------------------------------------------- // Utility function that returns a JSON representation of the given |dict| as @@ -367,239 +324,6 @@ void IntPercentageToDoublePolicyHandler::ApplyPolicySettings( } -// ExtensionListPolicyHandler implementation ----------------------------------- - -ExtensionListPolicyHandler::ExtensionListPolicyHandler(const char* policy_name, - const char* pref_path, - bool allow_wildcards) - : TypeCheckingPolicyHandler(policy_name, base::Value::TYPE_LIST), - pref_path_(pref_path), - allow_wildcards_(allow_wildcards) {} - -ExtensionListPolicyHandler::~ExtensionListPolicyHandler() {} - -bool ExtensionListPolicyHandler::CheckPolicySettings( - const PolicyMap& policies, - PolicyErrorMap* errors) { - return CheckAndGetList(policies, errors, NULL); -} - -void ExtensionListPolicyHandler::ApplyPolicySettings( - const PolicyMap& policies, - PrefValueMap* prefs) { - scoped_ptr<base::ListValue> list; - PolicyErrorMap errors; - if (CheckAndGetList(policies, &errors, &list) && list) - prefs->SetValue(pref_path(), list.release()); -} - -const char* ExtensionListPolicyHandler::pref_path() const { - return pref_path_; -} - -bool ExtensionListPolicyHandler::CheckAndGetList( - const PolicyMap& policies, - PolicyErrorMap* errors, - scoped_ptr<base::ListValue>* extension_ids) { - if (extension_ids) - extension_ids->reset(); - - const base::Value* value = NULL; - if (!CheckAndGetValue(policies, errors, &value)) - return false; - - if (!value) - return true; - - const base::ListValue* list_value = NULL; - if (!value->GetAsList(&list_value)) { - NOTREACHED(); - return false; - } - - // Filter the list, rejecting any invalid extension IDs. - scoped_ptr<base::ListValue> filtered_list(new base::ListValue()); - for (base::ListValue::const_iterator entry(list_value->begin()); - entry != list_value->end(); ++entry) { - std::string id; - if (!(*entry)->GetAsString(&id)) { - errors->AddError(policy_name(), - entry - list_value->begin(), - IDS_POLICY_TYPE_ERROR, - ValueTypeToString(base::Value::TYPE_STRING)); - continue; - } - if (!(allow_wildcards_ && id == "*") && - !extensions::Extension::IdIsValid(id)) { - errors->AddError(policy_name(), - entry - list_value->begin(), - IDS_POLICY_VALUE_FORMAT_ERROR); - continue; - } - filtered_list->Append(base::Value::CreateStringValue(id)); - } - - if (extension_ids) - *extension_ids = filtered_list.Pass(); - - return true; -} - - -// ExtensionInstallForcelistPolicyHandler implementation ----------------------- - -ExtensionInstallForcelistPolicyHandler::ExtensionInstallForcelistPolicyHandler( - const char* pref_name) - : TypeCheckingPolicyHandler(key::kExtensionInstallForcelist, - base::Value::TYPE_LIST), - pref_name_(pref_name) {} - -ExtensionInstallForcelistPolicyHandler:: - ~ExtensionInstallForcelistPolicyHandler() {} - -bool ExtensionInstallForcelistPolicyHandler::CheckPolicySettings( - const PolicyMap& policies, - PolicyErrorMap* errors) { - const base::Value* value; - return CheckAndGetValue(policies, errors, &value) && - ParseList(value, NULL, errors); -} - -void ExtensionInstallForcelistPolicyHandler::ApplyPolicySettings( - const PolicyMap& policies, - PrefValueMap* prefs) { - const base::Value* value = NULL; - scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); - if (CheckAndGetValue(policies, NULL, &value) && - value && - ParseList(value, dict.get(), NULL)) { - prefs->SetValue(pref_name_, dict.release()); - } -} - -bool ExtensionInstallForcelistPolicyHandler::ParseList( - const base::Value* policy_value, - base::DictionaryValue* extension_dict, - PolicyErrorMap* errors) { - if (!policy_value) - return true; - - const base::ListValue* policy_list_value = NULL; - if (!policy_value->GetAsList(&policy_list_value)) { - // This should have been caught in CheckPolicySettings. - NOTREACHED(); - return false; - } - - for (base::ListValue::const_iterator entry(policy_list_value->begin()); - entry != policy_list_value->end(); ++entry) { - std::string entry_string; - if (!(*entry)->GetAsString(&entry_string)) { - if (errors) { - errors->AddError(policy_name(), - entry - policy_list_value->begin(), - IDS_POLICY_TYPE_ERROR, - ValueTypeToString(base::Value::TYPE_STRING)); - } - continue; - } - - // Each string item of the list has the following form: - // <extension_id>;<update_url> - // Note: The update URL might also contain semicolons. - size_t pos = entry_string.find(';'); - if (pos == std::string::npos) { - if (errors) { - errors->AddError(policy_name(), - entry - policy_list_value->begin(), - IDS_POLICY_VALUE_FORMAT_ERROR); - } - continue; - } - - std::string extension_id = entry_string.substr(0, pos); - std::string update_url = entry_string.substr(pos+1); - if (!extensions::Extension::IdIsValid(extension_id) || - !GURL(update_url).is_valid()) { - if (errors) { - errors->AddError(policy_name(), - entry - policy_list_value->begin(), - IDS_POLICY_VALUE_FORMAT_ERROR); - } - continue; - } - - if (extension_dict) { - extensions::ExternalPolicyLoader::AddExtension( - extension_dict, extension_id, update_url); - } - } - - return true; -} - - -// ExtensionURLPatternListPolicyHandler implementation ------------------------- - -ExtensionURLPatternListPolicyHandler::ExtensionURLPatternListPolicyHandler( - const char* policy_name, - const char* pref_path) - : TypeCheckingPolicyHandler(policy_name, base::Value::TYPE_LIST), - pref_path_(pref_path) {} - -ExtensionURLPatternListPolicyHandler::~ExtensionURLPatternListPolicyHandler() {} - -bool ExtensionURLPatternListPolicyHandler::CheckPolicySettings( - const PolicyMap& policies, - PolicyErrorMap* errors) { - const base::Value* value = NULL; - if (!CheckAndGetValue(policies, errors, &value)) - return false; - - if (!value) - return true; - - const base::ListValue* list_value = NULL; - if (!value->GetAsList(&list_value)) { - NOTREACHED(); - return false; - } - - // Check that the list contains valid URLPattern strings only. - for (base::ListValue::const_iterator entry(list_value->begin()); - entry != list_value->end(); ++entry) { - std::string url_pattern_string; - if (!(*entry)->GetAsString(&url_pattern_string)) { - errors->AddError(policy_name(), - entry - list_value->begin(), - IDS_POLICY_TYPE_ERROR, - ValueTypeToString(base::Value::TYPE_STRING)); - return false; - } - - URLPattern pattern(URLPattern::SCHEME_ALL); - if (pattern.Parse(url_pattern_string) != URLPattern::PARSE_SUCCESS) { - errors->AddError(policy_name(), - entry - list_value->begin(), - IDS_POLICY_VALUE_FORMAT_ERROR); - return false; - } - } - - return true; -} - -void ExtensionURLPatternListPolicyHandler::ApplyPolicySettings( - const PolicyMap& policies, - PrefValueMap* prefs) { - if (!pref_path_) - return; - const Value* value = policies.GetValue(policy_name()); - if (value) - prefs->SetValue(pref_path_, value->DeepCopy()); -} - - // SimplePolicyHandler implementation ------------------------------------------ SimplePolicyHandler::SimplePolicyHandler( @@ -662,41 +386,6 @@ void AutofillPolicyHandler::ApplyPolicySettings(const PolicyMap& policies, // implementation. #if !defined(OS_ANDROID) - -// DownloadDirPolicyHandler implementation ------------------------------------- - -DownloadDirPolicyHandler::DownloadDirPolicyHandler( - const char* default_directory_pref_name, - const char* prompt_for_download_pref_name) - : TypeCheckingPolicyHandler(key::kDownloadDirectory, Value::TYPE_STRING), - default_directory_pref_name_(default_directory_pref_name), - prompt_for_download_pref_name_(prompt_for_download_pref_name) {} - -DownloadDirPolicyHandler::~DownloadDirPolicyHandler() { -} - -void DownloadDirPolicyHandler::ApplyPolicySettings(const PolicyMap& policies, - PrefValueMap* prefs) { - const Value* value = policies.GetValue(policy_name()); - base::FilePath::StringType string_value; - if (!value || !value->GetAsString(&string_value)) - return; - - base::FilePath::StringType expanded_value = - policy::path_parser::ExpandPathVariables(string_value); - // Make sure the path isn't empty, since that will point to an undefined - // location; the default location is used instead in that case. - // This is checked after path expansion because a non-empty policy value can - // lead to an empty path value after expansion (e.g. "\"\""). - if (expanded_value.empty()) - expanded_value = DownloadPrefs::GetDefaultDownloadDirectory().value(); - prefs->SetValue(default_directory_pref_name_, - Value::CreateStringValue(expanded_value)); - prefs->SetValue(prompt_for_download_pref_name_, - Value::CreateBooleanValue(false)); -} - - // DiskCacheDirPolicyHandler implementation ------------------------------------ DiskCacheDirPolicyHandler::DiskCacheDirPolicyHandler(const char* pref_name) @@ -823,546 +512,6 @@ void IncognitoModePolicyHandler::ApplyPolicySettings(const PolicyMap& policies, } -// DefaultSearchEncodingsPolicyHandler implementation -------------------------- - -DefaultSearchEncodingsPolicyHandler::DefaultSearchEncodingsPolicyHandler( - const char* pref_name) - : TypeCheckingPolicyHandler(key::kDefaultSearchProviderEncodings, - Value::TYPE_LIST), - pref_name_(pref_name) {} - -DefaultSearchEncodingsPolicyHandler::~DefaultSearchEncodingsPolicyHandler() { -} - -void DefaultSearchEncodingsPolicyHandler::ApplyPolicySettings( - const PolicyMap& policies, PrefValueMap* prefs) { - // The DefaultSearchProviderEncodings policy has type list, but the related - // preference has type string. Convert one into the other here, using - // ';' as a separator. - const Value* value = policies.GetValue(policy_name()); - const ListValue* list; - if (!value || !value->GetAsList(&list)) - return; - - ListValue::const_iterator iter(list->begin()); - ListValue::const_iterator end(list->end()); - std::vector<std::string> string_parts; - for (; iter != end; ++iter) { - std::string s; - if ((*iter)->GetAsString(&s)) { - string_parts.push_back(s); - } - } - std::string encodings = JoinString(string_parts, ';'); - prefs->SetValue(pref_name(), Value::CreateStringValue(encodings)); -} - - -// DefaultSearchPolicyHandler implementation ----------------------------------- - -DefaultSearchPolicyHandler::DefaultSearchPolicyHandler( - const char* id_pref_name, - const char* prepopulate_id_pref_name, - const PolicyToPreferenceMapEntry policy_to_pref_map[]) - : id_pref_name_(id_pref_name), - prepopulate_id_pref_name_(prepopulate_id_pref_name), - policy_to_pref_map_(policy_to_pref_map) { - for (size_t i = 0; i < DEFAULT_SEARCH_KEY_SIZE; ++i) { - const char* policy_name = policy_to_pref_map[i].policy_name; - if (policy_name == key::kDefaultSearchProviderEncodings) { - handlers_.push_back(new DefaultSearchEncodingsPolicyHandler( - policy_to_pref_map[i].preference_path)); - } else { - handlers_.push_back(new SimplePolicyHandler( - policy_name, - policy_to_pref_map[i].preference_path, - policy_to_pref_map[i].value_type)); - } - } -} - -DefaultSearchPolicyHandler::~DefaultSearchPolicyHandler() { - STLDeleteElements(&handlers_); -} - -bool DefaultSearchPolicyHandler::CheckPolicySettings(const PolicyMap& policies, - PolicyErrorMap* errors) { - if (!CheckIndividualPolicies(policies, errors)) - return false; - - if (DefaultSearchProviderIsDisabled(policies)) { - // Add an error for all specified default search policies except - // DefaultSearchProviderEnabled. - - for (std::vector<TypeCheckingPolicyHandler*>::const_iterator handler = - handlers_.begin(); - handler != handlers_.end(); ++handler) { - const char* policy_name = (*handler)->policy_name(); - if (policy_name != key::kDefaultSearchProviderEnabled && - HasDefaultSearchPolicy(policies, policy_name)) { - errors->AddError(policy_name, IDS_POLICY_DEFAULT_SEARCH_DISABLED); - } - } - return true; - } - - const Value* url; - std::string dummy; - if (DefaultSearchURLIsValid(policies, &url, &dummy) || - !AnyDefaultSearchPoliciesSpecified(policies)) - return true; - errors->AddError(key::kDefaultSearchProviderSearchURL, url ? - IDS_POLICY_INVALID_SEARCH_URL_ERROR : IDS_POLICY_NOT_SPECIFIED_ERROR); - return false; -} - -#define PREF_FOR(x) policy_to_pref_map_[x].preference_path -void DefaultSearchPolicyHandler::ApplyPolicySettings(const PolicyMap& policies, - PrefValueMap* prefs) { - if (DefaultSearchProviderIsDisabled(policies)) { - prefs->SetBoolean(PREF_FOR(DEFAULT_SEARCH_ENABLED), false); - - // If default search is disabled, the other fields are ignored. - prefs->SetString(PREF_FOR(DEFAULT_SEARCH_NAME), std::string()); - prefs->SetString(PREF_FOR(DEFAULT_SEARCH_SEARCH_URL), std::string()); - prefs->SetString(PREF_FOR(DEFAULT_SEARCH_SUGGEST_URL), std::string()); - prefs->SetString(PREF_FOR(DEFAULT_SEARCH_ICON_URL), std::string()); - prefs->SetString(PREF_FOR(DEFAULT_SEARCH_ENCODINGS), std::string()); - prefs->SetString(PREF_FOR(DEFAULT_SEARCH_KEYWORD), std::string()); - prefs->SetString(PREF_FOR(DEFAULT_SEARCH_INSTANT_URL), std::string()); - prefs->SetString(PREF_FOR(DEFAULT_SEARCH_NEW_TAB_URL), std::string()); - prefs->SetValue(PREF_FOR(DEFAULT_SEARCH_ALTERNATE_URLS), new ListValue()); - prefs->SetString( - PREF_FOR(DEFAULT_SEARCH_TERMS_REPLACEMENT_KEY), std::string()); - prefs->SetString(PREF_FOR(DEFAULT_SEARCH_IMAGE_URL), std::string()); - prefs->SetString( - PREF_FOR(DEFAULT_SEARCH_SEARCH_URL_POST_PARAMS), std::string()); - prefs->SetString( - PREF_FOR(DEFAULT_SEARCH_SUGGEST_URL_POST_PARAMS), std::string()); - prefs->SetString( - PREF_FOR(DEFAULT_SEARCH_INSTANT_URL_POST_PARAMS), std::string()); - prefs->SetString( - PREF_FOR(DEFAULT_SEARCH_IMAGE_URL_POST_PARAMS), std::string()); - } else { - // The search URL is required. The other entries are optional. Just make - // sure that they are all specified via policy, so that the regular prefs - // aren't used. - const Value* dummy; - std::string url; - if (DefaultSearchURLIsValid(policies, &dummy, &url)) { - - for (std::vector<TypeCheckingPolicyHandler*>::const_iterator handler = - handlers_.begin(); - handler != handlers_.end(); ++handler) { - (*handler)->ApplyPolicySettings(policies, prefs); - } - - EnsureStringPrefExists(prefs, PREF_FOR(DEFAULT_SEARCH_SUGGEST_URL)); - EnsureStringPrefExists(prefs, PREF_FOR(DEFAULT_SEARCH_ICON_URL)); - EnsureStringPrefExists(prefs, PREF_FOR(DEFAULT_SEARCH_ENCODINGS)); - EnsureStringPrefExists(prefs, PREF_FOR(DEFAULT_SEARCH_KEYWORD)); - EnsureStringPrefExists(prefs, PREF_FOR(DEFAULT_SEARCH_INSTANT_URL)); - EnsureStringPrefExists(prefs, PREF_FOR(DEFAULT_SEARCH_NEW_TAB_URL)); - EnsureListPrefExists(prefs, PREF_FOR(DEFAULT_SEARCH_ALTERNATE_URLS)); - EnsureStringPrefExists( - prefs, - PREF_FOR(DEFAULT_SEARCH_TERMS_REPLACEMENT_KEY)); - EnsureStringPrefExists(prefs, PREF_FOR(DEFAULT_SEARCH_IMAGE_URL)); - EnsureStringPrefExists( - prefs, - PREF_FOR(DEFAULT_SEARCH_SEARCH_URL_POST_PARAMS)); - EnsureStringPrefExists( - prefs, - PREF_FOR(DEFAULT_SEARCH_SUGGEST_URL_POST_PARAMS)); - EnsureStringPrefExists( - prefs, - PREF_FOR(DEFAULT_SEARCH_INSTANT_URL_POST_PARAMS)); - EnsureStringPrefExists( - prefs, - PREF_FOR(DEFAULT_SEARCH_IMAGE_URL_POST_PARAMS)); - - // For the name and keyword, default to the host if not specified. If - // there is no host (file: URLs? Not sure), use "_" to guarantee that the - // keyword is non-empty. - std::string name, keyword; - std::string host(GURL(url).host()); - if (host.empty()) - host = "_"; - if (!prefs->GetString(PREF_FOR(DEFAULT_SEARCH_NAME), &name) || - name.empty()) { - prefs->SetString(PREF_FOR(DEFAULT_SEARCH_NAME), host); - } - if (!prefs->GetString(PREF_FOR(DEFAULT_SEARCH_KEYWORD), &keyword) || - keyword.empty()) { - prefs->SetString(PREF_FOR(DEFAULT_SEARCH_KEYWORD), host); - } - - // And clear the IDs since these are not specified via policy. - prefs->SetString(id_pref_name_, std::string()); - prefs->SetString(prepopulate_id_pref_name_, std::string()); - } - } - content::NotificationService::current()->Notify( - chrome::NOTIFICATION_DEFAULT_SEARCH_POLICY_CHANGED, - content::NotificationService::AllSources(), - content::NotificationService::NoDetails()); -} -#undef PREF_FOR - -bool DefaultSearchPolicyHandler::CheckIndividualPolicies( - const PolicyMap& policies, - PolicyErrorMap* errors) { - for (std::vector<TypeCheckingPolicyHandler*>::const_iterator handler = - handlers_.begin(); - handler != handlers_.end(); ++handler) { - if (!(*handler)->CheckPolicySettings(policies, errors)) - return false; - } - return true; -} - -bool DefaultSearchPolicyHandler::HasDefaultSearchPolicy( - const PolicyMap& policies, - const char* policy_name) { - return policies.Get(policy_name) != NULL; -} - -bool DefaultSearchPolicyHandler::AnyDefaultSearchPoliciesSpecified( - const PolicyMap& policies) { - for (std::vector<TypeCheckingPolicyHandler*>::const_iterator handler = - handlers_.begin(); - handler != handlers_.end(); ++handler) { - if (policies.Get((*handler)->policy_name())) - return true; - } - return false; -} - -bool DefaultSearchPolicyHandler::DefaultSearchProviderIsDisabled( - const PolicyMap& policies) { - const Value* provider_enabled = - policies.GetValue(key::kDefaultSearchProviderEnabled); - bool enabled = true; - return provider_enabled && provider_enabled->GetAsBoolean(&enabled) && - !enabled; -} - -bool DefaultSearchPolicyHandler::DefaultSearchURLIsValid( - const PolicyMap& policies, - const Value** url_value, - std::string* url_string) { - *url_value = policies.GetValue(key::kDefaultSearchProviderSearchURL); - if (!*url_value || !(*url_value)->GetAsString(url_string) || - url_string->empty()) - return false; - TemplateURLData data; - data.SetURL(*url_string); - SearchTermsData search_terms_data; - return TemplateURL(NULL, data).SupportsReplacementUsingTermsData( - search_terms_data); -} - -void DefaultSearchPolicyHandler::EnsureStringPrefExists( - PrefValueMap* prefs, - const std::string& path) { - std::string value; - if (!prefs->GetString(path, &value)) - prefs->SetString(path, value); -} - -void DefaultSearchPolicyHandler::EnsureListPrefExists( - PrefValueMap* prefs, - const std::string& path) { - base::Value* value; - base::ListValue* list_value; - if (!prefs->GetValue(path, &value) || !value->GetAsList(&list_value)) - prefs->SetValue(path, new ListValue()); -} - - -// ProxyPolicyHandler implementation ------------------------------------------- - -// The proxy policies have the peculiarity that they are loaded from individual -// policies, but the providers then expose them through a unified -// DictionaryValue. Once Dictionary policies are fully supported, the individual -// proxy policies will be deprecated. http://crbug.com/108996 - -ProxyPolicyHandler::ProxyPolicyHandler(const char* pref_name) - : pref_name_(pref_name) {} - -ProxyPolicyHandler::~ProxyPolicyHandler() { -} - -bool ProxyPolicyHandler::CheckPolicySettings(const PolicyMap& policies, - PolicyErrorMap* errors) { - const Value* mode = GetProxyPolicyValue(policies, key::kProxyMode); - const Value* server = GetProxyPolicyValue(policies, key::kProxyServer); - const Value* server_mode = - GetProxyPolicyValue(policies, key::kProxyServerMode); - const Value* pac_url = GetProxyPolicyValue(policies, key::kProxyPacUrl); - const Value* bypass_list = - GetProxyPolicyValue(policies, key::kProxyBypassList); - - if ((server || pac_url || bypass_list) && !(mode || server_mode)) { - errors->AddError(key::kProxySettings, - key::kProxyMode, - IDS_POLICY_NOT_SPECIFIED_ERROR); - return false; - } - - std::string mode_value; - if (!CheckProxyModeAndServerMode(policies, errors, &mode_value)) - return false; - - // If neither ProxyMode nor ProxyServerMode are specified, mode_value will be - // empty and the proxy shouldn't be configured at all. - if (mode_value.empty()) - return true; - - bool is_valid_mode = false; - for (size_t i = 0; i != arraysize(kProxyModeValidationMap); ++i) { - const ProxyModeValidationEntry& entry = kProxyModeValidationMap[i]; - if (entry.mode_value != mode_value) - continue; - - is_valid_mode = true; - - if (!entry.pac_url_allowed && pac_url) { - errors->AddError(key::kProxySettings, - key::kProxyPacUrl, - entry.error_message_id); - } - if (!entry.bypass_list_allowed && bypass_list) { - errors->AddError(key::kProxySettings, - key::kProxyBypassList, - entry.error_message_id); - } - if (!entry.server_allowed && server) { - errors->AddError(key::kProxySettings, - key::kProxyServer, - entry.error_message_id); - } - - if ((!entry.pac_url_allowed && pac_url) || - (!entry.bypass_list_allowed && bypass_list) || - (!entry.server_allowed && server)) { - return false; - } - } - - if (!is_valid_mode) { - errors->AddError(key::kProxySettings, - mode ? key::kProxyMode : key::kProxyServerMode, - IDS_POLICY_OUT_OF_RANGE_ERROR, - mode_value); - return false; - } - return true; -} - -void ProxyPolicyHandler::ApplyPolicySettings(const PolicyMap& policies, - PrefValueMap* prefs) { - const Value* mode = GetProxyPolicyValue(policies, key::kProxyMode); - const Value* server = GetProxyPolicyValue(policies, key::kProxyServer); - const Value* server_mode = - GetProxyPolicyValue(policies, key::kProxyServerMode); - const Value* pac_url = GetProxyPolicyValue(policies, key::kProxyPacUrl); - const Value* bypass_list = - GetProxyPolicyValue(policies, key::kProxyBypassList); - - ProxyPrefs::ProxyMode proxy_mode; - if (mode) { - std::string string_mode; - CHECK(mode->GetAsString(&string_mode)); - CHECK(ProxyPrefs::StringToProxyMode(string_mode, &proxy_mode)); - } else if (server_mode) { - int int_mode = 0; - CHECK(server_mode->GetAsInteger(&int_mode)); - - switch (int_mode) { - case PROXY_SERVER_MODE: - proxy_mode = ProxyPrefs::MODE_DIRECT; - break; - case PROXY_AUTO_DETECT_PROXY_SERVER_MODE: - proxy_mode = ProxyPrefs::MODE_AUTO_DETECT; - break; - case PROXY_MANUALLY_CONFIGURED_PROXY_SERVER_MODE: - proxy_mode = ProxyPrefs::MODE_FIXED_SERVERS; - if (pac_url) - proxy_mode = ProxyPrefs::MODE_PAC_SCRIPT; - break; - case PROXY_USE_SYSTEM_PROXY_SERVER_MODE: - proxy_mode = ProxyPrefs::MODE_SYSTEM; - break; - default: - proxy_mode = ProxyPrefs::MODE_DIRECT; - NOTREACHED(); - } - } else { - return; - } - - switch (proxy_mode) { - case ProxyPrefs::MODE_DIRECT: - prefs->SetValue(pref_name_, ProxyConfigDictionary::CreateDirect()); - break; - case ProxyPrefs::MODE_AUTO_DETECT: - prefs->SetValue(pref_name_, ProxyConfigDictionary::CreateAutoDetect()); - break; - case ProxyPrefs::MODE_PAC_SCRIPT: { - std::string pac_url_string; - if (pac_url && pac_url->GetAsString(&pac_url_string)) { - prefs->SetValue( - pref_name_, - ProxyConfigDictionary::CreatePacScript(pac_url_string, false)); - } else { - NOTREACHED(); - } - break; - } - case ProxyPrefs::MODE_FIXED_SERVERS: { - std::string proxy_server; - std::string bypass_list_string; - if (server->GetAsString(&proxy_server)) { - if (bypass_list) - bypass_list->GetAsString(&bypass_list_string); - prefs->SetValue(pref_name_, - ProxyConfigDictionary::CreateFixedServers( - proxy_server, bypass_list_string)); - } - break; - } - case ProxyPrefs::MODE_SYSTEM: - prefs->SetValue(pref_name_, ProxyConfigDictionary::CreateSystem()); - break; - case ProxyPrefs::kModeCount: - NOTREACHED(); - } -} - -const Value* ProxyPolicyHandler::GetProxyPolicyValue( - const PolicyMap& policies, const char* policy_name) { - // See note on the ProxyPolicyHandler implementation above. - const Value* value = policies.GetValue(key::kProxySettings); - const DictionaryValue* settings; - if (!value || !value->GetAsDictionary(&settings)) - return NULL; - - const Value* policy_value = NULL; - std::string tmp; - if (!settings->Get(policy_name, &policy_value) || - policy_value->IsType(Value::TYPE_NULL) || - (policy_value->IsType(Value::TYPE_STRING) && - policy_value->GetAsString(&tmp) && - tmp.empty())) { - return NULL; - } - return policy_value; -} - -bool ProxyPolicyHandler::CheckProxyModeAndServerMode(const PolicyMap& policies, - PolicyErrorMap* errors, - std::string* mode_value) { - const Value* mode = GetProxyPolicyValue(policies, key::kProxyMode); - const Value* server = GetProxyPolicyValue(policies, key::kProxyServer); - const Value* server_mode = - GetProxyPolicyValue(policies, key::kProxyServerMode); - const Value* pac_url = GetProxyPolicyValue(policies, key::kProxyPacUrl); - - // If there's a server mode, convert it into a mode. - // When both are specified, the mode takes precedence. - if (mode) { - if (server_mode) { - errors->AddError(key::kProxySettings, - key::kProxyServerMode, - IDS_POLICY_OVERRIDDEN, - key::kProxyMode); - } - if (!mode->GetAsString(mode_value)) { - errors->AddError(key::kProxySettings, - key::kProxyMode, - IDS_POLICY_TYPE_ERROR, - ValueTypeToString(Value::TYPE_BOOLEAN)); - return false; - } - - ProxyPrefs::ProxyMode mode; - if (!ProxyPrefs::StringToProxyMode(*mode_value, &mode)) { - errors->AddError(key::kProxySettings, - key::kProxyMode, - IDS_POLICY_INVALID_PROXY_MODE_ERROR); - return false; - } - - if (mode == ProxyPrefs::MODE_PAC_SCRIPT && !pac_url) { - errors->AddError(key::kProxySettings, - key::kProxyPacUrl, - IDS_POLICY_NOT_SPECIFIED_ERROR); - return false; - } else if (mode == ProxyPrefs::MODE_FIXED_SERVERS && !server) { - errors->AddError(key::kProxySettings, - key::kProxyServer, - IDS_POLICY_NOT_SPECIFIED_ERROR); - return false; - } - } else if (server_mode) { - int server_mode_value; - if (!server_mode->GetAsInteger(&server_mode_value)) { - errors->AddError(key::kProxySettings, - key::kProxyServerMode, - IDS_POLICY_TYPE_ERROR, - ValueTypeToString(Value::TYPE_INTEGER)); - return false; - } - - switch (server_mode_value) { - case PROXY_SERVER_MODE: - *mode_value = ProxyPrefs::kDirectProxyModeName; - break; - case PROXY_AUTO_DETECT_PROXY_SERVER_MODE: - *mode_value = ProxyPrefs::kAutoDetectProxyModeName; - break; - case PROXY_MANUALLY_CONFIGURED_PROXY_SERVER_MODE: - if (server && pac_url) { - int message_id = IDS_POLICY_PROXY_BOTH_SPECIFIED_ERROR; - errors->AddError(key::kProxySettings, - key::kProxyServer, - message_id); - errors->AddError(key::kProxySettings, - key::kProxyPacUrl, - message_id); - return false; - } - if (!server && !pac_url) { - int message_id = IDS_POLICY_PROXY_NEITHER_SPECIFIED_ERROR; - errors->AddError(key::kProxySettings, - key::kProxyServer, - message_id); - errors->AddError(key::kProxySettings, - key::kProxyPacUrl, - message_id); - return false; - } - if (pac_url) - *mode_value = ProxyPrefs::kPacScriptProxyModeName; - else - *mode_value = ProxyPrefs::kFixedServersProxyModeName; - break; - case PROXY_USE_SYSTEM_PROXY_SERVER_MODE: - *mode_value = ProxyPrefs::kSystemProxyModeName; - break; - default: - errors->AddError(key::kProxySettings, - key::kProxyServerMode, - IDS_POLICY_OUT_OF_RANGE_ERROR, - base::IntToString(server_mode_value)); - return false; - } - } - return true; -} - - // JavascriptPolicyHandler implementation -------------------------------------- JavascriptPolicyHandler::JavascriptPolicyHandler(const char* pref_name) diff --git a/chrome/browser/policy/configuration_policy_handler.h b/chrome/browser/policy/configuration_policy_handler.h index 0fc8755183..668be54701 100644 --- a/chrome/browser/policy/configuration_policy_handler.h +++ b/chrome/browser/policy/configuration_policy_handler.h @@ -228,77 +228,6 @@ class IntPercentageToDoublePolicyHandler : public IntRangePolicyHandlerBase { DISALLOW_COPY_AND_ASSIGN(IntPercentageToDoublePolicyHandler); }; -// Implements additional checks for policies that are lists of extension IDs. -class ExtensionListPolicyHandler : public TypeCheckingPolicyHandler { - public: - ExtensionListPolicyHandler(const char* policy_name, - const char* pref_path, - bool allow_wildcards); - virtual ~ExtensionListPolicyHandler(); - - // ConfigurationPolicyHandler methods: - virtual bool CheckPolicySettings(const PolicyMap& policies, - PolicyErrorMap* errors) OVERRIDE; - virtual void ApplyPolicySettings(const PolicyMap& policies, - PrefValueMap* prefs) OVERRIDE; - - protected: - const char* pref_path() const; - - // Runs sanity checks on the policy value and returns it in |extension_ids|. - bool CheckAndGetList(const PolicyMap& policies, - PolicyErrorMap* errors, - scoped_ptr<base::ListValue>* extension_ids); - - private: - const char* pref_path_; - bool allow_wildcards_; - - DISALLOW_COPY_AND_ASSIGN(ExtensionListPolicyHandler); -}; - -class ExtensionInstallForcelistPolicyHandler - : public TypeCheckingPolicyHandler { - public: - explicit ExtensionInstallForcelistPolicyHandler(const char* pref_name); - virtual ~ExtensionInstallForcelistPolicyHandler(); - - // ConfigurationPolicyHandler methods: - virtual bool CheckPolicySettings(const PolicyMap& policies, - PolicyErrorMap* errors) OVERRIDE; - virtual void ApplyPolicySettings(const PolicyMap& policies, - PrefValueMap* prefs) OVERRIDE; - - private: - // Parses the data in |policy_value| and writes them to |extension_dict|. - bool ParseList(const base::Value* policy_value, - base::DictionaryValue* extension_dict, - PolicyErrorMap* errors); - - const char* pref_name_; - DISALLOW_COPY_AND_ASSIGN(ExtensionInstallForcelistPolicyHandler); -}; - -// Implements additional checks for policies that are lists of extension -// URLPatterns. -class ExtensionURLPatternListPolicyHandler : public TypeCheckingPolicyHandler { - public: - ExtensionURLPatternListPolicyHandler(const char* policy_name, - const char* pref_path); - virtual ~ExtensionURLPatternListPolicyHandler(); - - // ConfigurationPolicyHandler methods: - virtual bool CheckPolicySettings(const PolicyMap& policies, - PolicyErrorMap* errors) OVERRIDE; - virtual void ApplyPolicySettings(const PolicyMap& policies, - PrefValueMap* prefs) OVERRIDE; - - private: - const char* pref_path_; - - DISALLOW_COPY_AND_ASSIGN(ExtensionURLPatternListPolicyHandler); -}; - // ConfigurationPolicyHandler for the SyncDisabled policy. class SyncPolicyHandler : public TypeCheckingPolicyHandler { public: @@ -331,23 +260,6 @@ class AutofillPolicyHandler : public TypeCheckingPolicyHandler { #if !defined(OS_ANDROID) -// ConfigurationPolicyHandler for the DownloadDirectory policy. -class DownloadDirPolicyHandler : public TypeCheckingPolicyHandler { - public: - DownloadDirPolicyHandler(const char* default_directory_pref_name, - const char* prompt_for_download_pref_name); - virtual ~DownloadDirPolicyHandler(); - - // ConfigurationPolicyHandler methods: - virtual void ApplyPolicySettings(const PolicyMap& policies, - PrefValueMap* prefs) OVERRIDE; - - private: - const char* default_directory_pref_name_; - const char* prompt_for_download_pref_name_; - DISALLOW_COPY_AND_ASSIGN(DownloadDirPolicyHandler); -}; - // ConfigurationPolicyHandler for the DiskCacheDir policy. class DiskCacheDirPolicyHandler : public TypeCheckingPolicyHandler { public: @@ -402,151 +314,6 @@ class IncognitoModePolicyHandler : public ConfigurationPolicyHandler { DISALLOW_COPY_AND_ASSIGN(IncognitoModePolicyHandler); }; -// ConfigurationPolicyHandler for the DefaultSearchEncodings policy. -class DefaultSearchEncodingsPolicyHandler : public TypeCheckingPolicyHandler { - public: - explicit DefaultSearchEncodingsPolicyHandler(const char* pref_name); - virtual ~DefaultSearchEncodingsPolicyHandler(); - - // ConfigurationPolicyHandler methods: - virtual void ApplyPolicySettings(const PolicyMap& policies, - PrefValueMap* prefs) OVERRIDE; - - const char* pref_name() const { return pref_name_; } - - private: - const char* pref_name_; - DISALLOW_COPY_AND_ASSIGN(DefaultSearchEncodingsPolicyHandler); -}; - -enum DefaultSearchKey { - DEFAULT_SEARCH_ENABLED, - DEFAULT_SEARCH_NAME, - DEFAULT_SEARCH_KEYWORD, - DEFAULT_SEARCH_SEARCH_URL, - DEFAULT_SEARCH_SUGGEST_URL, - DEFAULT_SEARCH_INSTANT_URL, - DEFAULT_SEARCH_ICON_URL, - DEFAULT_SEARCH_ENCODINGS, - DEFAULT_SEARCH_ALTERNATE_URLS, - DEFAULT_SEARCH_TERMS_REPLACEMENT_KEY, - DEFAULT_SEARCH_IMAGE_URL, - DEFAULT_SEARCH_NEW_TAB_URL, - DEFAULT_SEARCH_SEARCH_URL_POST_PARAMS, - DEFAULT_SEARCH_SUGGEST_URL_POST_PARAMS, - DEFAULT_SEARCH_INSTANT_URL_POST_PARAMS, - DEFAULT_SEARCH_IMAGE_URL_POST_PARAMS, - // Must be last. - DEFAULT_SEARCH_KEY_SIZE, -}; - -// ConfigurationPolicyHandler for the default search policies. -class DefaultSearchPolicyHandler : public ConfigurationPolicyHandler { - public: - // Constructs a new handler for the DefaultSearch policy with the specified - // preference names. - // |id_pref_name|: Pref name for the search provider ID. - // |prepopulate_id_pref_name|: Pref name for the prepopulated provider ID. - // |policy_to_pref_map|: Defines the pref names for respective policy keys. - // Must contain exactly DEFAULT_SEARCH_KEY_SIZE entries and be ordered - // according to the DefaultSearchKey enum. - DefaultSearchPolicyHandler( - const char* id_pref_name, - const char* prepopulate_id_pref_name, - const PolicyToPreferenceMapEntry policy_to_pref_map[]); - virtual ~DefaultSearchPolicyHandler(); - - // ConfigurationPolicyHandler methods: - virtual bool CheckPolicySettings(const PolicyMap& policies, - PolicyErrorMap* errors) OVERRIDE; - virtual void ApplyPolicySettings(const PolicyMap& policies, - PrefValueMap* prefs) OVERRIDE; - - private: - // Calls |CheckPolicySettings()| on each of the handlers in |handlers_| - // and returns whether all of the calls succeeded. - bool CheckIndividualPolicies(const PolicyMap& policies, - PolicyErrorMap* errors); - - // Returns whether there is a value for |policy_name| in |policies|. - bool HasDefaultSearchPolicy(const PolicyMap& policies, - const char* policy_name); - - // Returns whether any default search policies are specified in |policies|. - bool AnyDefaultSearchPoliciesSpecified(const PolicyMap& policies); - - // Returns whether the default search provider is disabled. - bool DefaultSearchProviderIsDisabled(const PolicyMap& policies); - - // Returns whether the default search URL is set and valid. On success, both - // outparams (which must be non-NULL) are filled with the search URL. - bool DefaultSearchURLIsValid(const PolicyMap& policies, - const Value** url_value, - std::string* url_string); - - // Make sure that the |path| is present in |prefs_|. If not, set it to - // a blank string. - void EnsureStringPrefExists(PrefValueMap* prefs, const std::string& path); - - // Make sure that the |path| is present in |prefs_| and is a ListValue. If - // not, set it to an empty list. - void EnsureListPrefExists(PrefValueMap* prefs, const std::string& path); - - // The ConfigurationPolicyHandler handlers for each default search policy. - std::vector<TypeCheckingPolicyHandler*> handlers_; - - const char* id_pref_name_; - const char* prepopulate_id_pref_name_; - const PolicyToPreferenceMapEntry* policy_to_pref_map_; // weak - - DISALLOW_COPY_AND_ASSIGN(DefaultSearchPolicyHandler); -}; - -// ConfigurationPolicyHandler for the proxy policies. -class ProxyPolicyHandler : public ConfigurationPolicyHandler { - public: - // Constants for the "Proxy Server Mode" defined in the policies. - // Note that these diverge from internal presentation defined in - // ProxyPrefs::ProxyMode for legacy reasons. The following four - // PolicyProxyModeType types were not very precise and had overlapping use - // cases. - enum ProxyModeType { - // Disable Proxy, connect directly. - PROXY_SERVER_MODE = 0, - // Auto detect proxy or use specific PAC script if given. - PROXY_AUTO_DETECT_PROXY_SERVER_MODE = 1, - // Use manually configured proxy servers (fixed servers). - PROXY_MANUALLY_CONFIGURED_PROXY_SERVER_MODE = 2, - // Use system proxy server. - PROXY_USE_SYSTEM_PROXY_SERVER_MODE = 3, - - MODE_COUNT - }; - - explicit ProxyPolicyHandler(const char* pref_name); - virtual ~ProxyPolicyHandler(); - - // ConfigurationPolicyHandler methods: - virtual bool CheckPolicySettings(const PolicyMap& policies, - PolicyErrorMap* errors) OVERRIDE; - virtual void ApplyPolicySettings(const PolicyMap& policies, - PrefValueMap* prefs) OVERRIDE; - - private: - const Value* GetProxyPolicyValue(const PolicyMap& policies, - const char* policy_name); - - // Converts the deprecated ProxyServerMode policy value to a ProxyMode value - // and places the result in |mode_value|. Returns whether the conversion - // succeeded. - bool CheckProxyModeAndServerMode(const PolicyMap& policies, - PolicyErrorMap* errors, - std::string* mode_value); - - const char* pref_name_; - DISALLOW_COPY_AND_ASSIGN(ProxyPolicyHandler); -}; - // Handles JavaScript policies. class JavascriptPolicyHandler : public ConfigurationPolicyHandler { public: diff --git a/chrome/browser/policy/configuration_policy_handler_list.cc b/chrome/browser/policy/configuration_policy_handler_list.cc index e2a57142be..47d7a15425 100644 --- a/chrome/browser/policy/configuration_policy_handler_list.cc +++ b/chrome/browser/policy/configuration_policy_handler_list.cc @@ -10,10 +10,14 @@ #include "base/prefs/pref_value_map.h" #include "base/stl_util.h" #include "base/values.h" +#include "chrome/browser/extensions/policy_handlers.h" +#include "chrome/browser/net/proxy_policy_handler.h" #include "chrome/browser/policy/configuration_policy_handler.h" #include "chrome/browser/policy/policy_error_map.h" #include "chrome/browser/policy/policy_map.h" +#include "chrome/browser/search_engines/default_search_policy_handler.h" #include "chrome/common/pref_names.h" +#include "components/policy/core/common/policy_pref_names.h" #include "extensions/common/manifest.h" #include "grit/generated_resources.h" #include "policy/policy_constants.h" @@ -28,68 +32,16 @@ #include "chrome/browser/policy/configuration_policy_handler_android.h" #endif // defined(OS_ANDROID) +#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID) && !defined(OS_IOS) +#include "chrome/browser/download/download_dir_policy_handler.h" +#endif + #if !defined(OS_MACOSX) #include "apps/pref_names.h" #endif namespace policy { -// List of policy types to preference names, for policies affecting the default -// search provider. -const PolicyToPreferenceMapEntry kDefaultSearchPolicyMap[] = { - { key::kDefaultSearchProviderEnabled, - prefs::kDefaultSearchProviderEnabled, - Value::TYPE_BOOLEAN }, - { key::kDefaultSearchProviderName, - prefs::kDefaultSearchProviderName, - Value::TYPE_STRING }, - { key::kDefaultSearchProviderKeyword, - prefs::kDefaultSearchProviderKeyword, - Value::TYPE_STRING }, - { key::kDefaultSearchProviderSearchURL, - prefs::kDefaultSearchProviderSearchURL, - Value::TYPE_STRING }, - { key::kDefaultSearchProviderSuggestURL, - prefs::kDefaultSearchProviderSuggestURL, - Value::TYPE_STRING }, - { key::kDefaultSearchProviderInstantURL, - prefs::kDefaultSearchProviderInstantURL, - Value::TYPE_STRING }, - { key::kDefaultSearchProviderIconURL, - prefs::kDefaultSearchProviderIconURL, - Value::TYPE_STRING }, - { key::kDefaultSearchProviderEncodings, - prefs::kDefaultSearchProviderEncodings, - Value::TYPE_LIST }, - { key::kDefaultSearchProviderAlternateURLs, - prefs::kDefaultSearchProviderAlternateURLs, - Value::TYPE_LIST }, - { key::kDefaultSearchProviderSearchTermsReplacementKey, - prefs::kDefaultSearchProviderSearchTermsReplacementKey, - Value::TYPE_STRING }, - { key::kDefaultSearchProviderImageURL, - prefs::kDefaultSearchProviderImageURL, - Value::TYPE_STRING }, - { key::kDefaultSearchProviderNewTabURL, - prefs::kDefaultSearchProviderNewTabURL, - Value::TYPE_STRING }, - { key::kDefaultSearchProviderSearchURLPostParams, - prefs::kDefaultSearchProviderSearchURLPostParams, - Value::TYPE_STRING }, - { key::kDefaultSearchProviderSuggestURLPostParams, - prefs::kDefaultSearchProviderSuggestURLPostParams, - Value::TYPE_STRING }, - { key::kDefaultSearchProviderInstantURLPostParams, - prefs::kDefaultSearchProviderInstantURLPostParams, - Value::TYPE_STRING }, - { key::kDefaultSearchProviderImageURLPostParams, - prefs::kDefaultSearchProviderImageURLPostParams, - Value::TYPE_STRING }, -}; - -COMPILE_ASSERT(DEFAULT_SEARCH_KEY_SIZE == arraysize(kDefaultSearchPolicyMap), - wrong_policy_map_size); - namespace { // List of policy types to preference names. This is used for simple policies @@ -143,9 +95,6 @@ const PolicyToPreferenceMapEntry kSimplePolicyMap[] = { { key::kApplicationLocaleValue, prefs::kApplicationLocale, Value::TYPE_STRING }, - { key::kExtensionInstallForcelist, - prefs::kExtensionInstallForceList, - Value::TYPE_LIST }, { key::kDisabledPlugins, prefs::kPluginsDisabledPlugins, Value::TYPE_LIST }, @@ -279,7 +228,7 @@ const PolicyToPreferenceMapEntry kSimplePolicyMap[] = { prefs::kMediaCacheSize, Value::TYPE_INTEGER }, { key::kPolicyRefreshRate, - prefs::kUserPolicyRefreshRate, + policy_prefs::kUserPolicyRefreshRate, Value::TYPE_INTEGER }, { key::kDevicePolicyRefreshRate, prefs::kDevicePolicyRefreshRate, @@ -509,33 +458,27 @@ ConfigurationPolicyHandlerList::ConfigurationPolicyHandlerList() { handlers_.push_back( new AutofillPolicyHandler(autofill::prefs::kAutofillEnabled)); - handlers_.push_back( - new DefaultSearchPolicyHandler(prefs::kDefaultSearchProviderID, - prefs::kDefaultSearchProviderPrepopulateID, - kDefaultSearchPolicyMap)); + handlers_.push_back(new DefaultSearchPolicyHandler()); handlers_.push_back(new FileSelectionDialogsHandler( prefs::kAllowFileSelectionDialogs, prefs::kPromptForDownload)); handlers_.push_back( new IncognitoModePolicyHandler(prefs::kIncognitoModeAvailability)); handlers_.push_back( new JavascriptPolicyHandler(prefs::kManagedDefaultJavaScriptSetting)); - handlers_.push_back(new ProxyPolicyHandler(prefs::kProxy)); + handlers_.push_back(new ProxyPolicyHandler()); handlers_.push_back(new RestoreOnStartupPolicyHandler( prefs::kRestoreOnStartup, prefs::kURLsToRestoreOnStartup)); handlers_.push_back(new SyncPolicyHandler(prefs::kSyncManaged)); handlers_.push_back(new URLBlacklistPolicyHandler(prefs::kUrlBlacklist)); - handlers_.push_back( - new ExtensionListPolicyHandler(key::kExtensionInstallWhitelist, - prefs::kExtensionInstallAllowList, - false)); - handlers_.push_back( - new ExtensionListPolicyHandler(key::kExtensionInstallBlacklist, - prefs::kExtensionInstallDenyList, - true)); - handlers_.push_back(new ExtensionInstallForcelistPolicyHandler( - prefs::kExtensionInstallForceList)); - handlers_.push_back(new ExtensionURLPatternListPolicyHandler( + handlers_.push_back(new extensions::ExtensionListPolicyHandler( + key::kExtensionInstallWhitelist, + prefs::kExtensionInstallAllowList, + false)); + handlers_.push_back(new extensions::ExtensionListPolicyHandler( + key::kExtensionInstallBlacklist, prefs::kExtensionInstallDenyList, true)); + handlers_.push_back(new extensions::ExtensionInstallForcelistPolicyHandler()); + handlers_.push_back(new extensions::ExtensionURLPatternListPolicyHandler( key::kExtensionInstallSources, prefs::kExtensionAllowedInstallSites)); handlers_.push_back(new StringToIntEnumListPolicyHandler( key::kExtensionAllowedTypes, @@ -543,16 +486,15 @@ ConfigurationPolicyHandlerList::ConfigurationPolicyHandlerList() { kExtensionAllowedTypesMap, kExtensionAllowedTypesMap + arraysize(kExtensionAllowedTypesMap))); #if defined(OS_CHROMEOS) - handlers_.push_back( - new ExtensionListPolicyHandler(key::kAttestationExtensionWhitelist, - prefs::kAttestationExtensionWhitelist, - false)); + handlers_.push_back(new extensions::ExtensionListPolicyHandler( + key::kAttestationExtensionWhitelist, + prefs::kAttestationExtensionWhitelist, + false)); #endif // defined(OS_CHROMEOS) -#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID) +#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID) && !defined(OS_IOS) handlers_.push_back(new DiskCacheDirPolicyHandler(prefs::kDiskCacheDir)); - handlers_.push_back(new DownloadDirPolicyHandler( - prefs::kDownloadDefaultDirectory, prefs::kPromptForDownload)); + handlers_.push_back(new DownloadDirPolicyHandler); #endif // !defined(OS_CHROMEOS) && !defined(OS_ANDROID) #if defined(OS_CHROMEOS) diff --git a/chrome/browser/policy/configuration_policy_handler_list.h b/chrome/browser/policy/configuration_policy_handler_list.h index 599fd9a5e6..8ecde65951 100644 --- a/chrome/browser/policy/configuration_policy_handler_list.h +++ b/chrome/browser/policy/configuration_policy_handler_list.h @@ -18,11 +18,6 @@ class PolicyErrorMap; class PolicyMap; struct PolicyToPreferenceMapEntry; -// Declares the array of policy key to preference name mappings. Contains -// exactly DEFAULT_SEARCH_KEY entries, which are sorted according to the order -// of entries in the DefaultSearchKey enum. -extern const PolicyToPreferenceMapEntry kDefaultSearchPolicyMap[]; - // Converts policies to their corresponding preferences. Also does error // checking and cleans up policy values for displaying. class ConfigurationPolicyHandlerList { diff --git a/chrome/browser/policy/configuration_policy_handler_list_unittest.cc b/chrome/browser/policy/configuration_policy_handler_list_unittest.cc deleted file mode 100644 index c37e94f214..0000000000 --- a/chrome/browser/policy/configuration_policy_handler_list_unittest.cc +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2013 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/policy/configuration_policy_handler.h" -#include "chrome/browser/policy/configuration_policy_handler_list.h" -#include "policy/policy_constants.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace policy { - -TEST(DefaultSearchPolicyHandlerTest, PolicyToPreferenceMapInitialization) { - EXPECT_EQ(kDefaultSearchPolicyMap[DEFAULT_SEARCH_ENABLED].policy_name, - (const char * const)key::kDefaultSearchProviderEnabled); - EXPECT_EQ(kDefaultSearchPolicyMap[DEFAULT_SEARCH_NAME].policy_name, - (const char * const)key::kDefaultSearchProviderName); - EXPECT_EQ(kDefaultSearchPolicyMap[DEFAULT_SEARCH_KEYWORD].policy_name, - (const char * const)key::kDefaultSearchProviderKeyword); - EXPECT_EQ(kDefaultSearchPolicyMap[DEFAULT_SEARCH_SEARCH_URL].policy_name, - (const char * const)key::kDefaultSearchProviderSearchURL); - EXPECT_EQ(kDefaultSearchPolicyMap[DEFAULT_SEARCH_SUGGEST_URL].policy_name, - (const char * const)key::kDefaultSearchProviderSuggestURL); - EXPECT_EQ(kDefaultSearchPolicyMap[DEFAULT_SEARCH_INSTANT_URL].policy_name, - (const char * const)key::kDefaultSearchProviderInstantURL); - EXPECT_EQ(kDefaultSearchPolicyMap[DEFAULT_SEARCH_ICON_URL].policy_name, - (const char * const)key::kDefaultSearchProviderIconURL); - EXPECT_EQ(kDefaultSearchPolicyMap[DEFAULT_SEARCH_ENCODINGS].policy_name, - (const char * const)key::kDefaultSearchProviderEncodings); - EXPECT_EQ(kDefaultSearchPolicyMap[DEFAULT_SEARCH_ALTERNATE_URLS].policy_name, - (const char * const)key::kDefaultSearchProviderAlternateURLs); - EXPECT_EQ( - kDefaultSearchPolicyMap[DEFAULT_SEARCH_TERMS_REPLACEMENT_KEY].policy_name, - (const char * const)key::kDefaultSearchProviderSearchTermsReplacementKey); - EXPECT_EQ(kDefaultSearchPolicyMap[DEFAULT_SEARCH_IMAGE_URL].policy_name, - (const char * const)key::kDefaultSearchProviderImageURL); - EXPECT_EQ(kDefaultSearchPolicyMap[DEFAULT_SEARCH_NEW_TAB_URL].policy_name, - (const char * const)key::kDefaultSearchProviderNewTabURL); - EXPECT_EQ(kDefaultSearchPolicyMap[DEFAULT_SEARCH_SEARCH_URL_POST_PARAMS] - .policy_name, - (const char * const)key::kDefaultSearchProviderSearchURLPostParams); - EXPECT_EQ( - kDefaultSearchPolicyMap[DEFAULT_SEARCH_SUGGEST_URL_POST_PARAMS] - .policy_name, - (const char * const)key::kDefaultSearchProviderSuggestURLPostParams); - EXPECT_EQ( - kDefaultSearchPolicyMap[DEFAULT_SEARCH_INSTANT_URL_POST_PARAMS] - .policy_name, - (const char * const)key::kDefaultSearchProviderInstantURLPostParams); - EXPECT_EQ( - kDefaultSearchPolicyMap[DEFAULT_SEARCH_IMAGE_URL_POST_PARAMS].policy_name, - (const char * const)key::kDefaultSearchProviderImageURLPostParams); -} - -} // namespace policy diff --git a/chrome/browser/policy/configuration_policy_handler_unittest.cc b/chrome/browser/policy/configuration_policy_handler_unittest.cc index 703b8b839a..7d66c1ff43 100644 --- a/chrome/browser/policy/configuration_policy_handler_unittest.cc +++ b/chrome/browser/policy/configuration_policy_handler_unittest.cc @@ -5,7 +5,6 @@ #include "base/callback.h" #include "base/memory/scoped_ptr.h" #include "base/prefs/pref_value_map.h" -#include "chrome/browser/extensions/external_policy_loader.h" #include "chrome/browser/policy/configuration_policy_handler.h" #include "chrome/browser/policy/external_data_fetcher.h" #include "chrome/browser/policy/policy_error_map.h" @@ -485,208 +484,4 @@ TEST(IntPercentageToDoublePolicyHandler, ApplyPolicySettingsDontClamp) { EXPECT_TRUE(base::Value::Equals(expected.get(), value)); } -TEST(ExtensionListPolicyHandlerTest, CheckPolicySettings) { - base::ListValue list; - PolicyMap policy_map; - PolicyErrorMap errors; - ExtensionListPolicyHandler handler( - key::kExtensionInstallBlacklist, kTestPref, true); - - policy_map.Set(key::kExtensionInstallBlacklist, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, list.DeepCopy(), NULL); - errors.Clear(); - EXPECT_TRUE(handler.CheckPolicySettings(policy_map, &errors)); - EXPECT_TRUE(errors.empty()); - - list.Append(Value::CreateStringValue("abcdefghijklmnopabcdefghijklmnop")); - policy_map.Set(key::kExtensionInstallBlacklist, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, list.DeepCopy(), NULL); - errors.Clear(); - EXPECT_TRUE(handler.CheckPolicySettings(policy_map, &errors)); - EXPECT_TRUE(errors.empty()); - - list.Append(Value::CreateStringValue("*")); - policy_map.Set(key::kExtensionInstallBlacklist, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, list.DeepCopy(), NULL); - errors.Clear(); - EXPECT_TRUE(handler.CheckPolicySettings(policy_map, &errors)); - EXPECT_TRUE(errors.empty()); - - list.Append(Value::CreateStringValue("invalid")); - policy_map.Set(key::kExtensionInstallBlacklist, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, list.DeepCopy(), NULL); - errors.Clear(); - EXPECT_TRUE(handler.CheckPolicySettings(policy_map, &errors)); - EXPECT_FALSE(errors.empty()); - EXPECT_FALSE(errors.GetErrors(key::kExtensionInstallBlacklist).empty()); -} - -TEST(ExtensionListPolicyHandlerTest, ApplyPolicySettings) { - base::ListValue policy; - base::ListValue expected; - PolicyMap policy_map; - PrefValueMap prefs; - base::Value* value = NULL; - ExtensionListPolicyHandler handler( - key::kExtensionInstallBlacklist, kTestPref, false); - - policy.Append(Value::CreateStringValue("abcdefghijklmnopabcdefghijklmnop")); - expected.Append(Value::CreateStringValue("abcdefghijklmnopabcdefghijklmnop")); - - policy_map.Set(key::kExtensionInstallBlacklist, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, policy.DeepCopy(), NULL); - handler.ApplyPolicySettings(policy_map, &prefs); - EXPECT_TRUE(prefs.GetValue(kTestPref, &value)); - EXPECT_TRUE(base::Value::Equals(&expected, value)); - - policy.Append(Value::CreateStringValue("invalid")); - policy_map.Set(key::kExtensionInstallBlacklist, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, policy.DeepCopy(), NULL); - handler.ApplyPolicySettings(policy_map, &prefs); - EXPECT_TRUE(prefs.GetValue(kTestPref, &value)); - EXPECT_TRUE(base::Value::Equals(&expected, value)); -} - -TEST(ExtensionInstallForcelistPolicyHandlerTest, CheckPolicySettings) { - base::ListValue list; - PolicyMap policy_map; - PolicyErrorMap errors; - ExtensionInstallForcelistPolicyHandler handler(kTestPref); - - policy_map.Set(key::kExtensionInstallForcelist, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, list.DeepCopy(), NULL); - errors.Clear(); - EXPECT_TRUE(handler.CheckPolicySettings(policy_map, &errors)); - EXPECT_TRUE(errors.empty()); - - list.AppendString("abcdefghijklmnopabcdefghijklmnop;http://example.com"); - policy_map.Set(key::kExtensionInstallForcelist, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, list.DeepCopy(), NULL); - errors.Clear(); - EXPECT_TRUE(handler.CheckPolicySettings(policy_map, &errors)); - EXPECT_TRUE(errors.empty()); - - // Add an erroneous entry. This should generate an error, but the good - // entry should still be translated successfully. - list.AppendString("adfasdf;http://example.com"); - policy_map.Set(key::kExtensionInstallForcelist, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, list.DeepCopy(), NULL); - errors.Clear(); - EXPECT_TRUE(handler.CheckPolicySettings(policy_map, &errors)); - EXPECT_EQ(1U, errors.size()); - - // Add an entry with bad URL, which should generate another error. - list.AppendString("abcdefghijklmnopabcdefghijklmnop;nourl"); - policy_map.Set(key::kExtensionInstallForcelist, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, list.DeepCopy(), NULL); - errors.Clear(); - EXPECT_TRUE(handler.CheckPolicySettings(policy_map, &errors)); - EXPECT_EQ(2U, errors.size()); - - // Just an extension ID should also generate an error. - list.AppendString("abcdefghijklmnopabcdefghijklmnop"); - policy_map.Set(key::kExtensionInstallForcelist, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, list.DeepCopy(), NULL); - errors.Clear(); - EXPECT_TRUE(handler.CheckPolicySettings(policy_map, &errors)); - EXPECT_EQ(3U, errors.size()); -} - -TEST(ExtensionInstallForcelistPolicyHandlerTest, ApplyPolicySettings) { - base::ListValue policy; - base::DictionaryValue expected; - PolicyMap policy_map; - PrefValueMap prefs; - base::Value* value = NULL; - ExtensionInstallForcelistPolicyHandler handler(kTestPref); - - handler.ApplyPolicySettings(policy_map, &prefs); - EXPECT_FALSE(prefs.GetValue(kTestPref, &value)); - EXPECT_FALSE(value); - - policy_map.Set(key::kExtensionInstallForcelist, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, policy.DeepCopy(), NULL); - handler.ApplyPolicySettings(policy_map, &prefs); - EXPECT_TRUE(prefs.GetValue(kTestPref, &value)); - EXPECT_TRUE(base::Value::Equals(&expected, value)); - - policy.AppendString("abcdefghijklmnopabcdefghijklmnop;http://example.com"); - extensions::ExternalPolicyLoader::AddExtension( - &expected, "abcdefghijklmnopabcdefghijklmnop", "http://example.com"); - policy_map.Set(key::kExtensionInstallForcelist, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, policy.DeepCopy(), NULL); - handler.ApplyPolicySettings(policy_map, &prefs); - EXPECT_TRUE(prefs.GetValue(kTestPref, &value)); - EXPECT_TRUE(base::Value::Equals(&expected, value)); - - policy.AppendString("invalid"); - policy_map.Set(key::kExtensionInstallForcelist, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, policy.DeepCopy(), NULL); - handler.ApplyPolicySettings(policy_map, &prefs); - EXPECT_TRUE(prefs.GetValue(kTestPref, &value)); - EXPECT_TRUE(base::Value::Equals(&expected, value)); -} - -TEST(ExtensionURLPatternListPolicyHandlerTest, CheckPolicySettings) { - base::ListValue list; - PolicyMap policy_map; - PolicyErrorMap errors; - ExtensionURLPatternListPolicyHandler handler(key::kExtensionInstallSources, - kTestPref); - - policy_map.Set(key::kExtensionInstallSources, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, list.DeepCopy(), NULL); - errors.Clear(); - EXPECT_TRUE(handler.CheckPolicySettings(policy_map, &errors)); - EXPECT_TRUE(errors.empty()); - - list.Append(Value::CreateStringValue("http://*.google.com/*")); - policy_map.Set(key::kExtensionInstallSources, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, list.DeepCopy(), NULL); - errors.Clear(); - EXPECT_TRUE(handler.CheckPolicySettings(policy_map, &errors)); - EXPECT_TRUE(errors.empty()); - - list.Append(Value::CreateStringValue("<all_urls>")); - policy_map.Set(key::kExtensionInstallSources, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, list.DeepCopy(), NULL); - errors.Clear(); - EXPECT_TRUE(handler.CheckPolicySettings(policy_map, &errors)); - EXPECT_TRUE(errors.empty()); - - list.Append(Value::CreateStringValue("invalid")); - policy_map.Set(key::kExtensionInstallSources, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, list.DeepCopy(), NULL); - errors.Clear(); - EXPECT_FALSE(handler.CheckPolicySettings(policy_map, &errors)); - EXPECT_FALSE(errors.empty()); - EXPECT_FALSE(errors.GetErrors(key::kExtensionInstallSources).empty()); - - // URLPattern syntax has a different way to express 'all urls'. Though '*' - // would be compatible today, it would be brittle, so we disallow. - list.Append(Value::CreateStringValue("*")); - policy_map.Set(key::kExtensionInstallSources, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, list.DeepCopy(), NULL); - errors.Clear(); - EXPECT_FALSE(handler.CheckPolicySettings(policy_map, &errors)); - EXPECT_FALSE(errors.empty()); - EXPECT_FALSE(errors.GetErrors(key::kExtensionInstallSources).empty()); -} - -TEST(ExtensionURLPatternListPolicyHandlerTest, ApplyPolicySettings) { - base::ListValue list; - PolicyMap policy_map; - PrefValueMap prefs; - base::Value* value = NULL; - ExtensionURLPatternListPolicyHandler handler(key::kExtensionInstallSources, - kTestPref); - - list.Append(Value::CreateStringValue("https://corp.monkey.net/*")); - policy_map.Set(key::kExtensionInstallSources, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, list.DeepCopy(), NULL); - handler.ApplyPolicySettings(policy_map, &prefs); - ASSERT_TRUE(prefs.GetValue(kTestPref, &value)); - EXPECT_TRUE(base::Value::Equals(&list, value)); -} - } // namespace policy diff --git a/chrome/browser/policy/configuration_policy_pref_store_unittest.cc b/chrome/browser/policy/configuration_policy_pref_store_unittest.cc index 6df3ae942f..cc68d69158 100644 --- a/chrome/browser/policy/configuration_policy_pref_store_unittest.cc +++ b/chrome/browser/policy/configuration_policy_pref_store_unittest.cc @@ -2,28 +2,25 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "chrome/browser/policy/configuration_policy_pref_store_unittest.h" + #include <string> #include "base/callback.h" #include "base/files/file_path.h" -#include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop.h" #include "base/prefs/pref_store_observer_mock.h" #include "base/run_loop.h" #include "chrome/browser/policy/configuration_policy_handler.h" -#include "chrome/browser/policy/configuration_policy_handler_list.h" #include "chrome/browser/policy/configuration_policy_pref_store.h" #include "chrome/browser/policy/external_data_fetcher.h" -#include "chrome/browser/policy/mock_configuration_policy_provider.h" #include "chrome/browser/policy/policy_map.h" #include "chrome/browser/policy/policy_service_impl.h" #include "chrome/browser/prefs/incognito_mode_prefs.h" -#include "chrome/browser/prefs/proxy_config_dictionary.h" #include "chrome/common/content_settings.h" #include "chrome/common/pref_names.h" +#include "components/policy/core/common/policy_pref_names.h" #include "policy/policy_constants.h" #include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" using testing::Mock; using testing::Return; @@ -46,36 +43,29 @@ class PolicyAndPref { const char* pref_name_; }; -class ConfigurationPolicyPrefStoreTest : public testing::Test { - protected: - ConfigurationPolicyPrefStoreTest() { - EXPECT_CALL(provider_, IsInitializationComplete(_)) - .WillRepeatedly(Return(false)); - provider_.Init(); - PolicyServiceImpl::Providers providers; - providers.push_back(&provider_); - policy_service_.reset(new PolicyServiceImpl(providers)); - store_ = new ConfigurationPolicyPrefStore(policy_service_.get(), - &handler_list_, - POLICY_LEVEL_MANDATORY); - } +ConfigurationPolicyPrefStoreTest::ConfigurationPolicyPrefStoreTest() { + EXPECT_CALL(provider_, IsInitializationComplete(_)) + .WillRepeatedly(Return(false)); + provider_.Init(); + PolicyServiceImpl::Providers providers; + providers.push_back(&provider_); + policy_service_.reset(new PolicyServiceImpl(providers)); + store_ = new ConfigurationPolicyPrefStore( + policy_service_.get(), &handler_list_, POLICY_LEVEL_MANDATORY); +} - virtual void TearDown() OVERRIDE { - provider_.Shutdown(); - } +ConfigurationPolicyPrefStoreTest::~ConfigurationPolicyPrefStoreTest() {} - void UpdateProviderPolicy(const PolicyMap& policy) { - provider_.UpdateChromePolicy(policy); - base::RunLoop loop; - loop.RunUntilIdle(); - } +void ConfigurationPolicyPrefStoreTest::TearDown() { + provider_.Shutdown(); +} - ConfigurationPolicyHandlerList handler_list_; - MockConfigurationPolicyProvider provider_; - scoped_ptr<PolicyServiceImpl> policy_service_; - scoped_refptr<ConfigurationPolicyPrefStore> store_; - base::MessageLoop loop_; -}; +void ConfigurationPolicyPrefStoreTest::UpdateProviderPolicy( + const PolicyMap& policy) { + provider_.UpdateChromePolicy(policy); + base::RunLoop loop; + loop.RunUntilIdle(); +} // Test cases for list-valued policy settings. class ConfigurationPolicyPrefStoreListTest @@ -365,544 +355,10 @@ INSTANTIATE_TEST_CASE_P( PolicyAndPref(key::kMediaCacheSize, prefs::kMediaCacheSize), PolicyAndPref(key::kPolicyRefreshRate, - prefs::kUserPolicyRefreshRate), + policy_prefs::kUserPolicyRefreshRate), PolicyAndPref(key::kMaxConnectionsPerProxy, prefs::kMaxConnectionsPerProxy))); -// Test cases for the proxy policy settings. -class ConfigurationPolicyPrefStoreProxyTest - : public ConfigurationPolicyPrefStoreTest { - protected: - // Verify that all the proxy prefs are set to the specified expected values. - void VerifyProxyPrefs( - const std::string& expected_proxy_server, - const std::string& expected_proxy_pac_url, - const std::string& expected_proxy_bypass_list, - const ProxyPrefs::ProxyMode& expected_proxy_mode) { - const base::Value* value = NULL; - ASSERT_TRUE(store_->GetValue(prefs::kProxy, &value)); - ASSERT_EQ(base::Value::TYPE_DICTIONARY, value->GetType()); - ProxyConfigDictionary dict( - static_cast<const base::DictionaryValue*>(value)); - std::string s; - if (expected_proxy_server.empty()) { - EXPECT_FALSE(dict.GetProxyServer(&s)); - } else { - ASSERT_TRUE(dict.GetProxyServer(&s)); - EXPECT_EQ(expected_proxy_server, s); - } - if (expected_proxy_pac_url.empty()) { - EXPECT_FALSE(dict.GetPacUrl(&s)); - } else { - ASSERT_TRUE(dict.GetPacUrl(&s)); - EXPECT_EQ(expected_proxy_pac_url, s); - } - if (expected_proxy_bypass_list.empty()) { - EXPECT_FALSE(dict.GetBypassList(&s)); - } else { - ASSERT_TRUE(dict.GetBypassList(&s)); - EXPECT_EQ(expected_proxy_bypass_list, s); - } - ProxyPrefs::ProxyMode mode; - ASSERT_TRUE(dict.GetMode(&mode)); - EXPECT_EQ(expected_proxy_mode, mode); - } -}; - -TEST_F(ConfigurationPolicyPrefStoreProxyTest, ManualOptions) { - PolicyMap policy; - policy.Set(key::kProxyBypassList, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateStringValue("http://chromium.org/override"), - NULL); - policy.Set(key::kProxyServer, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateStringValue("chromium.org"), NULL); - policy.Set( - key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateIntegerValue( - ProxyPolicyHandler::PROXY_MANUALLY_CONFIGURED_PROXY_SERVER_MODE), - NULL); - UpdateProviderPolicy(policy); - - VerifyProxyPrefs("chromium.org", - std::string(), - "http://chromium.org/override", - ProxyPrefs::MODE_FIXED_SERVERS); -} - -TEST_F(ConfigurationPolicyPrefStoreProxyTest, ManualOptionsReversedApplyOrder) { - PolicyMap policy; - policy.Set( - key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateIntegerValue( - ProxyPolicyHandler::PROXY_MANUALLY_CONFIGURED_PROXY_SERVER_MODE), - NULL); - policy.Set(key::kProxyBypassList, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateStringValue("http://chromium.org/override"), - NULL); - policy.Set(key::kProxyServer, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateStringValue("chromium.org"), NULL); - UpdateProviderPolicy(policy); - - VerifyProxyPrefs("chromium.org", - std::string(), - "http://chromium.org/override", - ProxyPrefs::MODE_FIXED_SERVERS); -} - -TEST_F(ConfigurationPolicyPrefStoreProxyTest, ManualOptionsInvalid) { - PolicyMap policy; - policy.Set( - key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateIntegerValue( - ProxyPolicyHandler::PROXY_MANUALLY_CONFIGURED_PROXY_SERVER_MODE), - NULL); - UpdateProviderPolicy(policy); - - const base::Value* value = NULL; - EXPECT_FALSE(store_->GetValue(prefs::kProxy, &value)); -} - - -TEST_F(ConfigurationPolicyPrefStoreProxyTest, NoProxyServerMode) { - PolicyMap policy; - policy.Set(key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateIntegerValue( - ProxyPolicyHandler::PROXY_SERVER_MODE), - NULL); - UpdateProviderPolicy(policy); - VerifyProxyPrefs( - std::string(), std::string(), std::string(), ProxyPrefs::MODE_DIRECT); -} - -TEST_F(ConfigurationPolicyPrefStoreProxyTest, NoProxyModeName) { - PolicyMap policy; - policy.Set(key::kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateStringValue(ProxyPrefs::kDirectProxyModeName), - NULL); - UpdateProviderPolicy(policy); - VerifyProxyPrefs( - std::string(), std::string(), std::string(), ProxyPrefs::MODE_DIRECT); -} - -TEST_F(ConfigurationPolicyPrefStoreProxyTest, AutoDetectProxyServerMode) { - PolicyMap policy; - policy.Set( - key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateIntegerValue( - ProxyPolicyHandler::PROXY_AUTO_DETECT_PROXY_SERVER_MODE), - NULL); - UpdateProviderPolicy(policy); - VerifyProxyPrefs(std::string(), - std::string(), - std::string(), - ProxyPrefs::MODE_AUTO_DETECT); -} - -TEST_F(ConfigurationPolicyPrefStoreProxyTest, AutoDetectProxyModeName) { - PolicyMap policy; - policy.Set(key::kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateStringValue( - ProxyPrefs::kAutoDetectProxyModeName), - NULL); - UpdateProviderPolicy(policy); - VerifyProxyPrefs(std::string(), - std::string(), - std::string(), - ProxyPrefs::MODE_AUTO_DETECT); -} - -TEST_F(ConfigurationPolicyPrefStoreProxyTest, PacScriptProxyMode) { - PolicyMap policy; - policy.Set(key::kProxyPacUrl, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateStringValue("http://short.org/proxy.pac"), - NULL); - policy.Set(key::kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateStringValue( - ProxyPrefs::kPacScriptProxyModeName), - NULL); - UpdateProviderPolicy(policy); - VerifyProxyPrefs(std::string(), - "http://short.org/proxy.pac", - std::string(), - ProxyPrefs::MODE_PAC_SCRIPT); -} - -TEST_F(ConfigurationPolicyPrefStoreProxyTest, PacScriptProxyModeInvalid) { - PolicyMap policy; - policy.Set(key::kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateStringValue( - ProxyPrefs::kPacScriptProxyModeName), - NULL); - UpdateProviderPolicy(policy); - const base::Value* value = NULL; - EXPECT_FALSE(store_->GetValue(prefs::kProxy, &value)); -} - -// Regression test for http://crbug.com/78016, CPanel returns empty strings -// for unset properties. -TEST_F(ConfigurationPolicyPrefStoreProxyTest, PacScriptProxyModeBug78016) { - PolicyMap policy; - policy.Set(key::kProxyServer, - POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, - base::Value::CreateStringValue(std::string()), - NULL); - policy.Set(key::kProxyPacUrl, - POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, - base::Value::CreateStringValue("http://short.org/proxy.pac"), - NULL); - policy.Set(key::kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateStringValue( - ProxyPrefs::kPacScriptProxyModeName), - NULL); - UpdateProviderPolicy(policy); - VerifyProxyPrefs(std::string(), - "http://short.org/proxy.pac", - std::string(), - ProxyPrefs::MODE_PAC_SCRIPT); -} - -TEST_F(ConfigurationPolicyPrefStoreProxyTest, UseSystemProxyServerMode) { - PolicyMap policy; - policy.Set(key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateIntegerValue( - ProxyPolicyHandler::PROXY_USE_SYSTEM_PROXY_SERVER_MODE), - NULL); - UpdateProviderPolicy(policy); - VerifyProxyPrefs( - std::string(), std::string(), std::string(), ProxyPrefs::MODE_SYSTEM); -} - -TEST_F(ConfigurationPolicyPrefStoreProxyTest, UseSystemProxyMode) { - PolicyMap policy; - policy.Set(key::kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateStringValue(ProxyPrefs::kSystemProxyModeName), - NULL); - UpdateProviderPolicy(policy); - VerifyProxyPrefs( - std::string(), std::string(), std::string(), ProxyPrefs::MODE_SYSTEM); -} - -TEST_F(ConfigurationPolicyPrefStoreProxyTest, - ProxyModeOverridesProxyServerMode) { - PolicyMap policy; - policy.Set(key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateIntegerValue( - ProxyPolicyHandler::PROXY_SERVER_MODE), - NULL); - policy.Set(key::kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateStringValue( - ProxyPrefs::kAutoDetectProxyModeName), - NULL); - UpdateProviderPolicy(policy); - VerifyProxyPrefs(std::string(), - std::string(), - std::string(), - ProxyPrefs::MODE_AUTO_DETECT); -} - -TEST_F(ConfigurationPolicyPrefStoreProxyTest, ProxyInvalid) { - // No mode expects all three parameters being set. - PolicyMap policy; - policy.Set(key::kProxyPacUrl, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateStringValue("http://short.org/proxy.pac"), - NULL); - policy.Set(key::kProxyBypassList, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateStringValue("http://chromium.org/override"), - NULL); - policy.Set(key::kProxyServer, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateStringValue("chromium.org"), NULL); - for (int i = 0; i < ProxyPolicyHandler::MODE_COUNT; ++i) { - policy.Set(key::kProxyServerMode, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, base::Value::CreateIntegerValue(i), NULL); - UpdateProviderPolicy(policy); - const base::Value* value = NULL; - EXPECT_FALSE(store_->GetValue(prefs::kProxy, &value)); - } -} - -class ConfigurationPolicyPrefStoreDefaultSearchTest - : public ConfigurationPolicyPrefStoreTest { - public: - ConfigurationPolicyPrefStoreDefaultSearchTest() { - default_alternate_urls_.AppendString( - "http://www.google.com/#q={searchTerms}"); - default_alternate_urls_.AppendString( - "http://www.google.com/search#q={searchTerms}"); - } - - protected: - static const char* const kSearchURL; - static const char* const kSuggestURL; - static const char* const kIconURL; - static const char* const kName; - static const char* const kKeyword; - static const char* const kReplacementKey; - static const char* const kImageURL; - static const char* const kImageParams; - static const char* const kNewTabURL; - - // Build a default search policy by setting search-related keys in |policy| to - // reasonable values. You can update any of the keys after calling this - // method. - void BuildDefaultSearchPolicy(PolicyMap* policy); - - base::ListValue default_alternate_urls_; -}; - -const char* const ConfigurationPolicyPrefStoreDefaultSearchTest::kSearchURL = - "http://test.com/search?t={searchTerms}"; -const char* const ConfigurationPolicyPrefStoreDefaultSearchTest::kSuggestURL = - "http://test.com/sugg?={searchTerms}"; -const char* const ConfigurationPolicyPrefStoreDefaultSearchTest::kIconURL = - "http://test.com/icon.jpg"; -const char* const ConfigurationPolicyPrefStoreDefaultSearchTest::kName = - "MyName"; -const char* const ConfigurationPolicyPrefStoreDefaultSearchTest::kKeyword = - "MyKeyword"; -const char* const - ConfigurationPolicyPrefStoreDefaultSearchTest::kReplacementKey = "espv"; -const char* const ConfigurationPolicyPrefStoreDefaultSearchTest::kImageURL = - "http://test.com/searchbyimage/upload"; -const char* const ConfigurationPolicyPrefStoreDefaultSearchTest::kImageParams = - "image_content=content,image_url=http://test.com/test.png"; -const char* const ConfigurationPolicyPrefStoreDefaultSearchTest::kNewTabURL = - "http://test.com/newtab"; - -void ConfigurationPolicyPrefStoreDefaultSearchTest:: - BuildDefaultSearchPolicy(PolicyMap* policy) { - base::ListValue* encodings = new base::ListValue(); - encodings->AppendString("UTF-16"); - encodings->AppendString("UTF-8"); - policy->Set(key::kDefaultSearchProviderEnabled, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, base::Value::CreateBooleanValue(true), NULL); - policy->Set(key::kDefaultSearchProviderSearchURL, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, base::Value::CreateStringValue(kSearchURL), - NULL); - policy->Set(key::kDefaultSearchProviderName, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, base::Value::CreateStringValue(kName), NULL); - policy->Set(key::kDefaultSearchProviderKeyword, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, base::Value::CreateStringValue(kKeyword), - NULL); - policy->Set(key::kDefaultSearchProviderSuggestURL, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, base::Value::CreateStringValue(kSuggestURL), - NULL); - policy->Set(key::kDefaultSearchProviderIconURL, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, base::Value::CreateStringValue(kIconURL), - NULL); - policy->Set(key::kDefaultSearchProviderEncodings, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, encodings, NULL); - policy->Set(key::kDefaultSearchProviderAlternateURLs, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, default_alternate_urls_.DeepCopy(), NULL); - policy->Set(key::kDefaultSearchProviderSearchTermsReplacementKey, - POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateStringValue(kReplacementKey), NULL); - policy->Set(key::kDefaultSearchProviderImageURL, - POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateStringValue(kImageURL), NULL); - policy->Set(key::kDefaultSearchProviderImageURLPostParams, - POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateStringValue(kImageParams), NULL); - policy->Set(key::kDefaultSearchProviderNewTabURL, - POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, - base::Value::CreateStringValue(kNewTabURL), NULL); -} - -// Checks that if the policy for default search is valid, i.e. there's a -// search URL, that all the elements have been given proper defaults. -TEST_F(ConfigurationPolicyPrefStoreDefaultSearchTest, MinimallyDefined) { - PolicyMap policy; - policy.Set(key::kDefaultSearchProviderEnabled, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, base::Value::CreateBooleanValue(true), NULL); - policy.Set(key::kDefaultSearchProviderSearchURL, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, base::Value::CreateStringValue(kSearchURL), - NULL); - UpdateProviderPolicy(policy); - - const base::Value* value = NULL; - EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderSearchURL, &value)); - EXPECT_TRUE(base::StringValue(kSearchURL).Equals(value)); - - EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderName, &value)); - EXPECT_TRUE(base::StringValue("test.com").Equals(value)); - - EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderKeyword, &value)); - EXPECT_TRUE(base::StringValue("test.com").Equals(value)); - - EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderSuggestURL, - &value)); - EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); - - EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderIconURL, &value)); - EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); - - EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderEncodings, &value)); - EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); - - EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderInstantURL, - &value)); - EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); - - EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderAlternateURLs, - &value)); - EXPECT_TRUE(base::ListValue().Equals(value)); - - EXPECT_TRUE( - store_->GetValue(prefs::kDefaultSearchProviderSearchTermsReplacementKey, - &value)); - EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); - - EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderImageURL, &value)); - EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); - - EXPECT_TRUE(store_->GetValue( - prefs::kDefaultSearchProviderSearchURLPostParams, &value)); - EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); - - EXPECT_TRUE(store_->GetValue( - prefs::kDefaultSearchProviderSuggestURLPostParams, &value)); - EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); - - EXPECT_TRUE(store_->GetValue( - prefs::kDefaultSearchProviderInstantURLPostParams, &value)); - EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); - - EXPECT_TRUE(store_->GetValue( - prefs::kDefaultSearchProviderImageURLPostParams, &value)); - EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); - - EXPECT_TRUE(store_->GetValue( - prefs::kDefaultSearchProviderNewTabURL, &value)); - EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); -} - -// Checks that for a fully defined search policy, all elements have been -// read properly. -TEST_F(ConfigurationPolicyPrefStoreDefaultSearchTest, FullyDefined) { - PolicyMap policy; - BuildDefaultSearchPolicy(&policy); - UpdateProviderPolicy(policy); - - const base::Value* value = NULL; - EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderSearchURL, &value)); - EXPECT_TRUE(base::StringValue(kSearchURL).Equals(value)); - - EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderName, &value)); - EXPECT_TRUE(base::StringValue(kName).Equals(value)); - - EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderKeyword, &value)); - EXPECT_TRUE(base::StringValue(kKeyword).Equals(value)); - - EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderSuggestURL, - &value)); - EXPECT_TRUE(base::StringValue(kSuggestURL).Equals(value)); - - EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderIconURL, &value)); - EXPECT_TRUE(base::StringValue(kIconURL).Equals(value)); - - EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderEncodings, &value)); - EXPECT_TRUE(base::StringValue("UTF-16;UTF-8").Equals(value)); - - EXPECT_TRUE(store_->GetValue( - prefs::kDefaultSearchProviderAlternateURLs, &value)); - EXPECT_TRUE(default_alternate_urls_.Equals(value)); - - EXPECT_TRUE( - store_->GetValue(prefs::kDefaultSearchProviderSearchTermsReplacementKey, - &value)); - EXPECT_TRUE(base::StringValue(kReplacementKey).Equals(value)); - - EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderImageURL, &value)); - EXPECT_TRUE(base::StringValue(std::string(kImageURL)).Equals(value)); - - EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderImageURLPostParams, - &value)); - EXPECT_TRUE(base::StringValue(std::string(kImageParams)).Equals(value)); - - EXPECT_TRUE(store_->GetValue( - prefs::kDefaultSearchProviderSearchURLPostParams, &value)); - EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); - - EXPECT_TRUE(store_->GetValue( - prefs::kDefaultSearchProviderSuggestURLPostParams, &value)); - EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); - - EXPECT_TRUE(store_->GetValue( - prefs::kDefaultSearchProviderInstantURLPostParams, &value)); - EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); -} - -// Checks that if the default search policy is missing, that no elements of the -// default search policy will be present. -TEST_F(ConfigurationPolicyPrefStoreDefaultSearchTest, MissingUrl) { - PolicyMap policy; - BuildDefaultSearchPolicy(&policy); - policy.Erase(key::kDefaultSearchProviderSearchURL); - UpdateProviderPolicy(policy); - - EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderSearchURL, NULL)); - EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderName, NULL)); - EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderKeyword, NULL)); - EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderSuggestURL, NULL)); - EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderIconURL, NULL)); - EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderEncodings, NULL)); - EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderAlternateURLs, - NULL)); - EXPECT_FALSE(store_->GetValue( - prefs::kDefaultSearchProviderSearchTermsReplacementKey, NULL)); - EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderImageURL, NULL)); - EXPECT_FALSE(store_->GetValue( - prefs::kDefaultSearchProviderImageURLPostParams, NULL)); - EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderInstantURL, NULL)); - EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderNewTabURL, NULL)); -} - -// Checks that if the default search policy is invalid, that no elements of the -// default search policy will be present. -TEST_F(ConfigurationPolicyPrefStoreDefaultSearchTest, Invalid) { - PolicyMap policy; - BuildDefaultSearchPolicy(&policy); - const char* const bad_search_url = "http://test.com/noSearchTerms"; - policy.Set(key::kDefaultSearchProviderSearchURL, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, - base::Value::CreateStringValue(bad_search_url), NULL); - UpdateProviderPolicy(policy); - - EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderSearchURL, NULL)); - EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderName, NULL)); - EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderKeyword, NULL)); - EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderSuggestURL, NULL)); - EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderIconURL, NULL)); - EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderEncodings, NULL)); - EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderAlternateURLs, - NULL)); - EXPECT_FALSE(store_->GetValue( - prefs::kDefaultSearchProviderSearchTermsReplacementKey, NULL)); - EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderImageURL, NULL)); - EXPECT_FALSE(store_->GetValue( - prefs::kDefaultSearchProviderImageURLPostParams, NULL)); - EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderInstantURL, NULL)); - EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderNewTabURL, NULL)); -} - -// Checks that if the default search policy is invalid, that no elements of the -// default search policy will be present. -TEST_F(ConfigurationPolicyPrefStoreDefaultSearchTest, Disabled) { - PolicyMap policy; - policy.Set(key::kDefaultSearchProviderEnabled, POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, base::Value::CreateBooleanValue(false), NULL); - UpdateProviderPolicy(policy); - - const base::Value* value = NULL; - EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderEnabled, &value)); - base::FundamentalValue expected_enabled(false); - EXPECT_TRUE(base::Value::Equals(&expected_enabled, value)); - EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderSearchURL, &value)); - base::StringValue expected_search_url((std::string())); - EXPECT_TRUE(base::Value::Equals(&expected_search_url, value)); -} - // Tests Incognito mode availability preference setting. class ConfigurationPolicyPrefStoreIncognitoModeTest : public ConfigurationPolicyPrefStoreTest { @@ -1028,8 +484,8 @@ TEST_F(ConfigurationPolicyPrefStoreSyncTest, Disabled) { EXPECT_TRUE(sync_managed); } -// Test cases for how the DownloadDirectory and AllowFileSelectionDialogs policy -// influence the PromptForDownload preference. +// Test cases for how the AllowFileSelectionDialogs policy influences the +// PromptForDownload preference. class ConfigurationPolicyPrefStorePromptDownloadTest : public ConfigurationPolicyPrefStoreTest {}; @@ -1037,29 +493,6 @@ TEST_F(ConfigurationPolicyPrefStorePromptDownloadTest, Default) { EXPECT_FALSE(store_->GetValue(prefs::kPromptForDownload, NULL)); } -#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID) -TEST_F(ConfigurationPolicyPrefStorePromptDownloadTest, SetDownloadDirectory) { - PolicyMap policy; - EXPECT_FALSE(store_->GetValue(prefs::kPromptForDownload, NULL)); - policy.Set(key::kDownloadDirectory, - POLICY_LEVEL_MANDATORY, - POLICY_SCOPE_USER, - base::Value::CreateStringValue(std::string()), - NULL); - UpdateProviderPolicy(policy); - - // Setting a DownloadDirectory should disable the PromptForDownload pref. - const base::Value* value = NULL; - EXPECT_TRUE(store_->GetValue(prefs::kPromptForDownload, - &value)); - ASSERT_TRUE(value); - bool prompt_for_download = true; - bool result = value->GetAsBoolean(&prompt_for_download); - ASSERT_TRUE(result); - EXPECT_FALSE(prompt_for_download); -} -#endif // !defined(OS_CHROMEOS) && !defined(OS_ANDROID) - TEST_F(ConfigurationPolicyPrefStorePromptDownloadTest, EnableFileSelectionDialogs) { PolicyMap policy; diff --git a/chrome/browser/policy/configuration_policy_pref_store_unittest.h b/chrome/browser/policy/configuration_policy_pref_store_unittest.h new file mode 100644 index 0000000000..9fdf866156 --- /dev/null +++ b/chrome/browser/policy/configuration_policy_pref_store_unittest.h @@ -0,0 +1,40 @@ +// Copyright 2013 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_POLICY_CONFIGURATION_POLICY_PREF_STORE_UNITTEST_H_ +#define CHROME_BROWSER_POLICY_CONFIGURATION_POLICY_PREF_STORE_UNITTEST_H_ + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "chrome/browser/policy/configuration_policy_handler_list.h" +#include "chrome/browser/policy/mock_configuration_policy_provider.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace policy { + +class PolicyMap; +class PolicyService; +class ConfigurationPolicyPrefStore; + +class ConfigurationPolicyPrefStoreTest : public testing::Test { + protected: + ConfigurationPolicyPrefStoreTest(); + virtual ~ConfigurationPolicyPrefStoreTest(); + virtual void TearDown() OVERRIDE; + void UpdateProviderPolicy(const PolicyMap& policy); + + ConfigurationPolicyHandlerList handler_list_; + MockConfigurationPolicyProvider provider_; + scoped_ptr<PolicyService> policy_service_; + scoped_refptr<ConfigurationPolicyPrefStore> store_; + base::MessageLoop loop_; + + private: + DISALLOW_COPY_AND_ASSIGN(ConfigurationPolicyPrefStoreTest); +}; + +} // namespace policy + +#endif // CHROME_BROWSER_POLICY_CONFIGURATION_POLICY_PREF_STORE_UNITTEST_H_ diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc index eeb3e04c7a..95fe37c33a 100644 --- a/chrome/browser/policy/policy_browsertest.cc +++ b/chrome/browser/policy/policy_browsertest.cc @@ -469,62 +469,49 @@ class TestAudioObserver : public chromeos::CrasAudioHandler::AudioObserver { }; #endif -// This is a customized version of content::WindowedNotificationObserver that -// waits until either of the two provided notification types is observed. -// See content::WindowedNotificationObserver for further documentation. -class OneOfTwoNotificationsObserver : public content::NotificationObserver { +// This class waits until either a load stops or the WebContents is destroyed. +class WebContentsLoadedOrDestroyedWatcher + : public content::WebContentsObserver { public: - // Set up to wait for one of two notifications. - OneOfTwoNotificationsObserver(int notification_type1, int notification_type2); - virtual ~OneOfTwoNotificationsObserver(); + explicit WebContentsLoadedOrDestroyedWatcher( + content::WebContents* web_contents); + virtual ~WebContentsLoadedOrDestroyedWatcher(); - // Wait until one of the specified notifications is observed. If either - // notification has already been received, Wait() returns immediately. + // Waits until the WebContents's load is done or until it is destroyed. void Wait(); - // content::NotificationObserver: - virtual void Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) OVERRIDE; + // Overridden WebContentsObserver methods. + virtual void WebContentsDestroyed( + content::WebContents* web_contents) OVERRIDE; + virtual void DidStopLoading( + content::RenderViewHost* render_view_host) OVERRIDE; private: - bool seen_; - bool running_; - content::NotificationRegistrar registrar_; scoped_refptr<content::MessageLoopRunner> message_loop_runner_; - DISALLOW_COPY_AND_ASSIGN(OneOfTwoNotificationsObserver); + DISALLOW_COPY_AND_ASSIGN(WebContentsLoadedOrDestroyedWatcher); }; -OneOfTwoNotificationsObserver::OneOfTwoNotificationsObserver( - int notification_type1, int notification_type2) - : seen_(false), running_(false) { - registrar_.Add(this, notification_type1, - content::NotificationService::AllSources()); - registrar_.Add(this, notification_type2, - content::NotificationService::AllSources()); +WebContentsLoadedOrDestroyedWatcher::WebContentsLoadedOrDestroyedWatcher( + content::WebContents* web_contents) + : content::WebContentsObserver(web_contents), + message_loop_runner_(new content::MessageLoopRunner) { } -OneOfTwoNotificationsObserver::~OneOfTwoNotificationsObserver() {} +WebContentsLoadedOrDestroyedWatcher::~WebContentsLoadedOrDestroyedWatcher() {} -void OneOfTwoNotificationsObserver::Wait() { - if (seen_) - return; - running_ = true; - message_loop_runner_ = new content::MessageLoopRunner; +void WebContentsLoadedOrDestroyedWatcher::Wait() { message_loop_runner_->Run(); - EXPECT_TRUE(seen_); } -// NotificationObserver: -void OneOfTwoNotificationsObserver::Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) { - seen_ = true; - if (!running_) - return; +void WebContentsLoadedOrDestroyedWatcher::WebContentsDestroyed( + content::WebContents* web_contents) { + message_loop_runner_->Quit(); +} + +void WebContentsLoadedOrDestroyedWatcher::DidStopLoading( + content::RenderViewHost* render_view_host) { message_loop_runner_->Quit(); - running_ = false; } #if !defined(OS_MACOSX) @@ -1533,7 +1520,9 @@ IN_PROC_BROWSER_TEST_F(PolicyTest, ExtensionInstallForcelist) { base::FilePath test_path; ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_path)); - TestRequestInterceptor interceptor("update.extension"); + TestRequestInterceptor interceptor( + "update.extension", + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)); interceptor.PushJobCallback( TestRequestInterceptor::FileJob( test_path.Append(kTestExtensionsDir).Append(kGood2CrxManifestName))); @@ -1569,9 +1558,10 @@ IN_PROC_BROWSER_TEST_F(PolicyTest, ExtensionInstallForcelist) { if (!(*iter)->IsLoading()) { ++iter; } else { - OneOfTwoNotificationsObserver( - content::NOTIFICATION_LOAD_STOP, - content::NOTIFICATION_WEB_CONTENTS_DESTROYED).Wait(); + content::WebContents* web_contents = + content::WebContents::FromRenderViewHost(*iter); + ASSERT_TRUE(web_contents); + WebContentsLoadedOrDestroyedWatcher(web_contents).Wait(); // Test activity may have modified the set of extension processes during // message processing, so re-start the iteration to catch added/removed @@ -1753,6 +1743,7 @@ IN_PROC_BROWSER_TEST_F(PolicyTest, Javascript) { EXPECT_TRUE(IsJavascriptEnabled(contents)); EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_DEV_TOOLS)); EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_DEV_TOOLS_CONSOLE)); + EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_DEV_TOOLS_DEVICES)); // Disable Javascript via policy. PolicyMap policies; @@ -1765,6 +1756,7 @@ IN_PROC_BROWSER_TEST_F(PolicyTest, Javascript) { // Developer tools still work when javascript is disabled. EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_DEV_TOOLS)); EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_DEV_TOOLS_CONSOLE)); + EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_DEV_TOOLS_DEVICES)); // Javascript is always enabled for the internal pages. ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIAboutURL)); EXPECT_TRUE(IsJavascriptEnabled(contents)); diff --git a/chrome/browser/policy/policy_statistics_collector.cc b/chrome/browser/policy/policy_statistics_collector.cc index fb648a17aa..e972c947c8 100644 --- a/chrome/browser/policy/policy_statistics_collector.cc +++ b/chrome/browser/policy/policy_statistics_collector.cc @@ -41,7 +41,7 @@ void PolicyStatisticsCollector::Initialize() { TimeDelta update_rate = TimeDelta::FromMilliseconds(kStatisticsUpdateRate); Time last_update = Time::FromInternalValue( - prefs_->GetInt64(prefs::kLastPolicyStatisticsUpdate)); + prefs_->GetInt64(policy_prefs::kLastPolicyStatisticsUpdate)); TimeDelta delay = std::max(Time::Now() - last_update, TimeDelta::FromDays(0)); if (delay >= update_rate) CollectStatistics(); @@ -51,7 +51,7 @@ void PolicyStatisticsCollector::Initialize() { // static void PolicyStatisticsCollector::RegisterPrefs(PrefRegistrySimple* registry) { - registry->RegisterInt64Pref(prefs::kLastPolicyStatisticsUpdate, 0); + registry->RegisterInt64Pref(policy_prefs::kLastPolicyStatisticsUpdate, 0); } void PolicyStatisticsCollector::RecordPolicyUse(int id) { @@ -86,7 +86,7 @@ void PolicyStatisticsCollector::CollectStatistics() { } // Take care of next update. - prefs_->SetInt64(prefs::kLastPolicyStatisticsUpdate, + prefs_->SetInt64(policy_prefs::kLastPolicyStatisticsUpdate, base::Time::Now().ToInternalValue()); ScheduleUpdate(base::TimeDelta::FromMilliseconds(kStatisticsUpdateRate)); } diff --git a/chrome/browser/policy/policy_statistics_collector_unittest.cc b/chrome/browser/policy/policy_statistics_collector_unittest.cc index 11fca7ade7..e55d67a224 100644 --- a/chrome/browser/policy/policy_statistics_collector_unittest.cc +++ b/chrome/browser/policy/policy_statistics_collector_unittest.cc @@ -62,7 +62,8 @@ class PolicyStatisticsCollectorTest : public testing::Test { } virtual void SetUp() OVERRIDE { - prefs_.registry()->RegisterInt64Pref(prefs::kLastPolicyStatisticsUpdate, 0); + prefs_.registry()->RegisterInt64Pref( + policy_prefs::kLastPolicyStatisticsUpdate, 0); // Find ids for kTestPolicy1 and kTestPolicy2. const policy::PolicyDefinitionList* policy_list = @@ -123,7 +124,7 @@ class PolicyStatisticsCollectorTest : public testing::Test { TEST_F(PolicyStatisticsCollectorTest, CollectPending) { SetPolicy(kTestPolicy1); - prefs_.SetInt64(prefs::kLastPolicyStatisticsUpdate, + prefs_.SetInt64(policy_prefs::kLastPolicyStatisticsUpdate, (base::Time::Now() - update_delay_).ToInternalValue()); EXPECT_CALL(*policy_statistics_collector_.get(), @@ -138,7 +139,7 @@ TEST_F(PolicyStatisticsCollectorTest, CollectPendingVeryOld) { SetPolicy(kTestPolicy1); // Must not be 0.0 (read comment for Time::FromDoubleT). - prefs_.SetInt64(prefs::kLastPolicyStatisticsUpdate, + prefs_.SetInt64(policy_prefs::kLastPolicyStatisticsUpdate, base::Time::FromDoubleT(1.0).ToInternalValue()); EXPECT_CALL(*policy_statistics_collector_.get(), @@ -152,7 +153,7 @@ TEST_F(PolicyStatisticsCollectorTest, CollectPendingVeryOld) { TEST_F(PolicyStatisticsCollectorTest, CollectLater) { SetPolicy(kTestPolicy1); - prefs_.SetInt64(prefs::kLastPolicyStatisticsUpdate, + prefs_.SetInt64(policy_prefs::kLastPolicyStatisticsUpdate, (base::Time::Now() - update_delay_ / 2).ToInternalValue()); policy_statistics_collector_->Initialize(); @@ -164,7 +165,7 @@ TEST_F(PolicyStatisticsCollectorTest, MultiplePolicies) { SetPolicy(kTestPolicy1); SetPolicy(kTestPolicy2); - prefs_.SetInt64(prefs::kLastPolicyStatisticsUpdate, + prefs_.SetInt64(policy_prefs::kLastPolicyStatisticsUpdate, (base::Time::Now() - update_delay_).ToInternalValue()); EXPECT_CALL(*policy_statistics_collector_.get(), diff --git a/chrome/browser/policy/profile_policy_connector.cc b/chrome/browser/policy/profile_policy_connector.cc index b2ebe37137..fae3b36af7 100644 --- a/chrome/browser/policy/profile_policy_connector.cc +++ b/chrome/browser/policy/profile_policy_connector.cc @@ -44,9 +44,7 @@ ProfilePolicyConnector::ProfilePolicyConnector(Profile* profile) ProfilePolicyConnector::~ProfilePolicyConnector() {} -void ProfilePolicyConnector::Init( - bool force_immediate_load, - base::SequencedTaskRunner* sequenced_task_runner) { +void ProfilePolicyConnector::Init(bool force_immediate_load) { BrowserPolicyConnector* connector = g_browser_process->browser_policy_connector(); // |providers| contains a list of the policy providers available for the diff --git a/chrome/browser/policy/profile_policy_connector.h b/chrome/browser/policy/profile_policy_connector.h index 26af539163..d7c4c26d9a 100644 --- a/chrome/browser/policy/profile_policy_connector.h +++ b/chrome/browser/policy/profile_policy_connector.h @@ -21,10 +21,6 @@ class Profile; -namespace base { -class SequencedTaskRunner; -} - namespace net { class CertTrustAnchorProvider; } @@ -49,8 +45,7 @@ class ProfilePolicyConnector : public BrowserContextKeyedService { virtual ~ProfilePolicyConnector(); // If |force_immediate_load| then disk caches will be loaded synchronously. - void Init(bool force_immediate_load, - base::SequencedTaskRunner* sequenced_task_runner); + void Init(bool force_immediate_load); void InitForTesting(scoped_ptr<PolicyService> service); diff --git a/chrome/browser/policy/profile_policy_connector_factory.cc b/chrome/browser/policy/profile_policy_connector_factory.cc index 9d052606f4..e8b7bc3505 100644 --- a/chrome/browser/policy/profile_policy_connector_factory.cc +++ b/chrome/browser/policy/profile_policy_connector_factory.cc @@ -37,10 +37,8 @@ ProfilePolicyConnector* ProfilePolicyConnectorFactory::GetForProfile( scoped_ptr<ProfilePolicyConnector> ProfilePolicyConnectorFactory::CreateForProfile( Profile* profile, - bool force_immediate_load, - base::SequencedTaskRunner* sequenced_task_runner) { - return GetInstance()->CreateForProfileInternal( - profile, force_immediate_load, sequenced_task_runner); + bool force_immediate_load) { + return GetInstance()->CreateForProfileInternal(profile, force_immediate_load); } void ProfilePolicyConnectorFactory::SetServiceForTesting( @@ -81,11 +79,10 @@ ProfilePolicyConnector* scoped_ptr<ProfilePolicyConnector> ProfilePolicyConnectorFactory::CreateForProfileInternal( Profile* profile, - bool force_immediate_load, - base::SequencedTaskRunner* sequenced_task_runner) { + bool force_immediate_load) { DCHECK(connectors_.find(profile) == connectors_.end()); ProfilePolicyConnector* connector = new ProfilePolicyConnector(profile); - connector->Init(force_immediate_load, sequenced_task_runner); + connector->Init(force_immediate_load); connectors_[profile] = connector; return scoped_ptr<ProfilePolicyConnector>(connector); } diff --git a/chrome/browser/policy/profile_policy_connector_factory.h b/chrome/browser/policy/profile_policy_connector_factory.h index a44fdeef03..837140833a 100644 --- a/chrome/browser/policy/profile_policy_connector_factory.h +++ b/chrome/browser/policy/profile_policy_connector_factory.h @@ -46,8 +46,7 @@ class ProfilePolicyConnectorFactory : public BrowserContextKeyedBaseFactory { // startup. static scoped_ptr<ProfilePolicyConnector> CreateForProfile( Profile* profile, - bool force_immediate_load, - base::SequencedTaskRunner* sequenced_task_runner); + bool force_immediate_load); // Overrides the |connector| for the given |profile|; use only in tests. // Once this class becomes a proper PKS then it can reuse the testing @@ -65,8 +64,7 @@ class ProfilePolicyConnectorFactory : public BrowserContextKeyedBaseFactory { scoped_ptr<ProfilePolicyConnector> CreateForProfileInternal( Profile* profile, - bool force_immediate_load, - base::SequencedTaskRunner* sequenced_task_runner); + bool force_immediate_load); // BrowserContextKeyedBaseFactory: virtual void BrowserContextShutdown( diff --git a/chrome/browser/policy/profile_policy_connector_stub.cc b/chrome/browser/policy/profile_policy_connector_stub.cc index 6c039426c1..49747c8df7 100644 --- a/chrome/browser/policy/profile_policy_connector_stub.cc +++ b/chrome/browser/policy/profile_policy_connector_stub.cc @@ -12,9 +12,7 @@ ProfilePolicyConnector::ProfilePolicyConnector(Profile* profile) {} ProfilePolicyConnector::~ProfilePolicyConnector() {} -void ProfilePolicyConnector::Init( - bool force_immediate_load, - base::SequencedTaskRunner* sequenced_task_runner) { +void ProfilePolicyConnector::Init(bool force_immediate_load) { policy_service_.reset(new PolicyServiceStub()); } diff --git a/chrome/browser/policy/proto/chromeos/chrome_device_policy.proto b/chrome/browser/policy/proto/chromeos/chrome_device_policy.proto index a7fc4fc562..3623b8f199 100644 --- a/chrome/browser/policy/proto/chromeos/chrome_device_policy.proto +++ b/chrome/browser/policy/proto/chromeos/chrome_device_policy.proto @@ -362,7 +362,7 @@ message AttestationSettingsProto { // this is enabled a machine key will be generated and certified by the Chrome // OS CA. If this setting is disabled, even users with attestation settings // enabled will not be able to use those features on the device. - optional bool attestation_enabled = 1; + optional bool attestation_enabled = 1 [default = false]; // Chrome OS devices can use remote attestation (Verified Access) to get a // certificate issued by the Chrome OS CA that asserts the device is eligible @@ -370,7 +370,7 @@ message AttestationSettingsProto { // endorsement information to the Chrome OS CA which uniquely identifies the // device. This setting allows this feature to be disabled for the device // regardless of any user-specific settings. - optional bool content_protection_enabled = 2; + optional bool content_protection_enabled = 2 [default = true]; } message AccessibilitySettingsProto { diff --git a/chrome/browser/policy/url_blacklist_manager.cc b/chrome/browser/policy/url_blacklist_manager.cc index 062ed5a210..e4f6db4568 100644 --- a/chrome/browser/policy/url_blacklist_manager.cc +++ b/chrome/browser/policy/url_blacklist_manager.cc @@ -44,10 +44,10 @@ namespace { // Maximum filters per policy. Filters over this index are ignored. const size_t kMaxFiltersPerPolicy = 1000; -const char kServiceLoginAuth[] = "/ServiceLoginAuth"; - #if !defined(OS_CHROMEOS) +const char kServiceLoginAuth[] = "/ServiceLoginAuth"; + bool IsSigninFlowURL(const GURL& url) { // Whitelist all the signin flow URLs flagged by the SigninManager. if (SigninManager::IsWebBasedSigninFlowURL(url)) diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index 3d5a1c43d0..69b2c86849 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc @@ -486,7 +486,6 @@ void MigrateUserPrefs(Profile* profile) { prefs->ClearPref(kSyncPromoErrorMessage); #endif - PrefsTabHelper::MigrateUserPrefs(prefs); PromoResourceService::MigrateUserPrefs(prefs); TranslatePrefs::MigrateUserPrefs(prefs); diff --git a/chrome/browser/prefs/command_line_pref_store.cc b/chrome/browser/prefs/command_line_pref_store.cc index f6006835e2..7a123da4a1 100644 --- a/chrome/browser/prefs/command_line_pref_store.cc +++ b/chrome/browser/prefs/command_line_pref_store.cc @@ -70,8 +70,6 @@ const CommandLinePrefStore::BooleanSwitchToPreferenceMapEntry { chromeos::switches::kEnableTouchpadThreeFingerClick, prefs::kEnableTouchpadThreeFingerClick, true }, #endif - { switches::kDisableCloudPolicyOnSignin, - prefs::kDisableCloudPolicyOnSignin, true }, { switches::kDisableAsyncDns, prefs::kBuiltInDnsClientEnabled, false }, { switches::kEnableAsyncDns, prefs::kBuiltInDnsClientEnabled, true }, }; diff --git a/chrome/browser/prefs/pref_metrics_service.cc b/chrome/browser/prefs/pref_metrics_service.cc index cf56bc40db..18153bfb1b 100644 --- a/chrome/browser/prefs/pref_metrics_service.cc +++ b/chrome/browser/prefs/pref_metrics_service.cc @@ -37,12 +37,14 @@ namespace { const int kSessionStartupPrefValueMax = SessionStartupPref::kPrefValueMax; +#if defined(OS_ANDROID) // An unregistered preference to fill in indices in kTrackedPrefs below for // preferences that aren't defined on every platform. This is fine as the code // below (e.g. CheckTrackedPreferences()) skips unregistered preferences and // should thus never report any data about that index on the platforms where // that preference is unimplemented. const char kUnregisteredPreference[] = "_"; +#endif // These preferences must be kept in sync with the TrackedPreference enum in // tools/metrics/histograms/histograms.xml. To add a new preference, append it diff --git a/chrome/browser/prefs/pref_model_associator.cc b/chrome/browser/prefs/pref_model_associator.cc index 28759a10b6..42cbae2485 100644 --- a/chrome/browser/prefs/pref_model_associator.cc +++ b/chrome/browser/prefs/pref_model_associator.cc @@ -48,6 +48,34 @@ sync_pb::PreferenceSpecifics* GetMutableSpecifics( } } +// List of migrated preference name pairs. If a preference is migrated +// (meaning renamed) adding the old and new preference names here will ensure +// that the sync engine knows how to deal with the synced values coming in +// with the old name. Preference migration itself doesn't happen here. It may +// happen in session_startup_pref.cc. +const struct MigratedPreferences { + const char* const old_name; + const char* const new_name; +} kMigratedPreferences[] = { + { prefs::kURLsToRestoreOnStartupOld, prefs::kURLsToRestoreOnStartup }, +}; + +std::string GetOldMigratedPreferenceName(const char* preference_name) { + for (size_t i = 0; i < arraysize(kMigratedPreferences); ++i) { + if (!strcmp(kMigratedPreferences[i].new_name, preference_name)) + return kMigratedPreferences[i].old_name; + } + return std::string(); +} + +std::string GetNewMigratedPreferenceName(const char* old_preference_name) { + for (size_t i = 0; i < arraysize(kMigratedPreferences); ++i) { + if (!strcmp(kMigratedPreferences[i].old_name, old_preference_name)) + return kMigratedPreferences[i].new_name; + } + return std::string(); +} + } // namespace PrefModelAssociator::PrefModelAssociator(syncer::ModelType type) @@ -71,15 +99,21 @@ PrefModelAssociator::~PrefModelAssociator() { void PrefModelAssociator::InitPrefAndAssociate( const syncer::SyncData& sync_pref, const std::string& pref_name, - syncer::SyncChangeList* sync_changes) { + syncer::SyncChangeList* sync_changes, + SyncDataMap* migrated_preference_list) { const Value* user_pref_value = pref_service_->GetUserPrefValue( pref_name.c_str()); VLOG(1) << "Associating preference " << pref_name; + // Set if a migrated pref name has been added to the synced_preferences_ list. + bool remembered_migrated_synced_preference = false; + if (sync_pref.IsValid()) { const sync_pb::PreferenceSpecifics& preference = GetSpecifics(sync_pref); - DCHECK_EQ(pref_name, preference.name()); - + DCHECK(pref_name == preference.name() || + (IsMigratedPreference(pref_name.c_str()) && + preference.name() == + GetOldMigratedPreferenceName(pref_name.c_str()))); base::JSONReader reader; scoped_ptr<Value> sync_value(reader.ReadToValue(preference.value())); if (!sync_value.get()) { @@ -115,10 +149,49 @@ void PrefModelAssociator::InitPrefAndAssociate( LOG(ERROR) << "Failed to update preference."; return; } - sync_changes->push_back( + + if (IsMigratedPreference(pref_name.c_str())) { + // This preference has been migrated from an old version that must be + // kept in sync on older versions of Chrome. + std::string old_pref_name = + GetOldMigratedPreferenceName(pref_name.c_str()); + + if (preference.name() == old_pref_name) { + DCHECK(migrated_preference_list); + // If the name the syncer has is the old pre-migration value, then + // it's possible the new migrated preference name hasn't been synced + // yet. In that case the SyncChange should be an ACTION_ADD rather + // than an ACTION_UPDATE. Defer the decision of whether to sync with + // ACTION_ADD or ACTION_UPDATE until the migrated_preferences phase. + if (migrated_preference_list) + (*migrated_preference_list)[pref_name] = sync_data; + } else { + DCHECK_EQ(preference.name(), pref_name); + sync_changes->push_back( + syncer::SyncChange(FROM_HERE, + syncer::SyncChange::ACTION_UPDATE, + sync_data)); + } + + syncer::SyncData old_sync_data; + if (!CreatePrefSyncData(old_pref_name, *new_value, &old_sync_data)) { + LOG(ERROR) << "Failed to update preference."; + return; + } + if (migrated_preference_list) + (*migrated_preference_list)[old_pref_name] = old_sync_data; + + // Keep track of the name of the synced pref. This will be idempotent + // with the insertion of pref_name below when the migrated value has + // already been synced, not so when it has not. + synced_preferences_.insert(preference.name()); + remembered_migrated_synced_preference = true; + } else { + sync_changes->push_back( syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE, sync_data)); + } } } else if (!sync_value->IsType(Value::TYPE_NULL)) { // Only a server value exists. Just set the local user value. @@ -148,7 +221,8 @@ void PrefModelAssociator::InitPrefAndAssociate( // Make sure we add it to our list of synced preferences so we know what // the server is aware of. - synced_preferences_.insert(pref_name); + if (!remembered_migrated_synced_preference) + synced_preferences_.insert(pref_name); return; } @@ -170,6 +244,11 @@ syncer::SyncMergeResult PrefModelAssociator::MergeDataAndStartSyncing( syncer::SyncChangeList new_changes; std::set<std::string> remaining_preferences = registered_preferences_; + // Maintains a list of old migrated preference names that we wish to sync. + // Keep track of these in a list such that when the preference iteration + // loops below are complete we can go back and determine whether + SyncDataMap migrated_preference_list; + // Go through and check for all preferences we care about that sync already // knows about. for (syncer::SyncDataList::const_iterator sync_iter = @@ -179,19 +258,28 @@ syncer::SyncMergeResult PrefModelAssociator::MergeDataAndStartSyncing( DCHECK_EQ(type_, sync_iter->GetDataType()); const sync_pb::PreferenceSpecifics& preference = GetSpecifics(*sync_iter); - const std::string& sync_pref_name = preference.name(); + std::string sync_pref_name = preference.name(); if (remaining_preferences.count(sync_pref_name) == 0) { - // We're not syncing this preference locally, ignore the sync data. - // TODO(zea): Eventually we want to be able to have the syncable service - // reconstruct all sync data for it's datatype (therefore having - // GetAllSyncData be a complete representation). We should store this data - // somewhere, even if we don't use it. - continue; + if (IsOldMigratedPreference(sync_pref_name.c_str())) { + // This old pref name is not syncable locally anymore but we accept + // changes from other Chrome installs of previous versions and migrate + // them to the new name. Note that we will be merging any differences + // between the new and old values and sync'ing them back. + sync_pref_name = GetNewMigratedPreferenceName(sync_pref_name.c_str()); + } else { + // We're not syncing this preference locally, ignore the sync data. + // TODO(zea): Eventually we want to be able to have the syncable service + // reconstruct all sync data for its datatype (therefore having + // GetAllSyncData be a complete representation). We should store this + // data somewhere, even if we don't use it. + continue; + } + } else { + remaining_preferences.erase(sync_pref_name); } - - remaining_preferences.erase(sync_pref_name); - InitPrefAndAssociate(*sync_iter, sync_pref_name, &new_changes); + InitPrefAndAssociate(*sync_iter, sync_pref_name, &new_changes, + &migrated_preference_list); } // Go through and build sync data for any remaining preferences. @@ -199,7 +287,21 @@ syncer::SyncMergeResult PrefModelAssociator::MergeDataAndStartSyncing( remaining_preferences.begin(); pref_name_iter != remaining_preferences.end(); ++pref_name_iter) { - InitPrefAndAssociate(syncer::SyncData(), *pref_name_iter, &new_changes); + InitPrefAndAssociate(syncer::SyncData(), *pref_name_iter, &new_changes, + &migrated_preference_list); + } + + // Now go over any migrated preference names and build sync data for them too. + for (SyncDataMap::const_iterator migrated_pref_iter = + migrated_preference_list.begin(); + migrated_pref_iter != migrated_preference_list.end(); + ++migrated_pref_iter) { + syncer::SyncChange::SyncChangeType change_type = + (synced_preferences_.count(migrated_pref_iter->first) == 0) ? + syncer::SyncChange::ACTION_ADD : + syncer::SyncChange::ACTION_UPDATE; + new_changes.push_back( + syncer::SyncChange(FROM_HERE, change_type, migrated_pref_iter->second)); } // Push updates to sync. @@ -225,7 +327,10 @@ scoped_ptr<Value> PrefModelAssociator::MergePreference( const std::string& name, const Value& local_value, const Value& server_value) { - if (name == prefs::kURLsToRestoreOnStartup) { + // This function special cases preferences individually, so don't attempt + // to merge for all migrated values. + if (name == prefs::kURLsToRestoreOnStartup || + name == prefs::kURLsToRestoreOnStartupOld) { return scoped_ptr<Value>(MergeListValues(local_value, server_value)).Pass(); } @@ -321,6 +426,17 @@ Value* PrefModelAssociator::MergeDictionaryValues( return result; } +// static +bool PrefModelAssociator::IsMigratedPreference(const char* preference_name) { + return !GetOldMigratedPreferenceName(preference_name).empty(); +} + +// static +bool PrefModelAssociator::IsOldMigratedPreference( + const char* old_preference_name) { + return !GetNewMigratedPreferenceName(old_preference_name).empty(); +} + // Note: This will build a model of all preferences registered as syncable // with user controlled data. We do not track any information for preferences // not registered locally as syncable and do not inform the syncer of @@ -388,6 +504,13 @@ syncer::SyncError PrefModelAssociator::ProcessSyncChanges( // Windows client, the Windows client does not support // kConfirmToQuitEnabled. Ignore updates from these preferences. const char* pref_name = name.c_str(); + std::string new_name; + // We migrated this preference name, so do as if the name had not changed. + if (IsOldMigratedPreference(pref_name)) { + new_name = GetNewMigratedPreferenceName(pref_name); + pref_name = new_name.c_str(); + } + if (!IsPrefRegistered(pref_name)) continue; @@ -496,7 +619,7 @@ void PrefModelAssociator::ProcessPrefChange(const std::string& name) { // Not in synced_preferences_ means no synced data. InitPrefAndAssociate(..) // will determine if we care about its data (e.g. if it has a default value // and hasn't been changed yet we don't) and take care syncing any new data. - InitPrefAndAssociate(syncer::SyncData(), name, &changes); + InitPrefAndAssociate(syncer::SyncData(), name, &changes, NULL); } else { // We are already syncing this preference, just update it's sync node. syncer::SyncData sync_data; @@ -508,6 +631,24 @@ void PrefModelAssociator::ProcessPrefChange(const std::string& name) { syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE, sync_data)); + // This preference has been migrated from an old version that must be kept + // in sync on older versions of Chrome. + if (IsMigratedPreference(name.c_str())) { + std::string old_pref_name = GetOldMigratedPreferenceName(name.c_str()); + if (!CreatePrefSyncData(old_pref_name, + *preference->GetValue(), + &sync_data)) { + LOG(ERROR) << "Failed to update preference."; + return; + } + + syncer::SyncChange::SyncChangeType change_type = + (synced_preferences_.count(old_pref_name) == 0) ? + syncer::SyncChange::ACTION_ADD : + syncer::SyncChange::ACTION_UPDATE; + changes.push_back( + syncer::SyncChange(FROM_HERE, change_type, sync_data)); + } } syncer::SyncError error = diff --git a/chrome/browser/prefs/pref_model_associator.h b/chrome/browser/prefs/pref_model_associator.h index 539d38a301..0c0cb66e91 100644 --- a/chrome/browser/prefs/pref_model_associator.h +++ b/chrome/browser/prefs/pref_model_associator.h @@ -12,6 +12,7 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/containers/hash_tables.h" +#include "base/gtest_prod_util.h" #include "base/observer_list.h" #include "base/threading/non_thread_safe.h" #include "chrome/browser/prefs/synced_pref_observer.h" @@ -110,6 +111,12 @@ class PrefModelAssociator protected: friend class ProfileSyncServicePreferenceTest; + FRIEND_TEST_ALL_PREFIXES(ProfileSyncServicePreferenceTest, + ModelAssociationCloudHasOldMigratedData); + FRIEND_TEST_ALL_PREFIXES(ProfileSyncServicePreferenceTest, + ModelAssociationCloudHasNewMigratedData); + FRIEND_TEST_ALL_PREFIXES(ProfileSyncServicePreferenceTest, + ModelAssociationCloudAddsOldAndNewMigratedData); typedef std::map<std::string, syncer::SyncData> SyncDataMap; @@ -118,18 +125,26 @@ class PrefModelAssociator // with ours and append a new UPDATE SyncChange to |sync_changes|. If // sync_pref is not set, we append an ADD SyncChange to |sync_changes| with // the current preference data. + // |migrated_preference_list| points to a vector that may be updated with a + // string containing the old name of the preference described by |pref_name|. // Note: We do not modify the sync data for preferences that are either // controlled by policy (are not user modifiable) or have their default value // (are not user controlled). void InitPrefAndAssociate(const syncer::SyncData& sync_pref, const std::string& pref_name, - syncer::SyncChangeList* sync_changes); + syncer::SyncChangeList* sync_changes, + SyncDataMap* migrated_preference_list); static base::Value* MergeListValues( const base::Value& from_value, const base::Value& to_value); static base::Value* MergeDictionaryValues(const base::Value& from_value, const base::Value& to_value); + // Returns whether a given preference name is a new name of a migrated + // preference. Exposed here for testing. + static bool IsMigratedPreference(const char* preference_name); + static bool IsOldMigratedPreference(const char* old_preference_name); + // Do we have an active association between the preferences and sync models? // Set when start syncing, reset in StopSyncing. While this is not set, we // ignore any local preference changes (when we start syncing we will look diff --git a/chrome/browser/prefs/session_startup_pref.cc b/chrome/browser/prefs/session_startup_pref.cc index 478790a157..3ce2fa67b4 100644 --- a/chrome/browser/prefs/session_startup_pref.cc +++ b/chrome/browser/prefs/session_startup_pref.cc @@ -6,7 +6,9 @@ #include <string> +#include "base/metrics/histogram.h" #include "base/prefs/pref_service.h" +#include "base/time/time.h" #include "base/values.h" #include "base/version.h" #include "chrome/browser/prefs/scoped_user_pref_update.h" @@ -21,6 +23,13 @@ namespace { +enum StartupURLsMigrationMetrics { + STARTUP_URLS_MIGRATION_METRICS_PERFORMED, + STARTUP_URLS_MIGRATION_METRICS_NOT_PRESENT, + STARTUP_URLS_MIGRATION_METRICS_RESET, + STARTUP_URLS_MIGRATION_METRICS_MAX, +}; + // Converts a SessionStartupPref::Type to an integer written to prefs. int TypeToPrefValue(SessionStartupPref::Type type) { switch (type) { @@ -62,10 +71,16 @@ void SessionStartupPref::RegisterProfilePrefs( user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); registry->RegisterListPref(prefs::kURLsToRestoreOnStartup, user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); + registry->RegisterListPref(prefs::kURLsToRestoreOnStartupOld, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); registry->RegisterBooleanPref( prefs::kRestoreOnStartupMigrated, false, user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterInt64Pref( + prefs::kRestoreStartupURLsMigrationTime, + false, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); } // static @@ -136,8 +151,56 @@ SessionStartupPref SessionStartupPref::GetStartupPref(PrefService* prefs) { void SessionStartupPref::MigrateIfNecessary(PrefService* prefs) { DCHECK(prefs); + // Check if we need to migrate the old version of the startup URLs preference + // to the new name, and also send metrics about the migration. + StartupURLsMigrationMetrics metrics_result = + STARTUP_URLS_MIGRATION_METRICS_MAX; + const base::ListValue* old_startup_urls = + prefs->GetList(prefs::kURLsToRestoreOnStartupOld); + if (!prefs->GetUserPrefValue(prefs::kRestoreStartupURLsMigrationTime)) { + // Record the absence of the migration timestamp, this will get overwritten + // below if migration occurs now. + metrics_result = STARTUP_URLS_MIGRATION_METRICS_NOT_PRESENT; + + // Seems like we never migrated, do it if necessary. + if (!prefs->GetUserPrefValue(prefs::kURLsToRestoreOnStartup)) { + if (old_startup_urls && !old_startup_urls->empty()) { + prefs->Set(prefs::kURLsToRestoreOnStartup, *old_startup_urls); + prefs->ClearPref(prefs::kURLsToRestoreOnStartupOld); + } + metrics_result = STARTUP_URLS_MIGRATION_METRICS_PERFORMED; + } + + prefs->SetInt64(prefs::kRestoreStartupURLsMigrationTime, + base::Time::Now().ToInternalValue()); + } else if (old_startup_urls && !old_startup_urls->empty()) { + // Migration needs to be reset. + prefs->ClearPref(prefs::kURLsToRestoreOnStartupOld); + base::Time last_migration_time = base::Time::FromInternalValue( + prefs->GetInt64(prefs::kRestoreStartupURLsMigrationTime)); + base::Time now = base::Time::Now(); + prefs->SetInt64(prefs::kRestoreStartupURLsMigrationTime, + now.ToInternalValue()); + if (now < last_migration_time) + last_migration_time = now; + HISTOGRAM_CUSTOM_TIMES("Settings.StartupURLsResetTime", + now - last_migration_time, + base::TimeDelta::FromDays(0), + base::TimeDelta::FromDays(7), + 50); + metrics_result = STARTUP_URLS_MIGRATION_METRICS_RESET; + } + + // Record a metric migration event if something interesting happened. + if (metrics_result != STARTUP_URLS_MIGRATION_METRICS_MAX) { + UMA_HISTOGRAM_ENUMERATION( + "Settings.StartupURLsMigration", + metrics_result, + STARTUP_URLS_MIGRATION_METRICS_MAX); + } + if (!prefs->GetBoolean(prefs::kRestoreOnStartupMigrated)) { - // Read existing values + // Read existing values. const base::Value* homepage_is_new_tab_page_value = prefs->GetUserPrefValue(prefs::kHomePageIsNewTabPage); bool homepage_is_new_tab_page = true; diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc index e782149cf0..cf25d05843 100644 --- a/chrome/browser/prerender/prerender_browsertest.cc +++ b/chrome/browser/prerender/prerender_browsertest.cc @@ -8,6 +8,7 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/files/file_path.h" +#include "base/memory/ref_counted_memory.h" #include "base/path_service.h" #include "base/prefs/pref_service.h" #include "base/run_loop.h" @@ -39,6 +40,7 @@ #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/browser_finder.h" +#include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/chrome_paths.h" @@ -822,7 +824,38 @@ class PrerenderBrowserTest : virtual public InProcessBrowserTest { void NavigateToDestURLWithDisposition( WindowOpenDisposition disposition, bool expect_swap_to_succeed) const { - NavigateToURLImpl(dest_url_, disposition, expect_swap_to_succeed); + NavigateToURLWithParams( + content::OpenURLParams(dest_url_, Referrer(), disposition, + content::PAGE_TRANSITION_TYPED, false), + expect_swap_to_succeed); + } + + void NavigateToDestUrlAndWaitForPassTitle() { + string16 expected_title = ASCIIToUTF16(kPassTitle); + content::TitleWatcher title_watcher( + GetPrerenderContents()->prerender_contents(), + expected_title); + NavigateToDestURL(); + EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle()); + } + + void NavigateToURL(const std::string& dest_html_file) const { + NavigateToURLWithDisposition(dest_html_file, CURRENT_TAB, true); + } + + void NavigateToURLWithDisposition(const std::string& dest_html_file, + WindowOpenDisposition disposition, + bool expect_swap_to_succeed) const { + GURL dest_url = test_server()->GetURL(dest_html_file); + NavigateToURLWithParams( + content::OpenURLParams(dest_url, Referrer(), disposition, + content::PAGE_TRANSITION_TYPED, false), + expect_swap_to_succeed); + } + + void NavigateToURLWithParams(const content::OpenURLParams& params, + bool expect_swap_to_succeed) const { + NavigateToURLImpl(params, expect_swap_to_succeed); } void OpenDestURLViaClick() const { @@ -885,15 +918,6 @@ class PrerenderBrowserTest : virtual public InProcessBrowserTest { test_server()->GetURL("files/prerender/prerender_page.html")); } - void NavigateToDestUrlAndWaitForPassTitle() { - string16 expected_title = ASCIIToUTF16(kPassTitle); - content::TitleWatcher title_watcher( - GetPrerenderContents()->prerender_contents(), - expected_title); - NavigateToDestURL(); - EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle()); - } - // Called after the prerendered page has been navigated to and then away from. // Navigates back through the history to the prerendered page. void GoBackToPrerender() { @@ -931,17 +955,6 @@ class PrerenderBrowserTest : virtual public InProcessBrowserTest { EXPECT_TRUE(js_result); } - void NavigateToURL(const std::string& dest_html_file) const { - NavigateToURLWithDisposition(dest_html_file, CURRENT_TAB, true); - } - - void NavigateToURLWithDisposition(const std::string& dest_html_file, - WindowOpenDisposition disposition, - bool expect_swap_to_succeed) const { - GURL dest_url = test_server()->GetURL(dest_html_file); - NavigateToURLImpl(dest_url, disposition, expect_swap_to_succeed); - } - bool UrlIsInPrerenderManager(const std::string& html_file) const { return UrlIsInPrerenderManager(test_server()->GetURL(html_file)); } @@ -1098,6 +1111,10 @@ class PrerenderBrowserTest : virtual public InProcessBrowserTest { return explicitly_set_browser_ ? explicitly_set_browser_ : browser(); } + const GURL& dest_url() const { + return dest_url_; + } + void IncreasePrerenderMemory() { // Increase the memory allowed in a prerendered page above normal settings. // Debug build bots occasionally run against the default limit, and tests @@ -1229,8 +1246,7 @@ class PrerenderBrowserTest : virtual public InProcessBrowserTest { EXPECT_FALSE(HadPrerenderEventErrors()); } - void NavigateToURLImpl(const GURL& dest_url, - WindowOpenDisposition disposition, + void NavigateToURLImpl(const content::OpenURLParams& params, bool expect_swap_to_succeed) const { ASSERT_NE(static_cast<PrerenderManager*>(NULL), GetPrerenderManager()); // Make sure in navigating we have a URL to use in the PrerenderManager. @@ -1238,7 +1254,7 @@ class PrerenderBrowserTest : virtual public InProcessBrowserTest { // If opening the page in a background tab, it won't be shown when swapped // in. - if (disposition == NEW_BACKGROUND_TAB) + if (params.disposition == NEW_BACKGROUND_TAB) GetPrerenderContents()->set_should_be_shown(false); scoped_ptr<content::WindowedNotificationObserver> page_load_observer; @@ -1260,20 +1276,21 @@ class PrerenderBrowserTest : virtual public InProcessBrowserTest { // Navigate to the prerendered URL, but don't run the message loop. Browser // issued navigations to prerendered pages will synchronously swap in the // prerendered page. - ui_test_utils::NavigateToURLWithDisposition( - current_browser(), dest_url, disposition, - ui_test_utils::BROWSER_TEST_NONE); - - if (call_javascript_ && web_contents && expect_swap_to_succeed) { - if (page_load_observer.get()) - page_load_observer->Wait(); - - bool display_test_result = false; - ASSERT_TRUE(content::ExecuteScriptAndExtractBool( - web_contents, - "window.domAutomationController.send(DidDisplayPass())", - &display_test_result)); - EXPECT_TRUE(display_test_result); + WebContents* target_web_contents = current_browser()->OpenURL(params); + + if (web_contents && expect_swap_to_succeed) { + EXPECT_EQ(web_contents, target_web_contents); + if (call_javascript_) { + if (page_load_observer.get()) + page_load_observer->Wait(); + + bool display_test_result = false; + ASSERT_TRUE(content::ExecuteScriptAndExtractBool( + web_contents, + "window.domAutomationController.send(DidDisplayPass())", + &display_test_result)); + EXPECT_TRUE(display_test_result); + } } } @@ -2749,7 +2766,7 @@ IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, manager->RegisterDevToolsClientHostFor(agent.get(), &client_host); const char* url = "files/prerender/prerender_page.html"; PrerenderTestURL(url, FINAL_STATUS_DEVTOOLS_ATTACHED, 1); - NavigateToURL(url); + NavigateToURLWithDisposition(url, CURRENT_TAB, false); manager->ClientHostClosing(&client_host); } @@ -3195,4 +3212,31 @@ IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderDeferredSynchronousXHR) { NavigateToDestURL(); } +// Checks that prerenders are not swapped for navigations with extra headers. +IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderExtraHeadersNoSwap) { + PrerenderTestURL("files/prerender/prerender_page.html", + FINAL_STATUS_APP_TERMINATING, 1); + + content::OpenURLParams params(dest_url(), Referrer(), CURRENT_TAB, + content::PAGE_TRANSITION_TYPED, false); + params.extra_headers = "X-Custom-Header: 42\r\n"; + NavigateToURLWithParams(params, false); +} + +// Checks that prerenders are not swapped for navigations with browser-initiated +// POST data. +IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, + PrerenderBrowserInitiatedPostNoSwap) { + PrerenderTestURL("files/prerender/prerender_page.html", + FINAL_STATUS_APP_TERMINATING, 1); + + std::string post_data = "DATA"; + content::OpenURLParams params(dest_url(), Referrer(), CURRENT_TAB, + content::PAGE_TRANSITION_TYPED, false); + params.uses_post = true; + params.browser_initiated_post_data = + base::RefCountedString::TakeString(&post_data); + NavigateToURLWithParams(params, false); +} + } // namespace prerender diff --git a/chrome/browser/prerender/prerender_contents.cc b/chrome/browser/prerender/prerender_contents.cc index 13378955f9..1d4d672e6f 100644 --- a/chrome/browser/prerender/prerender_contents.cc +++ b/chrome/browser/prerender/prerender_contents.cc @@ -17,12 +17,12 @@ #include "chrome/browser/prerender/prerender_final_status.h" #include "chrome/browser/prerender/prerender_handle.h" #include "chrome/browser/prerender/prerender_manager.h" -#include "chrome/browser/prerender/prerender_render_view_host_observer.h" #include "chrome/browser/prerender/prerender_tracker.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_tab_contents.h" #include "chrome/common/prerender_messages.h" +#include "chrome/common/render_messages.h" #include "chrome/common/url_constants.h" #include "content/public/browser/browser_child_process_host.h" #include "content/public/browser/notification_service.h" @@ -277,10 +277,6 @@ void PrerenderContents::StartPrerendering( // Set the size of the prerender WebContents. prerender_contents_->GetView()->SizeContents(size_); - // Register as an observer of the RenderViewHost so we get messages. - render_view_host_observer_.reset( - new PrerenderRenderViewHostObserver(this, GetRenderViewHostMutable())); - child_id_ = GetRenderViewHost()->GetProcess()->GetID(); route_id_ = GetRenderViewHost()->GetRoutingID(); @@ -508,6 +504,18 @@ void PrerenderContents::DidUpdateFaviconURL( } } +bool PrerenderContents::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + // The following messages we do want to consume. + IPC_BEGIN_MESSAGE_MAP(PrerenderContents, message) + IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CancelPrerenderForPrinting, + OnCancelPrerenderForPrinting) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + + return handled; +} + bool PrerenderContents::AddAliasURL(const GURL& url) { const bool http = url.SchemeIs(content::kHttpScheme); const bool https = url.SchemeIs(content::kHttpsScheme); @@ -629,12 +637,6 @@ void PrerenderContents::Destroy(FinalStatus final_status) { match_complete_status() == MATCH_COMPLETE_REPLACEMENT)) { NotifyPrerenderStop(); } - - // We may destroy the PrerenderContents before we have initialized the - // RenderViewHost. Otherwise set the Observer's PrerenderContents to NULL to - // avoid any more messages being sent. - if (render_view_host_observer_) - render_view_host_observer_->set_prerender_contents(NULL); } base::ProcessMetrics* PrerenderContents::MaybeGetProcessMetrics() { @@ -671,7 +673,6 @@ void PrerenderContents::DestroyWhenUsingTooManyResources() { WebContents* PrerenderContents::ReleasePrerenderContents() { prerender_contents_->SetDelegate(NULL); - render_view_host_observer_.reset(); content::WebContentsObserver::Observe(NULL); SessionStorageNamespace* session_storage_namespace = GetSessionStorageNamespace(); @@ -728,5 +729,8 @@ SessionStorageNamespace* PrerenderContents::GetSessionStorageNamespace() const { GetDefaultSessionStorageNamespace(); } +void PrerenderContents::OnCancelPrerenderForPrinting() { + Destroy(FINAL_STATUS_WINDOW_PRINT); +} } // namespace prerender diff --git a/chrome/browser/prerender/prerender_contents.h b/chrome/browser/prerender/prerender_contents.h index ec0adbd1ff..8669cd90e5 100644 --- a/chrome/browser/prerender/prerender_contents.h +++ b/chrome/browser/prerender/prerender_contents.h @@ -44,7 +44,6 @@ namespace prerender { class PrerenderHandle; class PrerenderManager; -class PrerenderRenderViewHostObserver; class PrerenderContents : public content::NotificationObserver, public content::WebContentsObserver { @@ -231,6 +230,7 @@ class PrerenderContents : public content::NotificationObserver, content::RenderViewHost* render_view_host) OVERRIDE; virtual void DidUpdateFaviconURL(int32 page_id, const std::vector<content::FaviconURL>& urls) OVERRIDE; + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE; @@ -330,11 +330,12 @@ class PrerenderContents : public content::NotificationObserver, // Needs to be able to call the constructor. friend class PrerenderContentsFactoryImpl; - friend class PrerenderRenderViewHostObserver; - // Returns the ProcessMetrics for the render process, if it exists. base::ProcessMetrics* MaybeGetProcessMetrics(); + // Message handlers. + void OnCancelPrerenderForPrinting(); + ObserverList<Observer> observer_list_; // The prerender manager owning this object. @@ -395,8 +396,6 @@ class PrerenderContents : public content::NotificationObserver, // The prerendered WebContents; may be null. scoped_ptr<content::WebContents> prerender_contents_; - scoped_ptr<PrerenderRenderViewHostObserver> render_view_host_observer_; - scoped_ptr<WebContentsDelegateImpl> web_contents_delegate_; // These are -1 before a RenderView is created. diff --git a/chrome/browser/prerender/prerender_field_trial.cc b/chrome/browser/prerender/prerender_field_trial.cc index ad1f3a0435..3813c5f9e1 100644 --- a/chrome/browser/prerender/prerender_field_trial.cc +++ b/chrome/browser/prerender/prerender_field_trial.cc @@ -326,7 +326,7 @@ bool IsUnencryptedSyncEnabled(Profile* profile) { // Indicates whether the Local Predictor is enabled based on field trial // selection. -bool IsLocalPredictorEnabledBasedOnSelection() { +bool IsLocalPredictorEnabled() { #if defined(OS_ANDROID) || defined(OS_IOS) return false; #endif @@ -337,27 +337,19 @@ bool IsLocalPredictorEnabledBasedOnSelection() { return GetLocalPredictorSpecValue(kLocalPredictorKeyName) == kEnabledGroup; } -// Usually, we enable the Local Predictor based on field trial selection -// (see above), so we can just return that setting. -// However, via Finch, we can specify to not create a LocalPredictor if -// UnencryptedSync is not enabled. Therefore, we have to perform this additional -// check to determine whether or not we actually want to enable the -// LocalPredictor. -bool IsLocalPredictorEnabled(Profile* profile) { - if (GetLocalPredictorSpecValue(kLocalPredictorUnencryptedSyncOnlyKeyName) == +bool DisableLocalPredictorBasedOnSyncAndConfiguration(Profile* profile) { + return + GetLocalPredictorSpecValue(kLocalPredictorUnencryptedSyncOnlyKeyName) == kEnabledGroup && - !IsUnencryptedSyncEnabled(profile)) { - return false; - } - return IsLocalPredictorEnabledBasedOnSelection(); + !IsUnencryptedSyncEnabled(profile); } bool IsLoggedInPredictorEnabled() { - return IsLocalPredictorEnabledBasedOnSelection(); + return IsLocalPredictorEnabled(); } bool IsSideEffectFreeWhitelistEnabled() { - return IsLocalPredictorEnabledBasedOnSelection() && + return IsLocalPredictorEnabled() && GetLocalPredictorSpecValue(kSideEffectFreeWhitelistKeyName) != kDisabledGroup; } diff --git a/chrome/browser/prerender/prerender_field_trial.h b/chrome/browser/prerender/prerender_field_trial.h index d19f067fee..33c4372076 100644 --- a/chrome/browser/prerender/prerender_field_trial.h +++ b/chrome/browser/prerender/prerender_field_trial.h @@ -22,7 +22,11 @@ void ConfigurePrefetchAndPrerender(const CommandLine& command_line); bool IsOmniboxEnabled(Profile* profile); // Returns true iff the Prerender Local Predictor is enabled. -bool IsLocalPredictorEnabled(Profile* profile); +bool IsLocalPredictorEnabled(); + +// Indicates whether to disable the local predictor due to unencrypted sync +// settings and configuration. +bool DisableLocalPredictorBasedOnSyncAndConfiguration(Profile* profile); // Returns true iff the LoggedIn Predictor is enabled. bool IsLoggedInPredictorEnabled(); diff --git a/chrome/browser/prerender/prerender_local_predictor.cc b/chrome/browser/prerender/prerender_local_predictor.cc index cf4ab36924..f86110acbb 100644 --- a/chrome/browser/prerender/prerender_local_predictor.cc +++ b/chrome/browser/prerender/prerender_local_predictor.cc @@ -846,6 +846,8 @@ bool PrerenderLocalPredictor::ApplyParsedPrerenderServiceResponse( in_index == 1, (1 - in_index_timed_out) == 1); } + if (list->GetSize() > 0) + RecordEvent(EVENT_PRERENDER_SERIVCE_RETURNED_HINTING_CANDIDATES); } } @@ -970,6 +972,11 @@ HistoryService* PrerenderLocalPredictor::GetHistoryIfExists() const { void PrerenderLocalPredictor::Init() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); RecordEvent(EVENT_INIT_STARTED); + Profile* profile = prerender_manager_->profile(); + if (!profile || DisableLocalPredictorBasedOnSyncAndConfiguration(profile)) { + RecordEvent(EVENT_INIT_FAILED_UNENCRYPTED_SYNC_NOT_ENABLED); + return; + } HistoryService* history = GetHistoryIfExists(); if (history) { CHECK(!is_visit_database_observer_); @@ -1100,6 +1107,8 @@ void PrerenderLocalPredictor::ContinuePrerenderCheck( RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_EXAMINE_NEXT_URL_SERVICE); } + RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_EXAMINE_NEXT_URL_NOT_SKIPPED); + // We need to check whether we can issue a prerender for this URL. // We test a set of conditions. Each condition can either rule out // a prerender (in which case we reset url_info, so that it will not diff --git a/chrome/browser/prerender/prerender_local_predictor.h b/chrome/browser/prerender/prerender_local_predictor.h index bd722c46bf..720dc96256 100644 --- a/chrome/browser/prerender/prerender_local_predictor.h +++ b/chrome/browser/prerender/prerender_local_predictor.h @@ -129,6 +129,9 @@ class PrerenderLocalPredictor : public history::VisitDatabaseObserver, EVENT_NAMESPACE_MISMATCH_MERGE_RESULT_TOO_MANY_TRANSACTIONS = 79, EVENT_NAMESPACE_MISMATCH_MERGE_RESULT_NOT_MERGEABLE = 80, EVENT_NAMESPACE_MISMATCH_MERGE_RESULT_MERGEABLE = 81, + EVENT_INIT_FAILED_UNENCRYPTED_SYNC_NOT_ENABLED = 82, + EVENT_CONTINUE_PRERENDER_CHECK_EXAMINE_NEXT_URL_NOT_SKIPPED = 83, + EVENT_PRERENDER_SERIVCE_RETURNED_HINTING_CANDIDATES = 84, EVENT_MAX_VALUE }; diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc index 33990322cf..954edb55bb 100644 --- a/chrome/browser/prerender/prerender_manager.cc +++ b/chrome/browser/prerender/prerender_manager.cc @@ -40,6 +40,7 @@ #include "chrome/browser/prerender/prerender_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/tab_contents/tab_util.h" +#include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/tab_contents/core_tab_helper.h" #include "chrome/browser/ui/tab_contents/core_tab_helper_delegate.h" #include "chrome/common/chrome_switches.h" @@ -242,7 +243,7 @@ PrerenderManager::PrerenderManager(Profile* profile, // the same thread that it was created on. DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (IsLocalPredictorEnabled(profile)) + if (IsLocalPredictorEnabled()) local_predictor_.reset(new PrerenderLocalPredictor(this)); if (IsLoggedInPredictorEnabled() && !profile_->IsOffTheRecord()) { @@ -409,11 +410,17 @@ void PrerenderManager::CancelAllPrerenders() { } } -bool PrerenderManager::MaybeUsePrerenderedPage(WebContents* web_contents, - const GURL& url) { +bool PrerenderManager::MaybeUsePrerenderedPage(const GURL& url, + chrome::NavigateParams* params) { DCHECK(CalledOnValidThread()); + + content::WebContents* web_contents = params->target_contents; DCHECK(!IsWebContentsPrerendering(web_contents, NULL)); + // Don't prerender if the navigation involves some special parameters. + if (params->uses_post || !params->extra_headers.empty()) + return false; + DeleteOldEntries(); to_delete_prerenders_.clear(); // TODO(ajwong): This doesn't handle isolated apps correctly. @@ -546,6 +553,9 @@ bool PrerenderManager::MaybeUsePrerenderedPage(WebContents* web_contents, SwapTabContents(old_web_contents, new_web_contents); prerender_contents->CommitHistory(new_web_contents); + // Record the new target_contents for the callers. + params->target_contents = new_web_contents; + GURL icon_url = prerender_contents->icon_url(); if (!icon_url.is_empty()) { diff --git a/chrome/browser/prerender/prerender_manager.h b/chrome/browser/prerender/prerender_manager.h index 91c547d1f2..c8e7f642be 100644 --- a/chrome/browser/prerender/prerender_manager.h +++ b/chrome/browser/prerender/prerender_manager.h @@ -38,6 +38,10 @@ namespace base { class DictionaryValue; } +namespace chrome { +struct NavigateParams; +} + namespace content { class WebContents; } @@ -156,11 +160,12 @@ class PrerenderManager : public base::SupportsWeakPtr<PrerenderManager>, // Cancels all active prerenders. void CancelAllPrerenders(); - // If |url| matches a valid prerendered page, try to swap it into - // |web_contents| and merge browsing histories. Returns |true| if a - // prerendered page is swapped in, |false| otherwise. - bool MaybeUsePrerenderedPage(content::WebContents* web_contents, - const GURL& url); + // If |url| matches a valid prerendered page and |params| are compatible, try + // to swap it and merge browsing histories. Returns |true| and updates + // |params->target_contents| if a prerendered page is swapped in, |false| + // otherwise. + bool MaybeUsePrerenderedPage(const GURL& url, + chrome::NavigateParams* params); // Moves a PrerenderContents to the pending delete list from the list of // active prerenders when prerendering should be cancelled. diff --git a/chrome/browser/prerender/prerender_manager_factory.cc b/chrome/browser/prerender/prerender_manager_factory.cc index 4a3d5d8bab..31049a08ba 100644 --- a/chrome/browser/prerender/prerender_manager_factory.cc +++ b/chrome/browser/prerender/prerender_manager_factory.cc @@ -16,6 +16,7 @@ #include "chrome/browser/prerender/prerender_manager.h" #include "chrome/browser/profiles/incognito_helpers.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/sync/profile_sync_service_factory.h" #include "components/browser_context_keyed_service/browser_context_dependency_manager.h" #if defined(OS_CHROMEOS) @@ -48,6 +49,7 @@ PrerenderManagerFactory::PrerenderManagerFactory() // PrerenderLocalPredictor observers the history visit DB. DependsOn(HistoryServiceFactory::GetInstance()); DependsOn(predictors::PredictorDatabaseFactory::GetInstance()); + DependsOn(ProfileSyncServiceFactory::GetInstance()); } PrerenderManagerFactory::~PrerenderManagerFactory() { diff --git a/chrome/browser/prerender/prerender_render_view_host_observer.cc b/chrome/browser/prerender/prerender_render_view_host_observer.cc deleted file mode 100644 index 6445cc4b67..0000000000 --- a/chrome/browser/prerender/prerender_render_view_host_observer.cc +++ /dev/null @@ -1,52 +0,0 @@ -// 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/prerender/prerender_render_view_host_observer.h" - -#include "chrome/browser/prerender/prerender_contents.h" -#include "chrome/common/render_messages.h" - -using content::RenderViewHost; - -namespace prerender { - -PrerenderRenderViewHostObserver::PrerenderRenderViewHostObserver( - PrerenderContents* prerender_contents, - RenderViewHost* render_view_host) - : content::RenderViewHostObserver(render_view_host), - prerender_contents_(prerender_contents) { -} - -void PrerenderRenderViewHostObserver::RenderViewHostDestroyed( - RenderViewHost* rvh) { - // The base class deletes |this| on RenderViewHost destruction but we want the - // lifetime to be tied to the PrerenderContents instead, so we'll do nothing - // here. -} - -bool PrerenderRenderViewHostObserver::OnMessageReceived( - const IPC::Message& message) { - if (!prerender_contents_) - return content::RenderViewHostObserver::OnMessageReceived(message); - - bool handled = true; - // The following messages we do want to consume. - IPC_BEGIN_MESSAGE_MAP(PrerenderRenderViewHostObserver, message) - IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CancelPrerenderForPrinting, - OnCancelPrerenderForPrinting) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - - // Pass the message through. - if (!handled) - handled = content::RenderViewHostObserver::OnMessageReceived(message); - - return handled; -} - -void PrerenderRenderViewHostObserver::OnCancelPrerenderForPrinting() { - prerender_contents_->Destroy(FINAL_STATUS_WINDOW_PRINT); -} - -} // namespace prerender diff --git a/chrome/browser/prerender/prerender_render_view_host_observer.h b/chrome/browser/prerender/prerender_render_view_host_observer.h deleted file mode 100644 index 79339fe73a..0000000000 --- a/chrome/browser/prerender/prerender_render_view_host_observer.h +++ /dev/null @@ -1,46 +0,0 @@ -// 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_PRERENDER_PRERENDER_RENDER_VIEW_HOST_OBSERVER_H_ -#define CHROME_BROWSER_PRERENDER_PRERENDER_RENDER_VIEW_HOST_OBSERVER_H_ - -#include "content/public/browser/render_view_host_observer.h" - -namespace content { -class RenderViewHost; -} - -namespace IPC { -class Message; -} - -namespace prerender { - -class PrerenderContents; - -// Observer for RenderViewHost messages. -class PrerenderRenderViewHostObserver : public content::RenderViewHostObserver { - public: - PrerenderRenderViewHostObserver(PrerenderContents* prerender_contents, - content::RenderViewHost* render_view_host); - - virtual void RenderViewHostDestroyed(content::RenderViewHost* rvh) OVERRIDE; - - virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; - - void set_prerender_contents(PrerenderContents* prerender_contents) { - prerender_contents_ = prerender_contents; - } - - private: - // Message handlers. - void OnCancelPrerenderForPrinting(); - - // The associated prerender contents. - PrerenderContents* prerender_contents_; -}; - -} // namespace prerender - -#endif // CHROME_BROWSER_PRERENDER_PRERENDER_RENDER_VIEW_HOST_OBSERVER_H_ diff --git a/chrome/browser/prerender/prerender_tab_helper.cc b/chrome/browser/prerender/prerender_tab_helper.cc index dddb53e9a4..80cfa3af98 100644 --- a/chrome/browser/prerender/prerender_tab_helper.cc +++ b/chrome/browser/prerender/prerender_tab_helper.cc @@ -193,6 +193,7 @@ void PrerenderTabHelper::ProvisionalChangeToMainFrameUrl( void PrerenderTabHelper::DidCommitProvisionalLoadForFrame( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& validated_url, content::PageTransition transition_type, diff --git a/chrome/browser/prerender/prerender_tab_helper.h b/chrome/browser/prerender/prerender_tab_helper.h index 72ab579e69..ed22a49d89 100644 --- a/chrome/browser/prerender/prerender_tab_helper.h +++ b/chrome/browser/prerender/prerender_tab_helper.h @@ -66,6 +66,7 @@ class PrerenderTabHelper content::RenderViewHost* render_view_host) OVERRIDE; virtual void DidCommitProvisionalLoadForFrame( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& validated_url, content::PageTransition transition_type, diff --git a/chrome/browser/prerender/prerender_util.cc b/chrome/browser/prerender/prerender_util.cc index 1c4ab6ff59..3f775191a6 100644 --- a/chrome/browser/prerender/prerender_util.cc +++ b/chrome/browser/prerender/prerender_util.cc @@ -5,6 +5,7 @@ #include "chrome/browser/prerender/prerender_util.h" #include "base/logging.h" +#include "base/metrics/histogram.h" #include "base/metrics/sparse_histogram.h" #include "base/strings/string_util.h" #include "content/public/browser/resource_request_info.h" @@ -13,9 +14,61 @@ #include "url/url_canon.h" #include "url/url_parse.h" #include "url/url_util.h" +#include "webkit/common/resource_type.h" namespace prerender { +namespace { + +const char kModPagespeedHeader[] = "X-Mod-Pagespeed"; +const char kPageSpeedHeader[] = "X-Page-Speed"; +const char kPagespeedServerHistogram[] = + "Prerender.PagespeedHeader.ServerCounts"; +const char kPagespeedVersionHistogram[] = + "Prerender.PagespeedHeader.VersionCounts"; + +enum PagespeedHeaderServerType { + PAGESPEED_TOTAL_RESPONSES = 0, + PAGESPEED_MOD_PAGESPEED_SERVER = 1, + PAGESPEED_NGX_PAGESPEED_SERVER = 2, + PAGESPEED_PAGESPEED_SERVICE_SERVER = 3, + PAGESPEED_UNKNOWN_SERVER = 4, + PAGESPEED_SERVER_MAXIMUM = 5 +}; + +// Private function to parse the PageSpeed version number and encode it in +// buckets 2 through 99: if it is in the format a.b.c.d-e the bucket will be +// 2 + 2 * (max(c, 10) - 10) + (d > 1 ? 1 : 0); if it is not in this format +// we return zero. +int GetXModPagespeedBucketFromVersion(const std::string& version) { + int a, b, c, d, e; + int num_parsed = sscanf(version.c_str(), "%d.%d.%d.%d-%d", + &a, &b, &c, &d, &e); + int output = 0; + if (num_parsed == 5) { + output = 2; + if (c > 10) + output += 2 * (c - 10); + if (d > 1) + output++; + if (output < 2 || output > 99) + output = 0; + } + return output; +} + +// Private function to parse the X-Page-Speed header value and determine +// whether it is in the PageSpeed Service format, namely m_n_dc were m_n is +// a version number and dc is an encoded 2-character value. +bool IsPageSpeedServiceVersionNumber(const std::string& version) { + int a, b; + char c, d, e; // e is to detect EOL as we check that it /isn't/ converted. + int num_parsed = sscanf(version.c_str(), "%d_%d_%c%c%c", &a, &b, &c, &d, &e); + return (num_parsed == 4); +} + +} // namespace + bool MaybeGetQueryStringBasedAliasURL( const GURL& url, GURL* alias_url) { DCHECK(alias_url); @@ -89,44 +142,80 @@ bool IsControlGroupExperiment(uint8 experiment_id) { return experiment_id == 7 || experiment_id == 8; } -void URLRequestResponseStarted(net::URLRequest* request) { - static const char* kModPagespeedHeader = "X-Mod-Pagespeed"; - static const char* kModPagespeedHistogram = "Prerender.ModPagespeedHeader"; - const content::ResourceRequestInfo* info = - content::ResourceRequestInfo::ForRequest(request); - // Gather histogram information about the X-Mod-Pagespeed header. - if (info->GetResourceType() == ResourceType::MAIN_FRAME && - request->url().SchemeIsHTTPOrHTTPS()) { - UMA_HISTOGRAM_SPARSE_SLOWLY(kModPagespeedHistogram, 0); - if (request->response_headers() && - request->response_headers()->HasHeader(kModPagespeedHeader)) { - UMA_HISTOGRAM_SPARSE_SLOWLY(kModPagespeedHistogram, 1); - - // Attempt to parse the version number, and encode it in buckets - // 2 through 99. 0 and 1 are used to store all pageviews and - // # pageviews with the MPS header (see above). - void* iter = NULL; - std::string mps_version; - if (request->response_headers()->EnumerateHeader( - &iter, kModPagespeedHeader, &mps_version) && - !mps_version.empty()) { - // Mod Pagespeed versions are of the form a.b.c.d-e - int a, b, c, d, e; - int num_parsed = sscanf(mps_version.c_str(), "%d.%d.%d.%d-%d", - &a, &b, &c, &d, &e); - if (num_parsed == 5) { - int output = 2; - if (c > 10) - output += 2 * (c - 10); - if (d > 1) - output++; - if (output < 2 || output >= 99) - output = 99; - UMA_HISTOGRAM_SPARSE_SLOWLY(kModPagespeedHistogram, output); +void GatherPagespeedData(const ResourceType::Type resource_type, + const GURL& request_url, + const net::HttpResponseHeaders* response_headers) { + if (resource_type != ResourceType::MAIN_FRAME || + !request_url.SchemeIsHTTPOrHTTPS()) + return; + + // bucket 0 counts every response seen. + UMA_HISTOGRAM_ENUMERATION(kPagespeedServerHistogram, + PAGESPEED_TOTAL_RESPONSES, + PAGESPEED_SERVER_MAXIMUM); + if (!response_headers) + return; + + void* iter = NULL; + std::string name; + std::string value; + while (response_headers->EnumerateHeaderLines(&iter, &name, &value)) { + if (name == kModPagespeedHeader) { + // Bucket 1 counts occurences of the X-Mod-Pagespeed header. + UMA_HISTOGRAM_ENUMERATION(kPagespeedServerHistogram, + PAGESPEED_MOD_PAGESPEED_SERVER, + PAGESPEED_SERVER_MAXIMUM); + if (!value.empty()) { + // If the header value is in the X-Mod-Pagespeed version number format + // then increment the appropriate bucket, otherwise increment bucket 1, + // which is the catch-all "unknown version number" bucket. + int bucket = GetXModPagespeedBucketFromVersion(value); + if (bucket > 0) { + UMA_HISTOGRAM_SPARSE_SLOWLY(kPagespeedVersionHistogram, bucket); + } else { + UMA_HISTOGRAM_SPARSE_SLOWLY(kPagespeedVersionHistogram, 1); + } + } + break; + } else if (name == kPageSpeedHeader) { + // X-Page-Speed header versions are either in the X-Mod-Pagespeed format, + // indicating an nginx installation, or they're in the PageSpeed Service + // format, indicating a PSS installation, or in some other format, + // indicating an unknown installation [possibly IISpeed]. + if (!value.empty()) { + int bucket = GetXModPagespeedBucketFromVersion(value); + if (bucket > 0) { + // Bucket 2 counts occurences of the X-Page-Speed header with a + // value in the X-Mod-Pagespeed version number format. We also + // count these responses in the version histogram. + UMA_HISTOGRAM_ENUMERATION(kPagespeedServerHistogram, + PAGESPEED_NGX_PAGESPEED_SERVER, + PAGESPEED_SERVER_MAXIMUM); + UMA_HISTOGRAM_SPARSE_SLOWLY(kPagespeedVersionHistogram, bucket); + } else if (IsPageSpeedServiceVersionNumber(value)) { + // Bucket 3 counts occurences of the X-Page-Speed header with a + // value in the PageSpeed Service version number format. + UMA_HISTOGRAM_ENUMERATION(kPagespeedServerHistogram, + PAGESPEED_PAGESPEED_SERVICE_SERVER, + PAGESPEED_SERVER_MAXIMUM); + } else { + // Bucket 4 counts occurences of all other values. + UMA_HISTOGRAM_ENUMERATION(kPagespeedServerHistogram, + PAGESPEED_UNKNOWN_SERVER, + PAGESPEED_SERVER_MAXIMUM); } } + break; } } } +void URLRequestResponseStarted(net::URLRequest* request) { + const content::ResourceRequestInfo* info = + content::ResourceRequestInfo::ForRequest(request); + GatherPagespeedData(info->GetResourceType(), + request->url(), + request->response_headers()); +} + } // namespace prerender diff --git a/chrome/browser/prerender/prerender_util.h b/chrome/browser/prerender/prerender_util.h index fd938878d4..04ae703438 100644 --- a/chrome/browser/prerender/prerender_util.h +++ b/chrome/browser/prerender/prerender_util.h @@ -7,8 +7,10 @@ #include "base/basictypes.h" #include "url/gurl.h" +#include "webkit/common/resource_type.h" namespace net { +class HttpResponseHeaders; class URLRequest; } @@ -44,6 +46,13 @@ bool IsNoSwapInExperiment(uint8 experiment_id); // iff this is the case for the experiment_id specified. bool IsControlGroupExperiment(uint8 experiment_id); +// Called by URLRequestResponseStarted to gather data about Pagespeed headers +// into the Prerender.PagespeedHeader histogram. Public so it can be accessed +// by the unit test. +void GatherPagespeedData(const ResourceType::Type resource_type, + const GURL& request_url, + const net::HttpResponseHeaders* response_headers); + // Static method gathering stats about a URLRequest for which a response has // just started. void URLRequestResponseStarted(net::URLRequest* request); diff --git a/chrome/browser/prerender/prerender_util_unittest.cc b/chrome/browser/prerender/prerender_util_unittest.cc index 79a1ffa30b..af8dd1a47b 100644 --- a/chrome/browser/prerender/prerender_util_unittest.cc +++ b/chrome/browser/prerender/prerender_util_unittest.cc @@ -3,7 +3,13 @@ // found in the LICENSE file. #include "chrome/browser/prerender/prerender_util.h" + +#include "base/metrics/histogram.h" +#include "base/metrics/histogram_samples.h" +#include "base/metrics/statistics_recorder.h" +#include "net/http/http_response_headers.h" #include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" namespace prerender { @@ -72,4 +78,148 @@ TEST_F(PrerenderUtilTest, DetectGoogleSearchREsultURLTest) { EXPECT_FALSE(IsGoogleSearchResultURL(GURL("http://www.chromium.org/search"))); } +// Ensure that we count PageSpeed headers correctly. +TEST_F(PrerenderUtilTest, CountPageSpeedHeadersTest) { + base::StatisticsRecorder::Initialize(); + GURL url("http://google.com"); + std::string temp("HTTP/1.1 200 OK\n\n"); + std::replace(temp.begin(), temp.end(), '\n', '\0'); + scoped_refptr<net::HttpResponseHeaders> headers( + new net::HttpResponseHeaders(temp)); + + int num_responses = 0; + int num_mps = 0; + int num_ngx = 0; + int num_pss = 0; + int num_other = 0; + int num_bucket_1 = 0; // unrecognized format/value bucket + int num_bucket_30 = 0; // 1.2.24.1 bucket + int num_bucket_33 = 0; // 1.3.25.2 bucket + + scoped_ptr<base::HistogramSamples> server_samples; + scoped_ptr<base::HistogramSamples> version_samples; + + // No PageSpeed header. The VersionCounts histogram isn't created yet. + GatherPagespeedData(ResourceType::MAIN_FRAME, url, headers.get()); + base::HistogramBase* server_histogram = + base::StatisticsRecorder::FindHistogram( + "Prerender.PagespeedHeader.ServerCounts"); + ASSERT_TRUE(server_histogram != NULL); + ASSERT_TRUE(NULL == base::StatisticsRecorder::FindHistogram( + "Prerender.PagespeedHeader.VersionCounts")); + + server_samples = server_histogram->SnapshotSamples(); + EXPECT_EQ(++num_responses, server_samples->GetCount(0)); + EXPECT_EQ( num_mps, server_samples->GetCount(1)); + EXPECT_EQ( num_ngx, server_samples->GetCount(2)); + EXPECT_EQ( num_pss, server_samples->GetCount(3)); + EXPECT_EQ( num_other, server_samples->GetCount(4)); + + // X-Mod-Pagespeed header in expected format. VersionCounts now exists. + headers->AddHeader("X-Mod-Pagespeed: 1.2.24.1-2300"); + GatherPagespeedData(ResourceType::MAIN_FRAME, url, headers.get()); + base::HistogramBase* version_histogram = + base::StatisticsRecorder::FindHistogram( + "Prerender.PagespeedHeader.VersionCounts"); + ASSERT_TRUE(version_histogram != NULL); + server_samples = server_histogram->SnapshotSamples(); + version_samples = version_histogram->SnapshotSamples(); + EXPECT_EQ(++num_responses, server_samples->GetCount(0)); + EXPECT_EQ(++num_mps, server_samples->GetCount(1)); + EXPECT_EQ( num_ngx, server_samples->GetCount(2)); + EXPECT_EQ( num_pss, server_samples->GetCount(3)); + EXPECT_EQ( num_other, server_samples->GetCount(4)); + EXPECT_EQ( num_bucket_1, version_samples->GetCount(1)); + EXPECT_EQ(++num_bucket_30, version_samples->GetCount(30)); // +1 for #30 + EXPECT_EQ( num_bucket_33, version_samples->GetCount(33)); + headers->RemoveHeader("X-Mod-Pagespeed"); + + // X-Mod-Pagespeed header in unexpected format. + headers->AddHeader("X-Mod-Pagespeed: Powered By PageSpeed!"); + GatherPagespeedData(ResourceType::MAIN_FRAME, url, headers.get()); + server_samples = server_histogram->SnapshotSamples(); + version_samples = version_histogram->SnapshotSamples(); + EXPECT_EQ(++num_responses, server_samples->GetCount(0)); + EXPECT_EQ(++num_mps, server_samples->GetCount(1)); + EXPECT_EQ( num_ngx, server_samples->GetCount(2)); + EXPECT_EQ( num_pss, server_samples->GetCount(3)); + EXPECT_EQ( num_other, server_samples->GetCount(4)); + EXPECT_EQ(++num_bucket_1, version_samples->GetCount(1)); // +1 for 'huh?' + EXPECT_EQ( num_bucket_30, version_samples->GetCount(30)); + EXPECT_EQ( num_bucket_33, version_samples->GetCount(33)); + headers->RemoveHeader("X-Mod-Pagespeed"); + + // X-Page-Speed header in mod_pagespeed format (so ngx_pagespeed). + headers->AddHeader("X-Page-Speed: 1.3.25.2-2530"); + GatherPagespeedData(ResourceType::MAIN_FRAME, url, headers.get()); + server_samples = server_histogram->SnapshotSamples(); + version_samples = version_histogram->SnapshotSamples(); + EXPECT_EQ(++num_responses, server_samples->GetCount(0)); + EXPECT_EQ( num_mps, server_samples->GetCount(1)); + EXPECT_EQ(++num_ngx, server_samples->GetCount(2)); + EXPECT_EQ( num_pss, server_samples->GetCount(3)); + EXPECT_EQ( num_other, server_samples->GetCount(4)); + EXPECT_EQ( num_bucket_1, version_samples->GetCount(1)); + EXPECT_EQ( num_bucket_30, version_samples->GetCount(30)); + EXPECT_EQ(++num_bucket_33, version_samples->GetCount(33)); // +1 for #33 + headers->RemoveHeader("X-Page-Speed"); + + // X-Page-Speed header in PageSpeed Service format. + headers->AddHeader("X-Page-Speed: 97_4_bo"); + GatherPagespeedData(ResourceType::MAIN_FRAME, url, headers.get()); + server_samples = server_histogram->SnapshotSamples(); + version_samples = version_histogram->SnapshotSamples(); + EXPECT_EQ(++num_responses, server_samples->GetCount(0)); + EXPECT_EQ( num_mps, server_samples->GetCount(1)); // no change + EXPECT_EQ( num_ngx, server_samples->GetCount(2)); + EXPECT_EQ(++num_pss, server_samples->GetCount(3)); // +1 for PSS + EXPECT_EQ( num_other, server_samples->GetCount(4)); + EXPECT_EQ( num_bucket_1, version_samples->GetCount(1)); + EXPECT_EQ( num_bucket_30, version_samples->GetCount(30)); + EXPECT_EQ( num_bucket_33, version_samples->GetCount(33)); + headers->RemoveHeader("X-Page-Speed"); + + // X-Page-Speed header in an unrecognized format (IISpeed in this case). + headers->AddHeader("X-Page-Speed: 1.0PS1.2-20130615"); + GatherPagespeedData(ResourceType::MAIN_FRAME, url, headers.get()); + server_samples = server_histogram->SnapshotSamples(); + version_samples = version_histogram->SnapshotSamples(); + EXPECT_EQ(++num_responses, server_samples->GetCount(0)); + EXPECT_EQ( num_mps, server_samples->GetCount(1)); // no change + EXPECT_EQ( num_pss, server_samples->GetCount(3)); + EXPECT_EQ(++num_other, server_samples->GetCount(4)); // +1 for 'other' + EXPECT_EQ( num_bucket_1, version_samples->GetCount(1)); + EXPECT_EQ( num_bucket_30, version_samples->GetCount(30)); + EXPECT_EQ( num_bucket_33, version_samples->GetCount(33)); + + // Not a main frame => not counted at all. + GatherPagespeedData(ResourceType::SUB_FRAME, url, headers.get()); + server_samples = server_histogram->SnapshotSamples(); + version_samples = version_histogram->SnapshotSamples(); + EXPECT_EQ( num_responses, server_samples->GetCount(0)); + EXPECT_EQ( num_mps, server_samples->GetCount(1)); + EXPECT_EQ( num_ngx, server_samples->GetCount(2)); + EXPECT_EQ( num_pss, server_samples->GetCount(3)); + EXPECT_EQ( num_other, server_samples->GetCount(4)); + EXPECT_EQ( num_bucket_1, version_samples->GetCount(1)); + EXPECT_EQ( num_bucket_30, version_samples->GetCount(30)); + EXPECT_EQ( num_bucket_33, version_samples->GetCount(33)); + + // Not a http/https URL => not counted at all. + GURL data_url("data:image/png;base64,yadda yadda=="); + GatherPagespeedData(ResourceType::MAIN_FRAME, data_url, headers.get()); + server_samples = server_histogram->SnapshotSamples(); + version_samples = version_histogram->SnapshotSamples(); + EXPECT_EQ( num_responses, server_samples->GetCount(0)); + EXPECT_EQ( num_mps, server_samples->GetCount(1)); + EXPECT_EQ( num_ngx, server_samples->GetCount(2)); + EXPECT_EQ( num_pss, server_samples->GetCount(3)); + EXPECT_EQ( num_other, server_samples->GetCount(4)); + EXPECT_EQ( num_bucket_1, version_samples->GetCount(1)); + EXPECT_EQ( num_bucket_30, version_samples->GetCount(30)); + EXPECT_EQ( num_bucket_33, version_samples->GetCount(33)); + + headers->RemoveHeader("X-Page-Speed"); +} + } // namespace prerender diff --git a/chrome/browser/printing/print_dialog_cloud_interative_uitest.cc b/chrome/browser/printing/print_dialog_cloud_interative_uitest.cc deleted file mode 100644 index eab142376c..0000000000 --- a/chrome/browser/printing/print_dialog_cloud_interative_uitest.cc +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright (c) 2012 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/printing/print_dialog_cloud.h" -#include "chrome/browser/printing/print_dialog_cloud_internal.h" - -#include <functional> - -#include "base/bind.h" -#include "base/file_util.h" -#include "base/files/file_path.h" -#include "base/memory/singleton.h" -#include "base/path_service.h" -#include "base/strings/utf_string_conversions.h" -#include "base/threading/thread_restrictions.h" -#include "base/values.h" -#include "chrome/browser/printing/cloud_print/cloud_print_url.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_window.h" -#include "chrome/common/chrome_paths.h" -#include "chrome/common/url_constants.h" -#include "chrome/test/base/in_process_browser_test.h" -#include "chrome/test/base/ui_test_utils.h" -#include "content/public/browser/notification_service.h" -#include "content/public/browser/notification_types.h" -#include "content/public/browser/render_view_host.h" -#include "content/public/browser/web_contents.h" -#include "content/public/test/test_browser_thread.h" -#include "net/url_request/url_request.h" -#include "net/url_request/url_request_filter.h" -#include "net/url_request/url_request_test_job.h" -#include "net/url_request/url_request_test_util.h" -#include "ui/base/test/ui_controls.h" - -using content::BrowserThread; - -namespace { - -class TestData { - public: - static TestData* GetInstance() { - return Singleton<TestData>::get(); - } - - const char* GetTestData() { - // Fetching this data blocks the IO thread, but we don't really care because - // this is a test. - base::ThreadRestrictions::ScopedAllowIO allow_io; - - if (test_data_.empty()) { - base::FilePath test_data_directory; - PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory); - base::FilePath test_file = - test_data_directory.AppendASCII("printing/cloud_print_uitest.html"); - base::ReadFileToString(test_file, &test_data_); - } - return test_data_.c_str(); - } - private: - TestData() {} - - std::string test_data_; - - friend struct DefaultSingletonTraits<TestData>; -}; - -// A simple test net::URLRequestJob. We don't care what it does, only that -// whether it starts and finishes. -class SimpleTestJob : public net::URLRequestTestJob { - public: - SimpleTestJob(net::URLRequest* request, - net::NetworkDelegate* network_delegate) - : net::URLRequestTestJob(request, - network_delegate, - test_headers(), - TestData::GetInstance()->GetTestData(), - true) {} - - virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE { - net::URLRequestTestJob::GetResponseInfo(info); - if (request_->url().SchemeIsSecure()) { - // Make up a fake certificate for this response since we don't have - // access to the real SSL info. - const char* kCertIssuer = "Chrome Internal"; - const int kLifetimeDays = 100; - - info->ssl_info.cert = - new net::X509Certificate(request_->url().GetWithEmptyPath().spec(), - kCertIssuer, - base::Time::Now(), - base::Time::Now() + - base::TimeDelta::FromDays(kLifetimeDays)); - info->ssl_info.cert_status = 0; - info->ssl_info.security_bits = -1; - } - } - - private: - virtual ~SimpleTestJob() {} -}; - -class TestController { - public: - static TestController* GetInstance() { - return Singleton<TestController>::get(); - } - void set_result(bool value) { - result_ = value; - } - bool result() { - return result_; - } - void set_expected_url(const GURL& url) { - expected_url_ = url; - } - const GURL expected_url() { - return expected_url_; - } - void set_delegate(net::TestDelegate* delegate) { - delegate_ = delegate; - } - net::TestDelegate* delegate() { - return delegate_; - } - void set_use_delegate(bool value) { - use_delegate_ = value; - } - bool use_delegate() { - return use_delegate_; - } - private: - TestController() - : result_(false), - use_delegate_(false), - delegate_(NULL) {} - - bool result_; - bool use_delegate_; - GURL expected_url_; - net::TestDelegate* delegate_; - - friend struct DefaultSingletonTraits<TestController>; -}; - -} // namespace - -class PrintDialogCloudTest : public InProcessBrowserTest { - public: - PrintDialogCloudTest() : handler_added_(false) { - PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory_); - } - - // Must be static for handing into AddHostnameHandler. - static net::URLRequest::ProtocolFactory Factory; - - class AutoQuitDelegate : public net::TestDelegate { - public: - AutoQuitDelegate() {} - - virtual void OnResponseCompleted(net::URLRequest* request) OVERRIDE { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::MessageLoop::QuitClosure()); - } - }; - - virtual void SetUp() OVERRIDE { - TestController::GetInstance()->set_result(false); - InProcessBrowserTest::SetUp(); - } - - virtual void TearDown() OVERRIDE { - if (handler_added_) { - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(UnregisterTestHandlers, scheme_, host_name_)); - handler_added_ = false; - TestController::GetInstance()->set_delegate(NULL); - } - InProcessBrowserTest::TearDown(); - } - - // Normally this is something I would expect could go into SetUp(), - // but there seems to be some timing or ordering related issue with - // the test harness that made that flaky. Calling this from the - // individual test functions seems to fix that. - void AddTestHandlers() { - if (!handler_added_) { - GURL cloud_print_service_url = - CloudPrintURL(browser()->profile()). - GetCloudPrintServiceURL(); - scheme_ = cloud_print_service_url.scheme(); - host_name_ = cloud_print_service_url.host(); - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(RegisterTestHandlers, scheme_, host_name_)); - handler_added_ = true; - - GURL cloud_print_dialog_url = - CloudPrintURL(browser()->profile()). - GetCloudPrintServiceDialogURL(); - TestController::GetInstance()->set_expected_url(cloud_print_dialog_url); - TestController::GetInstance()->set_delegate(&delegate_); - } - - CreateDialogForTest(); - } - - void CreateDialogForTest() { - base::FilePath path_to_pdf = - test_data_directory_.AppendASCII("printing/cloud_print_uitest.pdf"); - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&print_dialog_cloud::CreatePrintDialogForFile, - browser()->profile(), browser()->window()->GetNativeWindow(), - path_to_pdf, string16(), string16(), - std::string("application/pdf"), false)); - } - - private: - static void RegisterTestHandlers(const std::string& scheme, - const std::string& host_name) { - net::URLRequestFilter::GetInstance()->AddHostnameHandler( - scheme, host_name, &PrintDialogCloudTest::Factory); - } - static void UnregisterTestHandlers(const std::string& scheme, - const std::string& host_name) { - net::URLRequestFilter::GetInstance()->RemoveHostnameHandler(scheme, - host_name); - } - - bool handler_added_; - std::string scheme_; - std::string host_name_; - base::FilePath test_data_directory_; - AutoQuitDelegate delegate_; -}; - -net::URLRequestJob* PrintDialogCloudTest::Factory( - net::URLRequest* request, - net::NetworkDelegate* network_delegate, - const std::string& scheme) { - if (request && - (request->url() == TestController::GetInstance()->expected_url())) { - if (TestController::GetInstance()->use_delegate()) - request->set_delegate(TestController::GetInstance()->delegate()); - TestController::GetInstance()->set_result(true); - return new SimpleTestJob(request, network_delegate); - } - return new net::URLRequestTestJob(request, - network_delegate, - net::URLRequestTestJob::test_headers(), - std::string(), - true); -} - -IN_PROC_BROWSER_TEST_F(PrintDialogCloudTest, HandlersRegistered) { - AddTestHandlers(); - - TestController::GetInstance()->set_use_delegate(true); - - content::RunMessageLoop(); - - ASSERT_TRUE(TestController::GetInstance()->result()); - - // Close the dialog before finishing the test. - content::WindowedNotificationObserver tab_closed_observer( - content::NOTIFICATION_WEB_CONTENTS_DESTROYED, - content::NotificationService::AllSources()); - - // Can't use ui_test_utils::SendKeyPressSync or - // ui_test_utils::SendKeyPressAndWait due to a race condition with closing - // the window. See http://crbug.com/111269 - BrowserWindow* window = browser()->window(); - ASSERT_TRUE(window); - gfx::NativeWindow native_window = window->GetNativeWindow(); - ASSERT_TRUE(native_window); - bool key_sent = ui_controls::SendKeyPress(native_window, ui::VKEY_ESCAPE, - false, false, false, false); - EXPECT_TRUE(key_sent); - if (key_sent) - tab_closed_observer.Wait(); -} diff --git a/chrome/browser/printing/print_job_worker.cc b/chrome/browser/printing/print_job_worker.cc index a3298f5998..66477b1beb 100644 --- a/chrome/browser/printing/print_job_worker.cc +++ b/chrome/browser/printing/print_job_worker.cc @@ -13,6 +13,7 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/printing/print_job.h" +#include "chrome/browser/printing/printing_ui_web_contents_observer.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h" #include "grit/generated_resources.h" @@ -77,11 +78,12 @@ void PrintJobWorker::SetPrintDestination( destination_ = destination; } -void PrintJobWorker::GetSettings(bool ask_user_for_settings, - gfx::NativeView parent_view, - int document_page_count, - bool has_selection, - MarginType margin_type) { +void PrintJobWorker::GetSettings( + bool ask_user_for_settings, + scoped_ptr<PrintingUIWebContentsObserver> web_contents_observer, + int document_page_count, + bool has_selection, + MarginType margin_type) { DCHECK_EQ(message_loop(), base::MessageLoop::current()); DCHECK_EQ(page_number_, PageNumber::npos()); @@ -100,8 +102,10 @@ void PrintJobWorker::GetSettings(bool ask_user_for_settings, BrowserThread::UI, FROM_HERE, base::Bind(&HoldRefCallback, make_scoped_refptr(owner_), base::Bind(&PrintJobWorker::GetSettingsWithUI, - base::Unretained(this), parent_view, - document_page_count, has_selection))); + base::Unretained(this), + base::Passed(&web_contents_observer), + document_page_count, + has_selection))); } else { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, @@ -169,11 +173,17 @@ void PrintJobWorker::GetSettingsDone(PrintingContext::Result result) { result)); } -void PrintJobWorker::GetSettingsWithUI(gfx::NativeView parent_view, - int document_page_count, - bool has_selection) { +void PrintJobWorker::GetSettingsWithUI( + scoped_ptr<PrintingUIWebContentsObserver> web_contents_observer, + int document_page_count, + bool has_selection) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + gfx::NativeView parent_view = web_contents_observer->GetParentView(); + if (!parent_view) { + GetSettingsWithUIDone(printing::PrintingContext::FAILED); + return; + } printing_context_->AskUserForSettings( parent_view, document_page_count, has_selection, base::Bind(&PrintJobWorker::GetSettingsWithUIDone, diff --git a/chrome/browser/printing/print_job_worker.h b/chrome/browser/printing/print_job_worker.h index 2b639fafd3..beb3cd7f45 100644 --- a/chrome/browser/printing/print_job_worker.h +++ b/chrome/browser/printing/print_job_worker.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_H__ -#define CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_H__ +#ifndef CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_H_ +#define CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_H_ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" @@ -13,7 +13,8 @@ #include "printing/print_destination_interface.h" #include "printing/printing_context.h" #include "printing/print_job_constants.h" -#include "ui/gfx/native_widget_types.h" + +class PrintingUIWebContentsObserver; namespace base { class DictionaryValue; @@ -45,11 +46,12 @@ class PrintJobWorker : public base::Thread { // Initializes the print settings. If |ask_user_for_settings| is true, a // Print... dialog box will be shown to ask the user his preference. - void GetSettings(bool ask_user_for_settings, - gfx::NativeView parent_view, - int document_page_count, - bool has_selection, - MarginType margin_type); + void GetSettings( + bool ask_user_for_settings, + scoped_ptr<PrintingUIWebContentsObserver> web_contents_observer, + int document_page_count, + bool has_selection, + MarginType margin_type); // Set the new print settings. This function takes ownership of // |new_settings|. @@ -95,9 +97,10 @@ class PrintJobWorker : public base::Thread { // Asks the user for print settings. Must be called on the UI thread. // Required on Mac and Linux. Windows can display UI from non-main threads, // but sticks with this for consistency. - void GetSettingsWithUI(gfx::NativeView parent_view, - int document_page_count, - bool has_selection); + void GetSettingsWithUI( + scoped_ptr<PrintingUIWebContentsObserver> web_contents_observer, + int document_page_count, + bool has_selection); // The callback used by PrintingContext::GetSettingsWithUI() to notify this // object that the print settings are set. This is needed in order to bounce @@ -140,4 +143,4 @@ class PrintJobWorker : public base::Thread { } // namespace printing -#endif // CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_H__ +#endif // CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_H_ diff --git a/chrome/browser/printing/printer_query.cc b/chrome/browser/printing/printer_query.cc index 6935975bbc..03198e235b 100644 --- a/chrome/browser/printing/printer_query.cc +++ b/chrome/browser/printing/printer_query.cc @@ -10,6 +10,7 @@ #include "base/threading/thread_restrictions.h" #include "base/values.h" #include "chrome/browser/printing/print_job_worker.h" +#include "chrome/browser/printing/printing_ui_web_contents_observer.h" namespace printing { @@ -68,12 +69,13 @@ int PrinterQuery::cookie() const { return cookie_; } -void PrinterQuery::GetSettings(GetSettingsAskParam ask_user_for_settings, - gfx::NativeView parent_view, - int expected_page_count, - bool has_selection, - MarginType margin_type, - const base::Closure& callback) { +void PrinterQuery::GetSettings( + GetSettingsAskParam ask_user_for_settings, + scoped_ptr<PrintingUIWebContentsObserver> web_contents_observer, + int expected_page_count, + bool has_selection, + MarginType margin_type, + const base::Closure& callback) { DCHECK_EQ(io_message_loop_, base::MessageLoop::current()); DCHECK(!is_print_dialog_box_shown_); @@ -85,8 +87,11 @@ void PrinterQuery::GetSettings(GetSettingsAskParam ask_user_for_settings, FROM_HERE, base::Bind(&PrintJobWorker::GetSettings, base::Unretained(worker_.get()), - is_print_dialog_box_shown_, parent_view, - expected_page_count, has_selection, margin_type)); + is_print_dialog_box_shown_, + base::Passed(&web_contents_observer), + expected_page_count, + has_selection, + margin_type)); } void PrinterQuery::SetSettings(const DictionaryValue& new_settings, diff --git a/chrome/browser/printing/printer_query.h b/chrome/browser/printing/printer_query.h index 3256a917b7..aab4914324 100644 --- a/chrome/browser/printing/printer_query.h +++ b/chrome/browser/printing/printer_query.h @@ -10,7 +10,8 @@ #include "base/memory/scoped_ptr.h" #include "chrome/browser/printing/print_job_worker_owner.h" #include "printing/print_job_constants.h" -#include "ui/gfx/native_widget_types.h" + +class PrintingUIWebContentsObserver; namespace base { class DictionaryValue; @@ -19,8 +20,8 @@ class MessageLoop; namespace printing { - class PrintDestinationInterface; - class PrintJobWorker; +class PrintDestinationInterface; +class PrintJobWorker; // Query the printer for settings. class PrinterQuery : public PrintJobWorkerOwner { @@ -42,15 +43,16 @@ class PrinterQuery : public PrintJobWorkerOwner { virtual int cookie() const OVERRIDE; // Initializes the printing context. It is fine to call this function multiple - // times to reinitialize the settings. |parent_view| parameter's window will - // be the owner of the print setting dialog box. It is unused when + // times to reinitialize the settings. |web_contents_observer| can be queried + // to find the owner of the print setting dialog box. It is unused when // |ask_for_user_settings| is DEFAULTS. - void GetSettings(GetSettingsAskParam ask_user_for_settings, - gfx::NativeView parent_view, - int expected_page_count, - bool has_selection, - MarginType margin_type, - const base::Closure& callback); + void GetSettings( + GetSettingsAskParam ask_user_for_settings, + scoped_ptr<PrintingUIWebContentsObserver> web_contents_observer, + int expected_page_count, + bool has_selection, + MarginType margin_type, + const base::Closure& callback); // Updates the current settings with |new_settings| dictionary values. void SetSettings(const base::DictionaryValue& new_settings, diff --git a/chrome/browser/printing/printing_message_filter.cc b/chrome/browser/printing/printing_message_filter.cc index 79a1194991..5b48b891b8 100644 --- a/chrome/browser/printing/printing_message_filter.cc +++ b/chrome/browser/printing/printing_message_filter.cc @@ -10,13 +10,13 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/printing/printer_query.h" #include "chrome/browser/printing/print_job_manager.h" +#include "chrome/browser/printing/printing_ui_web_contents_observer.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_io_data.h" #include "chrome/common/print_messages.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" -#include "content/public/browser/web_contents_view.h" #if defined(ENABLE_FULL_PRINTING) #include "chrome/browser/ui/webui/print_preview/print_preview_ui.h" @@ -30,6 +30,7 @@ #include "base/file_util.h" #include "base/lazy_instance.h" #include "chrome/browser/printing/print_dialog_cloud.h" +#include "content/public/browser/web_contents_view.h" #endif #if defined(OS_ANDROID) @@ -273,10 +274,12 @@ void PrintingMessageFilter::GetPrintSettingsForRenderView( DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); content::WebContents* wc = GetWebContentsForRenderView(render_view_id); if (wc) { + scoped_ptr<PrintingUIWebContentsObserver> wc_observer( + new PrintingUIWebContentsObserver(wc)); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&printing::PrinterQuery::GetSettings, printer_query, - params.ask_user_for_settings, wc->GetView()->GetNativeView(), + params.ask_user_for_settings, base::Passed(&wc_observer), params.expected_page_count, params.has_selection, params.margin_type, callback)); } else { diff --git a/chrome/browser/printing/printing_ui_web_contents_observer.cc b/chrome/browser/printing/printing_ui_web_contents_observer.cc new file mode 100644 index 0000000000..03a09f7009 --- /dev/null +++ b/chrome/browser/printing/printing_ui_web_contents_observer.cc @@ -0,0 +1,20 @@ +// Copyright 2013 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/printing/printing_ui_web_contents_observer.h" + +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_view.h" + +PrintingUIWebContentsObserver::PrintingUIWebContentsObserver( + content::WebContents* web_contents) + : content::WebContentsObserver(web_contents) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); +} + +gfx::NativeView PrintingUIWebContentsObserver::GetParentView() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + return web_contents() ? web_contents()->GetView()->GetNativeView() : NULL; +} diff --git a/chrome/browser/printing/printing_ui_web_contents_observer.h b/chrome/browser/printing/printing_ui_web_contents_observer.h new file mode 100644 index 0000000000..66d51e7fba --- /dev/null +++ b/chrome/browser/printing/printing_ui_web_contents_observer.h @@ -0,0 +1,25 @@ +// Copyright 2013 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_PRINTING_PRINTING_UI_WEB_CONTENTS_OBSERVER_H_ +#define CHROME_BROWSER_PRINTING_PRINTING_UI_WEB_CONTENTS_OBSERVER_H_ + +#include "base/basictypes.h" +#include "content/public/browser/web_contents_observer.h" +#include "ui/gfx/native_widget_types.h" + +// Wrapper used to keep track of the lifetime of a WebContents. +// Lives on the UI thread. +class PrintingUIWebContentsObserver : public content::WebContentsObserver { + public: + explicit PrintingUIWebContentsObserver(content::WebContents* web_contents); + + // Return the parent NativeView of the observed WebContents. + gfx::NativeView GetParentView(); + + private: + DISALLOW_COPY_AND_ASSIGN(PrintingUIWebContentsObserver); +}; + +#endif // CHROME_BROWSER_PRINTING_PRINTING_UI_WEB_CONTENTS_OBSERVER_H_ diff --git a/chrome/browser/profile_resetter/profile_resetter_unittest.cc b/chrome/browser/profile_resetter/profile_resetter_unittest.cc index a1aaf6bc05..44bdb2cdc8 100644 --- a/chrome/browser/profile_resetter/profile_resetter_unittest.cc +++ b/chrome/browser/profile_resetter/profile_resetter_unittest.cc @@ -42,7 +42,7 @@ const char kDistributionConfig[] = "{" " }," " \"session\" : {" " \"restore_on_startup\" : 4," - " \"urls_to_restore_on_startup\" : [\"http://goo.gl\", \"http://foo.de\"]" + " \"startup_urls\" : [\"http://goo.gl\", \"http://foo.de\"]" " }," " \"search_provider_overrides\" : [" " {" diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc index 4d3f8cac46..b19122c546 100644 --- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc +++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc @@ -33,6 +33,7 @@ #include "chrome/browser/prerender/prerender_manager_factory.h" #include "chrome/browser/printing/cloud_print/cloud_print_proxy_service_factory.h" #include "chrome/browser/profiles/gaia_info_update_service_factory.h" +#include "chrome/browser/search/hotword_service_factory.h" #include "chrome/browser/search/instant_service_factory.h" #include "chrome/browser/search_engines/template_url_fetcher_factory.h" #include "chrome/browser/search_engines/template_url_service_factory.h" @@ -89,6 +90,7 @@ #include "chrome/browser/extensions/api/socket/tcp_socket.h" #include "chrome/browser/extensions/api/socket/udp_socket.h" #include "chrome/browser/extensions/api/sockets_tcp/tcp_socket_event_dispatcher.h" +#include "chrome/browser/extensions/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h" #include "chrome/browser/extensions/api/sockets_udp/udp_socket_event_dispatcher.h" #include "chrome/browser/extensions/api/streams_private/streams_private_api.h" #include "chrome/browser/extensions/api/system_info/system_info_api.h" @@ -218,6 +220,8 @@ EnsureBrowserContextKeyedServiceFactoriesBuilt() { extensions::ActivityLogFactory::GetInstance(); extensions::ActivityLogAPI::GetFactoryInstance(); extensions::AlarmManager::GetFactoryInstance(); + extensions::ApiResourceManager<extensions::ResumableTCPServerSocket>:: + GetFactoryInstance(); extensions::ApiResourceManager<extensions::ResumableTCPSocket>:: GetFactoryInstance(); extensions::ApiResourceManager<extensions::ResumableUDPSocket>:: @@ -227,6 +231,7 @@ EnsureBrowserContextKeyedServiceFactoriesBuilt() { extensions::ApiResourceManager<extensions::Socket>::GetFactoryInstance(); extensions::ApiResourceManager<extensions::UsbDeviceResource>:: GetFactoryInstance(); + extensions::api::TCPServerSocketEventDispatcher::GetFactoryInstance(); extensions::api::TCPSocketEventDispatcher::GetFactoryInstance(); extensions::api::UDPSocketEventDispatcher::GetFactoryInstance(); extensions::AudioAPI::GetFactoryInstance(); @@ -294,6 +299,7 @@ EnsureBrowserContextKeyedServiceFactoriesBuilt() { GlobalErrorServiceFactory::GetInstance(); GoogleURLTrackerFactory::GetInstance(); HistoryServiceFactory::GetInstance(); + HotwordServiceFactory::GetInstance(); invalidation::InvalidationServiceFactory::GetInstance(); InstantServiceFactory::GetInstance(); #if defined(ENABLE_MDNS) diff --git a/chrome/browser/profiles/profile_browsertest.cc b/chrome/browser/profiles/profile_browsertest.cc index 0386d22517..063c13e352 100644 --- a/chrome/browser/profiles/profile_browsertest.cc +++ b/chrome/browser/profiles/profile_browsertest.cc @@ -24,6 +24,7 @@ namespace { class MockProfileDelegate : public Profile::Delegate { public: + MOCK_METHOD1(OnPrefsLoaded, void(Profile*)); MOCK_METHOD3(OnProfileCreated, void(Profile*, bool, bool)); }; diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc index d761103407..9c9c1de725 100644 --- a/chrome/browser/profiles/profile_impl.cc +++ b/chrome/browser/profiles/profile_impl.cc @@ -408,16 +408,16 @@ ProfileImpl::ProfileImpl( #if defined(OS_CHROMEOS) cloud_policy_manager_ = policy::UserCloudPolicyManagerFactoryChromeOS::CreateForProfile( - this, force_immediate_policy_load); + this, force_immediate_policy_load, sequenced_task_runner); #else cloud_policy_manager_ = policy::UserCloudPolicyManagerFactory::CreateForProfile( - this, force_immediate_policy_load); + this, force_immediate_policy_load, sequenced_task_runner); #endif #endif profile_policy_connector_ = policy::ProfilePolicyConnectorFactory::CreateForProfile( - this, force_immediate_policy_load, sequenced_task_runner); + this, force_immediate_policy_load); DCHECK(create_mode == CREATE_MODE_ASYNCHRONOUS || create_mode == CREATE_MODE_SYNCHRONOUS); @@ -492,6 +492,10 @@ void ProfileImpl::DoFinalInit() { prefs::kProfileName, base::Bind(&ProfileImpl::UpdateProfileNameCache, base::Unretained(this))); + pref_change_registrar_.Add( + prefs::kForceEphemeralProfiles, + base::Bind(&ProfileImpl::UpdateProfileIsEphemeralCache, + base::Unretained(this))); // It would be nice to use PathService for fetching this directory, but // the cache directory depends on the profile directory, which isn't available @@ -783,6 +787,8 @@ void ProfileImpl::OnPrefsLoaded(bool success) { // TODO(sky): remove this in a couple of releases (m28ish). prefs_->SetBoolean(prefs::kSessionExitedCleanly, true); + g_browser_process->profile_manager()->InitProfileUserPrefs(this); + BrowserContextDependencyManager::GetInstance()->CreateBrowserContextServices( this); @@ -1189,6 +1195,16 @@ void ProfileImpl::UpdateProfileAvatarCache() { } } +void ProfileImpl::UpdateProfileIsEphemeralCache() { + ProfileManager* profile_manager = g_browser_process->profile_manager(); + ProfileInfoCache& cache = profile_manager->GetProfileInfoCache(); + size_t index = cache.GetIndexOfProfileWithPath(GetPath()); + if (index != std::string::npos) { + bool is_ephemeral = GetPrefs()->GetBoolean(prefs::kForceEphemeralProfiles); + cache.SetProfileIsEphemeralAtIndex(index, is_ephemeral); + } +} + // Gets the cache parameters from the command line. If |is_media_context| is // set to true then settings for the media context type is what we need, // |cache_path| will be set to the user provided path, or will not be touched if diff --git a/chrome/browser/profiles/profile_impl.h b/chrome/browser/profiles/profile_impl.h index 47fce06a89..1401d1b533 100644 --- a/chrome/browser/profiles/profile_impl.h +++ b/chrome/browser/profiles/profile_impl.h @@ -185,6 +185,7 @@ class ProfileImpl : public Profile { // Updates the ProfileInfoCache with data from this profile. void UpdateProfileNameCache(); void UpdateProfileAvatarCache(); + void UpdateProfileIsEphemeralCache(); void GetCacheParameters(bool is_media_context, base::FilePath* cache_path, diff --git a/chrome/browser/profiles/profile_info_cache.cc b/chrome/browser/profiles/profile_info_cache.cc index 1f38167439..f57205eb12 100644 --- a/chrome/browser/profiles/profile_info_cache.cc +++ b/chrome/browser/profiles/profile_info_cache.cc @@ -51,6 +51,7 @@ const char kGAIAPictureFileNameKey[] = "gaia_picture_file_name"; const char kIsManagedKey[] = "is_managed"; const char kSigninRequiredKey[] = "signin_required"; const char kManagedUserId[] = "managed_user_id"; +const char kProfileIsEphemeral[] = "is_ephemeral"; const char kDefaultUrlPrefix[] = "chrome://theme/IDR_PROFILE_AVATAR_"; const char kGAIAPictureFileName[] = "Google Profile Picture.png"; @@ -216,6 +217,7 @@ void ProfileInfoCache::AddProfileToCache(const base::FilePath& profile_path, // Default value for whether background apps are running is false. info->SetBoolean(kBackgroundAppsKey, false); info->SetString(kManagedUserId, managed_user_id); + info->SetBoolean(kProfileIsEphemeral, false); cache->SetWithoutPathExpansion(key, info.release()); sorted_keys_.insert(FindPositionForProfile(key, name), key); @@ -404,6 +406,12 @@ std::string ProfileInfoCache::GetManagedUserIdOfProfileAtIndex( return managed_user_id; } +bool ProfileInfoCache::ProfileIsEphemeralAtIndex(size_t index) const { + bool value = false; + GetInfoForProfileAtIndex(index)->GetBoolean(kProfileIsEphemeral, &value); + return value; +} + void ProfileInfoCache::OnGAIAPictureLoaded(const base::FilePath& path, gfx::Image** image) const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -667,6 +675,16 @@ void ProfileInfoCache::SetProfileSigninRequiredAtIndex(size_t index, SetInfoForProfileAtIndex(index, info.release()); } +void ProfileInfoCache::SetProfileIsEphemeralAtIndex(size_t index, bool value) { + if (value == ProfileIsEphemeralAtIndex(index)) + return; + + scoped_ptr<DictionaryValue> info(GetInfoForProfileAtIndex(index)->DeepCopy()); + info->SetBoolean(kProfileIsEphemeral, value); + // This takes ownership of |info|. + SetInfoForProfileAtIndex(index, info.release()); +} + string16 ProfileInfoCache::ChooseNameForNewProfile(size_t icon_index) const { string16 name; for (int name_index = 1; ; ++name_index) { diff --git a/chrome/browser/profiles/profile_info_cache.h b/chrome/browser/profiles/profile_info_cache.h index aeb48420bd..bf6cabe51c 100644 --- a/chrome/browser/profiles/profile_info_cache.h +++ b/chrome/browser/profiles/profile_info_cache.h @@ -80,6 +80,7 @@ class ProfileInfoCache : public ProfileInfoInterface, virtual bool ProfileIsSigninRequiredAtIndex(size_t index) const OVERRIDE; virtual std::string GetManagedUserIdOfProfileAtIndex(size_t index) const OVERRIDE; + virtual bool ProfileIsEphemeralAtIndex(size_t index) const OVERRIDE; size_t GetAvatarIconIndexOfProfileAtIndex(size_t index) const; @@ -96,6 +97,7 @@ class ProfileInfoCache : public ProfileInfoInterface, void SetGAIAPictureOfProfileAtIndex(size_t index, const gfx::Image* image); void SetIsUsingGAIAPictureOfProfileAtIndex(size_t index, bool value); void SetProfileSigninRequiredAtIndex(size_t index, bool value); + void SetProfileIsEphemeralAtIndex(size_t index, bool value); // Returns unique name that can be assigned to a newly created profile. string16 ChooseNameForNewProfile(size_t icon_index) const; diff --git a/chrome/browser/profiles/profile_info_cache_unittest.cc b/chrome/browser/profiles/profile_info_cache_unittest.cc index 1d5e6df7d0..1a6e089af5 100644 --- a/chrome/browser/profiles/profile_info_cache_unittest.cc +++ b/chrome/browser/profiles/profile_info_cache_unittest.cc @@ -18,6 +18,7 @@ #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" #include "content/public/browser/notification_service.h" +#include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/test_utils.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/resource/resource_bundle.h" @@ -459,6 +460,11 @@ TEST_F(ProfileInfoCacheTest, CreateManagedTestingProfile) { std::string managed_user_id = is_managed ? "TEST_ID" : ""; EXPECT_EQ(managed_user_id, GetCache()->GetManagedUserIdOfProfileAtIndex(i)); } + + // Managed profiles have a custom theme, which needs to be deleted on the FILE + // thread. Reset the profile manager now so everything is deleted while we + // still have a FILE thread. + TestingBrowserProcess::GetGlobal()->SetProfileManager(NULL); } TEST_F(ProfileInfoCacheTest, AddStubProfile) { diff --git a/chrome/browser/profiles/profile_info_interface.h b/chrome/browser/profiles/profile_info_interface.h index b359a86fbc..85d9e7eef5 100644 --- a/chrome/browser/profiles/profile_info_interface.h +++ b/chrome/browser/profiles/profile_info_interface.h @@ -58,6 +58,9 @@ class ProfileInfoInterface { // This profile is associated with an account but has been signed-out. virtual bool ProfileIsSigninRequiredAtIndex(size_t index) const = 0; + // Profile is known to be ephemeral and should be deleted when closed. + virtual bool ProfileIsEphemeralAtIndex(size_t index) const = 0; + protected: virtual ~ProfileInfoInterface() {} }; diff --git a/chrome/browser/profiles/profile_info_util.cc b/chrome/browser/profiles/profile_info_util.cc index 1f56b3d552..0f8b1e5f85 100644 --- a/chrome/browser/profiles/profile_info_util.cc +++ b/chrome/browser/profiles/profile_info_util.cc @@ -4,15 +4,154 @@ #include "chrome/browser/profiles/profile_info_util.h" -#include "skia/ext/image_operations.h" +#include "base/memory/scoped_ptr.h" +#include "third_party/skia/include/core/SkPaint.h" +#include "third_party/skia/include/core/SkPath.h" +#include "third_party/skia/include/core/SkScalar.h" +#include "third_party/skia/include/core/SkXfermode.h" #include "ui/gfx/canvas.h" -#include "ui/gfx/rect.h" +#include "ui/gfx/image/canvas_image_source.h" +#include "ui/gfx/image/image_skia_operations.h" namespace profiles { const int kAvatarIconWidth = 38; const int kAvatarIconHeight = 31; -const int kAvatarIconBorder = 2; +const int kAvatarIconPadding = 2; + +namespace internal { + +// A CanvasImageSource that draws a sized and positioned avatar with an +// optional border independently of the scale factor. +class AvatarImageSource : public gfx::CanvasImageSource { + public: + enum AvatarPosition { + POSITION_CENTER, + POSITION_BOTTOM_CENTER, + }; + + enum AvatarBorder { + BORDER_NONE, + BORDER_NORMAL, + BORDER_ETCHED, + }; + + AvatarImageSource(gfx::ImageSkia avatar, + const gfx::Size& canvas_size, + int size, + AvatarPosition position, + AvatarBorder border); + virtual ~AvatarImageSource(); + + // CanvasImageSource override: + virtual void Draw(gfx::Canvas* canvas) OVERRIDE; + + private: + gfx::ImageSkia avatar_; + const gfx::Size canvas_size_; + const int size_; + const AvatarPosition position_; + const AvatarBorder border_; + + DISALLOW_COPY_AND_ASSIGN(AvatarImageSource); +}; + +AvatarImageSource::AvatarImageSource(gfx::ImageSkia avatar, + const gfx::Size& canvas_size, + int size, + AvatarPosition position, + AvatarBorder border) + : gfx::CanvasImageSource(canvas_size, false), + canvas_size_(canvas_size), + size_(size - kAvatarIconPadding), + position_(position), + border_(border) { + // Resize the avatar to the desired square size. + avatar_ = gfx::ImageSkiaOperations::CreateResizedImage( + avatar, skia::ImageOperations::RESIZE_BEST, gfx::Size(size_, size_)); +} + +AvatarImageSource::~AvatarImageSource() { +} + +void AvatarImageSource::Draw(gfx::Canvas* canvas) { + // Center the avatar horizontally. + int x = (canvas_size_.width() - size_) / 2; + int y; + + if (position_ == POSITION_CENTER) { + // Draw the avatar centered on the canvas. + y = (canvas_size_.height() - size_) / 2; + } else { + // Draw the avatar on the bottom center of the canvas, leaving 1px below. + y = canvas_size_.height() - size_ - 1; + } + + canvas->DrawImageInt(avatar_, x, y); + + if (border_ == BORDER_NORMAL) { + // Draw a gray border on the inside of the avatar. + SkColor border_color = SkColorSetARGB(83, 0, 0, 0); + + // Offset the rectangle by a half pixel so the border is drawn within the + // appropriate pixels no matter the scale factor. Subtract 1 from the right + // and bottom sizes to specify the endpoints, yielding -0.5. + SkPath path; + path.addRect(SkFloatToScalar(x + 0.5f), // left + SkFloatToScalar(y + 0.5f), // top + SkFloatToScalar(x + size_ - 0.5f), // right + SkFloatToScalar(y + size_ - 0.5f)); // bottom + + SkPaint paint; + paint.setColor(border_color); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(SkIntToScalar(1)); + + canvas->DrawPath(path, paint); + } else if (border_ == BORDER_ETCHED) { + // Give the avatar an etched look by drawing a highlight on the bottom and + // right edges. + SkColor shadow_color = SkColorSetARGB(83, 0, 0, 0); + SkColor highlight_color = SkColorSetARGB(96, 255, 255, 255); + + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(SkIntToScalar(1)); + + SkPath path; + + // Left and top shadows. To support higher scale factors than 1, position + // the orthogonal dimension of each line on the half-pixel to separate the + // pixel. For a vertical line, this means adding 0.5 to the x-value. + path.moveTo(SkFloatToScalar(x + 0.5f), SkIntToScalar(y + size_)); + + // Draw up to the top-left. Stop with the y-value at a half-pixel. + path.rLineTo(SkIntToScalar(0), SkFloatToScalar(-size_ + 0.5f)); + + // Draw right to the top-right, stopping within the last pixel. + path.rLineTo(SkFloatToScalar(size_ - 0.5f), SkIntToScalar(0)); + + paint.setColor(shadow_color); + canvas->DrawPath(path, paint); + + path.reset(); + + // Bottom and right highlights. Note that the shadows own the shared corner + // pixels, so reduce the sizes accordingly. + path.moveTo(SkIntToScalar(x + 1), SkFloatToScalar(y + size_ - 0.5f)); + + // Draw right to the bottom-right. + path.rLineTo(SkFloatToScalar(size_ - 1.5f), SkIntToScalar(0)); + + // Draw up to the top-right. + path.rLineTo(SkIntToScalar(0), SkFloatToScalar(-size_ + 1.5f)); + + paint.setColor(highlight_color); + canvas->DrawPath(path, paint); + } +} + +} // namespace internal gfx::Image GetSizedAvatarIconWithBorder(const gfx::Image& image, bool is_rectangle, @@ -20,21 +159,18 @@ gfx::Image GetSizedAvatarIconWithBorder(const gfx::Image& image, if (!is_rectangle) return image; - int length = std::min(width, height) - kAvatarIconBorder; - SkBitmap bmp = skia::ImageOperations::Resize( - *image.ToSkBitmap(), skia::ImageOperations::RESIZE_BEST, length, length); - gfx::Canvas canvas(gfx::Size(width, height), 1.0f, false); - - // Draw the icon centered on the canvas. - int x = (width - length) / 2; - int y = (height - length) / 2; - canvas.DrawImageInt(gfx::ImageSkia::CreateFrom1xBitmap(bmp), x, y); + gfx::Size size(width, height); - // Draw a gray border on the inside of the icon. - SkColor color = SkColorSetARGB(83, 0, 0, 0); - canvas.DrawRect(gfx::Rect(x, y, length - 1, length - 1), color); + // Source for a centered, sized icon with a border. + scoped_ptr<gfx::ImageSkiaSource> source( + new internal::AvatarImageSource( + *image.ToImageSkia(), + size, + std::min(width, height), + internal::AvatarImageSource::POSITION_CENTER, + internal::AvatarImageSource::BORDER_NORMAL)); - return gfx::Image(gfx::ImageSkia(canvas.ExtractImageRep())); + return gfx::Image(gfx::ImageSkia(source.release(), size)); } gfx::Image GetAvatarIconForMenu(const gfx::Image& image, @@ -48,19 +184,18 @@ gfx::Image GetAvatarIconForWebUI(const gfx::Image& image, if (!is_rectangle) return image; - int length = - std::min(kAvatarIconWidth, kAvatarIconHeight) - kAvatarIconBorder; - SkBitmap bmp = skia::ImageOperations::Resize( - *image.ToSkBitmap(), skia::ImageOperations::RESIZE_BEST, length, length); - gfx::Canvas canvas( - gfx::Size(kAvatarIconWidth, kAvatarIconHeight), 1.0f, false); + gfx::Size size(kAvatarIconWidth, kAvatarIconHeight); - // Draw the icon centered on the canvas. - int x = (kAvatarIconWidth - length) / 2; - int y = (kAvatarIconHeight - length) / 2; - canvas.DrawImageInt(gfx::ImageSkia::CreateFrom1xBitmap(bmp), x, y); + // Source for a centered, sized icon. + scoped_ptr<gfx::ImageSkiaSource> source( + new internal::AvatarImageSource( + *image.ToImageSkia(), + size, + std::min(kAvatarIconWidth, kAvatarIconHeight), + internal::AvatarImageSource::POSITION_CENTER, + internal::AvatarImageSource::BORDER_NONE)); - return gfx::Image(gfx::ImageSkia(canvas.ExtractImageRep())); + return gfx::Image(gfx::ImageSkia(source.release(), size)); } gfx::Image GetAvatarIconForTitleBar(const gfx::Image& image, @@ -70,35 +205,21 @@ gfx::Image GetAvatarIconForTitleBar(const gfx::Image& image, if (!is_rectangle) return image; - int length = std::min(std::min(kAvatarIconWidth, kAvatarIconHeight), - std::min(dst_width, dst_height)) - kAvatarIconBorder; - SkBitmap bmp = skia::ImageOperations::Resize( - *image.ToSkBitmap(), skia::ImageOperations::RESIZE_BEST, length, length); - gfx::Canvas canvas(gfx::Size(dst_width, dst_height), 1.0f, false); - - // Draw the icon on the bottom center of the canvas. - int x1 = (dst_width - length) / 2; - int x2 = x1 + length; - int y1 = dst_height - length - 1; - int y2 = y1 + length; - canvas.DrawImageInt(gfx::ImageSkia::CreateFrom1xBitmap(bmp), x1, y1); - - // Give the icon an etched look by drawing a highlight on the bottom edge - // and a shadow on the remaining edges. - SkColor highlight_color = SkColorSetARGB(128, 255, 255, 255); - SkColor shadow_color = SkColorSetARGB(83, 0, 0, 0); - // Bottom highlight. - canvas.DrawLine(gfx::Point(x1, y2 - 1), gfx::Point(x2, y2 - 1), - highlight_color); - // Top shadow. - canvas.DrawLine(gfx::Point(x1, y1), gfx::Point(x2, y1), shadow_color); - // Left shadow. - canvas.DrawLine(gfx::Point(x1, y1 + 1), gfx::Point(x1, y2 - 1), shadow_color); - // Right shadow. - canvas.DrawLine(gfx::Point(x2 - 1, y1 + 1), gfx::Point(x2 - 1, y2 - 1), - shadow_color); - - return gfx::Image(gfx::ImageSkia(canvas.ExtractImageRep())); + int size = std::min(std::min(kAvatarIconWidth, kAvatarIconHeight), + std::min(dst_width, dst_height)); + gfx::Size dst_size(dst_width, dst_height); + + // Source for a sized icon drawn at the bottom center of the canvas, + // with an etched border. + scoped_ptr<gfx::ImageSkiaSource> source( + new internal::AvatarImageSource( + *image.ToImageSkia(), + dst_size, + size, + internal::AvatarImageSource::POSITION_BOTTOM_CENTER, + internal::AvatarImageSource::BORDER_ETCHED)); + + return gfx::Image(gfx::ImageSkia(source.release(), dst_size)); } } // namespace profiles diff --git a/chrome/browser/profiles/profile_info_util.h b/chrome/browser/profiles/profile_info_util.h index da04a11fb0..ce1b78e4ee 100644 --- a/chrome/browser/profiles/profile_info_util.h +++ b/chrome/browser/profiles/profile_info_util.h @@ -11,7 +11,7 @@ namespace profiles { extern const int kAvatarIconWidth; extern const int kAvatarIconHeight; -extern const int kAvatarIconBorder; +extern const int kAvatarIconPadding; // Returns a version of |image| of a specific size and with a grey border. // Note that no checks are done on the width/height so make sure they're diff --git a/chrome/browser/profiles/profile_info_util_unittest.cc b/chrome/browser/profiles/profile_info_util_unittest.cc index 47020ff873..786e5b7bd0 100644 --- a/chrome/browser/profiles/profile_info_util_unittest.cc +++ b/chrome/browser/profiles/profile_info_util_unittest.cc @@ -8,25 +8,50 @@ #include "grit/theme_resources.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/gfx/image/image_skia_rep.h" #include "ui/gfx/image/image_unittest_util.h" namespace { +// Helper function to check that the image is sized properly +// and supports multiple pixel densities. +void VerifyScaling(gfx::Image& image, gfx::Size& size) { + gfx::Size canvas_size(100, 100); + gfx::Canvas canvas(canvas_size, 1.0f, false); + gfx::Canvas canvas2(canvas_size, 2.0f, false); + + ASSERT_FALSE(gfx::test::IsEmpty(image)); + EXPECT_EQ(image.Size(), size); + + gfx::ImageSkia image_skia = *image.ToImageSkia(); + canvas.DrawImageInt(image_skia, 15, 10); + EXPECT_TRUE(image.ToImageSkia()->HasRepresentation(1.0f)); + + canvas2.DrawImageInt(image_skia, 15, 10); + EXPECT_TRUE(image.ToImageSkia()->HasRepresentation(2.0f)); +} + TEST(ProfileInfoUtilTest, SizedMenuIcon) { // Test that an avatar icon isn't changed. const gfx::Image& profile_image( ResourceBundle::GetSharedInstance().GetImageNamed(IDR_PROFILE_AVATAR_0)); gfx::Image result = profiles::GetSizedAvatarIconWithBorder(profile_image, false, 50, 50); + EXPECT_FALSE(gfx::test::IsEmpty(result)); EXPECT_TRUE(gfx::test::IsEqual(profile_image, result)); // Test that a rectangular picture (e.g., GAIA image) is changed. gfx::Image rect_picture(gfx::test::CreateImage()); + + gfx::Size size(30, 20); gfx::Image result2 = - profiles::GetSizedAvatarIconWithBorder(rect_picture, true, 50, 50); - EXPECT_FALSE(gfx::test::IsEmpty(result2)); - EXPECT_FALSE(gfx::test::IsEqual(rect_picture, result2)); + profiles::GetSizedAvatarIconWithBorder( + rect_picture, true, size.width(), size.height()); + + VerifyScaling(result2, size); } TEST(ProfileInfoUtilTest, MenuIcon) { @@ -39,9 +64,10 @@ TEST(ProfileInfoUtilTest, MenuIcon) { // Test that a rectangular picture is changed. gfx::Image rect_picture(gfx::test::CreateImage()); + gfx::Size size(profiles::kAvatarIconWidth, profiles::kAvatarIconHeight); gfx::Image result2 = profiles::GetAvatarIconForMenu(rect_picture, true); - EXPECT_FALSE(gfx::test::IsEmpty(result2)); - EXPECT_FALSE(gfx::test::IsEqual(rect_picture, result2)); + + VerifyScaling(result2, size); } TEST(ProfileInfoUtilTest, WebUIIcon) { @@ -54,26 +80,32 @@ TEST(ProfileInfoUtilTest, WebUIIcon) { // Test that a rectangular picture is changed. gfx::Image rect_picture(gfx::test::CreateImage()); + gfx::Size size(profiles::kAvatarIconWidth, profiles::kAvatarIconHeight); gfx::Image result2 = profiles::GetAvatarIconForWebUI(rect_picture, true); - EXPECT_FALSE(gfx::test::IsEmpty(result2)); - EXPECT_FALSE(gfx::test::IsEqual(rect_picture, result2)); + + VerifyScaling(result2, size); } TEST(ProfileInfoUtilTest, TitleBarIcon) { + int width = 100; + int height = 40; + // Test that an avatar icon isn't changed. const gfx::Image& profile_image( ResourceBundle::GetSharedInstance().GetImageNamed(IDR_PROFILE_AVATAR_0)); gfx::Image result = profiles::GetAvatarIconForTitleBar( - profile_image, false, 100, 40); + profile_image, false, width, height); EXPECT_FALSE(gfx::test::IsEmpty(result)); EXPECT_TRUE(gfx::test::IsEqual(profile_image, result)); // Test that a rectangular picture is changed. gfx::Image rect_picture(gfx::test::CreateImage()); + + gfx::Size size(width, height); gfx::Image result2 = profiles::GetAvatarIconForTitleBar( - rect_picture, true, 100, 40); - EXPECT_FALSE(gfx::test::IsEmpty(result2)); - EXPECT_FALSE(gfx::test::IsEqual(rect_picture, result2)); + rect_picture, true, width, height); + + VerifyScaling(result2, size); } } // namespace diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc index 5256afc691..43ca4f9008 100644 --- a/chrome/browser/profiles/profile_manager.cc +++ b/chrome/browser/profiles/profile_manager.cc @@ -552,6 +552,7 @@ bool ProfileManager::AddProfile(Profile* profile) { } RegisterProfile(profile, true); + InitProfileUserPrefs(profile); DoFinalInit(profile, ShouldGoOffTheRecord(profile)); return true; } @@ -623,7 +624,10 @@ void ProfileManager::Observe( DCHECK(browser); Profile* profile = browser->profile(); DCHECK(profile); - if (!profile->IsOffTheRecord() && ++browser_counts_[profile] == 1) { + bool is_ephemeral = + profile->GetPrefs()->GetBoolean(prefs::kForceEphemeralProfiles); + if (!profile->IsOffTheRecord() && !is_ephemeral && + ++browser_counts_[profile] == 1) { active_profiles_.push_back(profile); save_active_profiles = true; } @@ -719,6 +723,12 @@ void ProfileManager::BrowserListObserver::OnBrowserSetLastActive( return; Profile* last_active = browser->profile(); + + // Don't remember ephemeral profiles as last because they are not going to + // persist after restart. + if (last_active->GetPrefs()->GetBoolean(prefs::kForceEphemeralProfiles)) + return; + PrefService* local_state = g_browser_process->local_state(); DCHECK(local_state); // Only keep track of profiles that we are managing; tests may create others. @@ -731,7 +741,6 @@ void ProfileManager::BrowserListObserver::OnBrowserSetLastActive( #endif // !defined(OS_ANDROID) && !defined(OS_IOS) void ProfileManager::DoFinalInit(Profile* profile, bool go_off_the_record) { - InitProfileUserPrefs(profile); DoFinalInitForServices(profile, go_off_the_record); AddProfileToCache(profile); DoFinalInitLogging(profile); @@ -1013,8 +1022,14 @@ void ProfileManager::InitProfileUserPrefs(Profile* profile) { if (!profile->GetPrefs()->HasPrefPath(prefs::kProfileName)) profile->GetPrefs()->SetString(prefs::kProfileName, profile_name); - if (!profile->GetPrefs()->HasPrefPath(prefs::kManagedUserId)) + if (!profile->GetPrefs()->HasPrefPath(prefs::kManagedUserId)) { + if (managed_user_id.empty() && + CommandLine::ForCurrentProcess()->HasSwitch( + switches::kNewProfileIsSupervised)) { + managed_user_id = "Test ID"; + } profile->GetPrefs()->SetString(prefs::kManagedUserId, managed_user_id); + } } void ProfileManager::SetGuestProfilePrefs(Profile* profile) { diff --git a/chrome/browser/profiles/profile_manager.h b/chrome/browser/profiles/profile_manager.h index f84ac9565f..a8674de09b 100644 --- a/chrome/browser/profiles/profile_manager.h +++ b/chrome/browser/profiles/profile_manager.h @@ -193,6 +193,10 @@ class ProfileManager : public base::NonThreadSafe, // Sign-Out a profile against use until re-authentication. void SignOutProfile(Profile* profile); + // Initializes user prefs of |profile|. This includes profile name and + // avatar values. + void InitProfileUserPrefs(Profile* profile); + // Register and add testing profile to the ProfileManager. Use ONLY in tests. // This allows the creation of Profiles outside of the standard creation path // for testing. If |addToCache|, adds to ProfileInfoCache as well. @@ -277,10 +281,6 @@ class ProfileManager : public base::NonThreadSafe, // Adds |profile| to the profile info cache if it hasn't been added yet. void AddProfileToCache(Profile* profile); - // Initializes user prefs of |profile|. This includes profile name and - // avatar values - void InitProfileUserPrefs(Profile* profile); - // Apply settings for (desktop) Guest User profile. void SetGuestProfilePrefs(Profile* profile); diff --git a/chrome/browser/profiles/profile_manager_unittest.cc b/chrome/browser/profiles/profile_manager_unittest.cc index 87dd48894c..de575a98b7 100644 --- a/chrome/browser/profiles/profile_manager_unittest.cc +++ b/chrome/browser/profiles/profile_manager_unittest.cc @@ -620,6 +620,85 @@ TEST_F(ProfileManagerTest, LastOpenedProfilesDoesNotContainIncognito) { #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS) // There's no Browser object on Android and there's no multi-profiles on Chrome. +TEST_F(ProfileManagerTest, EphemeralProfilesDontEndUpAsLastProfile) { + base::FilePath dest_path = temp_dir_.path(); + dest_path = dest_path.Append(FILE_PATH_LITERAL("Ephemeral Profile")); + + ProfileManager* profile_manager = g_browser_process->profile_manager(); + + TestingProfile* profile = + static_cast<TestingProfile*>(profile_manager->GetProfile(dest_path)); + ASSERT_TRUE(profile); + profile->GetPrefs()->SetBoolean(prefs::kForceEphemeralProfiles, true); + + // Here the last used profile is still the "Default" profile. + Profile* last_used_profile = profile_manager->GetLastUsedProfile(); + EXPECT_NE(profile, last_used_profile); + + // Create a browser for profile2. + Browser::CreateParams profile_params(profile, chrome::GetActiveDesktop()); + scoped_ptr<Browser> browser( + chrome::CreateBrowserWithTestWindowForParams(&profile_params)); + last_used_profile = profile_manager->GetLastUsedProfile(); + EXPECT_NE(profile, last_used_profile); + + // Close the browser. + browser.reset(); + last_used_profile = profile_manager->GetLastUsedProfile(); + EXPECT_NE(profile, last_used_profile); +} + +TEST_F(ProfileManagerTest, EphemeralProfilesDontEndUpAsLastOpenedAtShutdown) { + base::FilePath dest_path1 = temp_dir_.path(); + dest_path1 = dest_path1.Append(FILE_PATH_LITERAL("Normal Profile")); + + base::FilePath dest_path2 = temp_dir_.path(); + dest_path2 = dest_path2.Append(FILE_PATH_LITERAL("Ephemeral Profile")); + + ProfileManager* profile_manager = g_browser_process->profile_manager(); + + // Successfully create the profiles. + TestingProfile* normal_profile = + static_cast<TestingProfile*>(profile_manager->GetProfile(dest_path1)); + ASSERT_TRUE(normal_profile); + + // Add one ephemeral profile which should not end up in this list. + TestingProfile* ephemeral_profile = + static_cast<TestingProfile*>(profile_manager->GetProfile(dest_path2)); + ASSERT_TRUE(ephemeral_profile); + ephemeral_profile->GetPrefs()->SetBoolean(prefs::kForceEphemeralProfiles, + true); + + // Create a browser for profile1. + Browser::CreateParams profile1_params(normal_profile, + chrome::GetActiveDesktop()); + scoped_ptr<Browser> browser1( + chrome::CreateBrowserWithTestWindowForParams(&profile1_params)); + + // Create a browser for the ephemeral profile. + Browser::CreateParams profile2_params(ephemeral_profile, + chrome::GetActiveDesktop()); + scoped_ptr<Browser> browser2( + chrome::CreateBrowserWithTestWindowForParams(&profile2_params)); + + std::vector<Profile*> last_opened_profiles = + profile_manager->GetLastOpenedProfiles(); + ASSERT_EQ(1U, last_opened_profiles.size()); + EXPECT_EQ(normal_profile, last_opened_profiles[0]); + + // Simulate a shutdown. + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST, + content::NotificationService::AllSources(), + content::NotificationService::NoDetails()); + browser1.reset(); + browser2.reset(); + + last_opened_profiles = profile_manager->GetLastOpenedProfiles(); + ASSERT_EQ(1U, last_opened_profiles.size()); + EXPECT_EQ(normal_profile, last_opened_profiles[0]); +} + TEST_F(ProfileManagerTest, ActiveProfileDeleted) { ProfileManager* profile_manager = g_browser_process->profile_manager(); ASSERT_TRUE(profile_manager); diff --git a/chrome/browser/profiles/profiles_state.cc b/chrome/browser/profiles/profiles_state.cc index 72e1295ba8..316527136f 100644 --- a/chrome/browser/profiles/profiles_state.cc +++ b/chrome/browser/profiles/profiles_state.cc @@ -7,9 +7,16 @@ #include "base/command_line.h" #include "base/files/file_path.h" #include "base/prefs/pref_registry_simple.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_info_cache.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/ui/browser.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" +#include "grit/generated_resources.h" +#include "ui/base/l10n/l10n_util.h" #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/login/user_manager.h" @@ -33,8 +40,7 @@ bool IsNewProfileManagementEnabled() { switches::kNewProfileManagement); } -base::FilePath GetDefaultProfileDir( - const base::FilePath& user_data_dir) { +base::FilePath GetDefaultProfileDir(const base::FilePath& user_data_dir) { base::FilePath default_profile_dir(user_data_dir); default_profile_dir = default_profile_dir.AppendASCII(chrome::kInitialProfile); @@ -54,4 +60,20 @@ void RegisterPrefs(PrefRegistrySimple* registry) { registry->RegisterListPref(prefs::kProfilesLastActive); } +string16 GetActiveProfileDisplayName(Browser* browser) { + string16 profile_name; + Profile* profile = browser->profile(); + + if (profile->IsGuestSession()) { + profile_name = l10n_util::GetStringUTF16(IDS_GUEST_PROFILE_NAME); + } else { + ProfileInfoCache& cache = + g_browser_process->profile_manager()->GetProfileInfoCache(); + size_t index = cache.GetIndexOfProfileWithPath(profile->GetPath()); + if (index != std::string::npos) + profile_name = cache.GetNameOfProfileAtIndex(index); + } + return profile_name; +} + } // namespace profiles diff --git a/chrome/browser/profiles/profiles_state.h b/chrome/browser/profiles/profiles_state.h index 2858e62cdb..3f6debcf00 100644 --- a/chrome/browser/profiles/profiles_state.h +++ b/chrome/browser/profiles/profiles_state.h @@ -5,6 +5,9 @@ #ifndef CHROME_BROWSER_PROFILES_PROFILES_STATE_H_ #define CHROME_BROWSER_PROFILES_PROFILES_STATE_H_ +#include "base/strings/string16.h" + +class Browser; class PrefRegistrySimple; namespace base { class FilePath; } @@ -26,6 +29,9 @@ base::FilePath GetProfilePrefsPath(const base::FilePath& profile_dir); // Register multi-profile related preferences in Local State. void RegisterPrefs(PrefRegistrySimple* registry); +// Returns the display name of the active on-the-record profile (or guest). +string16 GetActiveProfileDisplayName(Browser* browser); + } // namespace profiles #endif // CHROME_BROWSER_PROFILES_PROFILES_STATE_H_ diff --git a/chrome/browser/renderer_host/chrome_render_view_host_observer.cc b/chrome/browser/renderer_host/chrome_render_view_host_observer.cc deleted file mode 100644 index 2a6f67a7fe..0000000000 --- a/chrome/browser/renderer_host/chrome_render_view_host_observer.cc +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright (c) 2012 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/renderer_host/chrome_render_view_host_observer.h" - -#include <vector> - -#include "base/command_line.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" -#include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/extensions/extension_system.h" -#include "chrome/browser/net/predictor.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/search_engines/search_terms_data.h" -#include "chrome/browser/search_engines/template_url.h" -#include "chrome/browser/search_engines/template_url_service.h" -#include "chrome/browser/search_engines/template_url_service_factory.h" -#include "chrome/common/chrome_switches.h" -#include "chrome/common/extensions/extension.h" -#include "chrome/common/extensions/extension_messages.h" -#include "chrome/common/render_messages.h" -#include "chrome/common/url_constants.h" -#include "content/public/browser/child_process_security_policy.h" -#include "content/public/browser/page_navigator.h" -#include "content/public/browser/render_process_host.h" -#include "content/public/browser/render_view_host.h" -#include "content/public/browser/site_instance.h" -#include "content/public/browser/web_contents.h" -#include "content/public/common/page_transition_types.h" -#include "extensions/common/constants.h" -#include "extensions/common/manifest.h" -#include "net/http/http_request_headers.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "ui/base/window_open_disposition.h" -#include "ui/gfx/codec/jpeg_codec.h" -#include "ui/gfx/size.h" - -#if defined(OS_WIN) -#include "base/win/win_util.h" -#endif // OS_WIN - -using content::ChildProcessSecurityPolicy; -using content::OpenURLParams; -using content::RenderViewHost; -using content::SiteInstance; -using content::WebContents; -using extensions::Extension; -using extensions::Manifest; - -ChromeRenderViewHostObserver::ChromeRenderViewHostObserver( - RenderViewHost* render_view_host, chrome_browser_net::Predictor* predictor) - : content::RenderViewHostObserver(render_view_host), - predictor_(predictor) { - SiteInstance* site_instance = render_view_host->GetSiteInstance(); - profile_ = Profile::FromBrowserContext( - site_instance->GetBrowserContext()); - - InitRenderViewForExtensions(); -} - -ChromeRenderViewHostObserver::~ChromeRenderViewHostObserver() { - if (render_view_host()) - RemoveRenderViewHostForExtensions(render_view_host()); -} - -void ChromeRenderViewHostObserver::RenderViewHostInitialized() { - // This reinitializes some state in the case where a render process crashes - // but we keep the same RenderViewHost instance. - InitRenderViewForExtensions(); -} - -void ChromeRenderViewHostObserver::RenderViewHostDestroyed( - RenderViewHost* rvh) { - RemoveRenderViewHostForExtensions(rvh); - delete this; -} - -void ChromeRenderViewHostObserver::Navigate(const GURL& url) { - if (!predictor_) - return; - if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kChromeFrame) && - (url.SchemeIs(content::kHttpScheme) || - url.SchemeIs(content::kHttpsScheme))) - predictor_->PreconnectUrlAndSubresources(url, GURL()); -} - -bool ChromeRenderViewHostObserver::OnMessageReceived( - const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(ChromeRenderViewHostObserver, message) - IPC_MESSAGE_HANDLER(ChromeViewHostMsg_FocusedNodeTouched, - OnFocusedNodeTouched) - IPC_MESSAGE_HANDLER(ChromeViewHostMsg_RequestThumbnailForContextNode_ACK, - OnRequestThumbnailForContextNodeACK) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; -} - -void ChromeRenderViewHostObserver::InitRenderViewForExtensions() { - const Extension* extension = GetExtension(); - if (!extension) - return; - - content::RenderProcessHost* process = render_view_host()->GetProcess(); - - // Some extensions use chrome:// URLs. - // This is a temporary solution. Replace it with access to chrome-static:// - // once it is implemented. See: crbug.com/226927. - Manifest::Type type = extension->GetType(); - if (type == Manifest::TYPE_EXTENSION || - type == Manifest::TYPE_LEGACY_PACKAGED_APP || - (type == Manifest::TYPE_PLATFORM_APP && - extension->location() == Manifest::COMPONENT)) { - ChildProcessSecurityPolicy::GetInstance()->GrantScheme( - process->GetID(), chrome::kChromeUIScheme); - } - - // Some extensions use file:// URLs. - if (type == Manifest::TYPE_EXTENSION || - type == Manifest::TYPE_LEGACY_PACKAGED_APP) { - if (extensions::ExtensionSystem::Get(profile_)->extension_service()-> - extension_prefs()->AllowFileAccess(extension->id())) { - ChildProcessSecurityPolicy::GetInstance()->GrantScheme( - process->GetID(), chrome::kFileScheme); - } - } - - switch (type) { - case Manifest::TYPE_EXTENSION: - case Manifest::TYPE_USER_SCRIPT: - case Manifest::TYPE_HOSTED_APP: - case Manifest::TYPE_LEGACY_PACKAGED_APP: - case Manifest::TYPE_PLATFORM_APP: - // Always send a Loaded message before ActivateExtension so that - // ExtensionDispatcher knows what Extension is active, not just its ID. - // This is important for classifying the Extension's JavaScript context - // correctly (see ExtensionDispatcher::ClassifyJavaScriptContext). - Send(new ExtensionMsg_Loaded( - std::vector<ExtensionMsg_Loaded_Params>( - 1, ExtensionMsg_Loaded_Params(extension)))); - Send(new ExtensionMsg_ActivateExtension(extension->id())); - break; - - case Manifest::TYPE_UNKNOWN: - case Manifest::TYPE_THEME: - case Manifest::TYPE_SHARED_MODULE: - break; - } -} - -const Extension* ChromeRenderViewHostObserver::GetExtension() { - // Note that due to ChromeContentBrowserClient::GetEffectiveURL(), hosted apps - // (excluding bookmark apps) will have a chrome-extension:// URL for their - // site, so we can ignore that wrinkle here. - SiteInstance* site_instance = render_view_host()->GetSiteInstance(); - const GURL& site = site_instance->GetSiteURL(); - - if (!site.SchemeIs(extensions::kExtensionScheme)) - return NULL; - - ExtensionService* service = - extensions::ExtensionSystem::Get(profile_)->extension_service(); - if (!service) - return NULL; - - // Reload the extension if it has crashed. - // TODO(yoz): This reload doesn't happen synchronously for unpacked - // extensions. It seems to be fast enough, but there is a race. - // We should delay loading until the extension has reloaded. - if (service->GetTerminatedExtension(site.host())) - service->ReloadExtension(site.host()); - - // May be null if the extension doesn't exist, for example if somebody typos - // a chrome-extension:// URL. - return service->extensions()->GetByID(site.host()); -} - -void ChromeRenderViewHostObserver::RemoveRenderViewHostForExtensions( - RenderViewHost* rvh) { - ExtensionProcessManager* process_manager = - extensions::ExtensionSystem::Get(profile_)->process_manager(); - if (process_manager) - process_manager->UnregisterRenderViewHost(rvh); -} - -void ChromeRenderViewHostObserver::OnFocusedNodeTouched(bool editable) { -#if defined(OS_WIN) && defined(USE_AURA) - if (editable) { - base::win::DisplayVirtualKeyboard(); - } else { - base::win::DismissVirtualKeyboard(); - } -#endif // OS_WIN && USE_AURA -} - -// Handles the image thumbnail for the context node, composes a image search -// request based on the received thumbnail and opens the request in a new tab. -void ChromeRenderViewHostObserver::OnRequestThumbnailForContextNodeACK( - const SkBitmap& bitmap, - const gfx::Size& original_size) { - if (bitmap.isNull()) - return; - WebContents* web_contents = - WebContents::FromRenderViewHost(render_view_host()); - const TemplateURL* const default_provider = - TemplateURLServiceFactory::GetForProfile(profile_)-> - GetDefaultSearchProvider(); - if (!web_contents || !default_provider) - return; - - const int kDefaultQualityForImageSearch = 90; - std::vector<unsigned char> data; - if (!gfx::JPEGCodec::Encode( - reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)), - gfx::JPEGCodec::FORMAT_SkBitmap, bitmap.width(), bitmap.height(), - static_cast<int>(bitmap.rowBytes()), kDefaultQualityForImageSearch, - &data)) - return; - - TemplateURLRef::SearchTermsArgs search_args = - TemplateURLRef::SearchTermsArgs(base::string16()); - search_args.image_thumbnail_content = std::string(data.begin(), data.end()); - // TODO(jnd): Add a method in WebContentsViewDelegate to get the image URL - // from the ContextMenuParams which creates current context menu. - search_args.image_url = GURL(); - search_args.image_original_size = original_size; - TemplateURLRef::PostContent post_content; - GURL result(default_provider->image_url_ref().ReplaceSearchTerms( - search_args, &post_content)); - if (!result.is_valid()) - return; - - OpenURLParams open_url_params(result, content::Referrer(), NEW_FOREGROUND_TAB, - content::PAGE_TRANSITION_LINK, false); - const std::string& content_type = post_content.first; - std::string* post_data = &post_content.second; - if (!post_data->empty()) { - DCHECK(!content_type.empty()); - open_url_params.uses_post = true; - open_url_params.browser_initiated_post_data = - base::RefCountedString::TakeString(post_data); - open_url_params.extra_headers += base::StringPrintf( - "%s: %s\r\n", net::HttpRequestHeaders::kContentType, - content_type.c_str()); - } - web_contents->OpenURL(open_url_params); -} diff --git a/chrome/browser/renderer_host/chrome_render_view_host_observer.h b/chrome/browser/renderer_host/chrome_render_view_host_observer.h deleted file mode 100644 index 213fc0d582..0000000000 --- a/chrome/browser/renderer_host/chrome_render_view_host_observer.h +++ /dev/null @@ -1,61 +0,0 @@ -// 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_RENDERER_HOST_CHROME_RENDER_VIEW_HOST_OBSERVER_H_ -#define CHROME_BROWSER_RENDERER_HOST_CHROME_RENDER_VIEW_HOST_OBSERVER_H_ - -#include <string> - -#include "content/public/browser/render_view_host_observer.h" - -class Profile; -class SkBitmap; - -namespace chrome_browser_net { -class Predictor; -} - -namespace extensions { -class Extension; -} - -namespace gfx { -class Size; -} - -// This class holds the Chrome specific parts of RenderViewHost, and has the -// same lifetime. -class ChromeRenderViewHostObserver : public content::RenderViewHostObserver { - public: - ChromeRenderViewHostObserver(content::RenderViewHost* render_view_host, - chrome_browser_net::Predictor* predictor); - virtual ~ChromeRenderViewHostObserver(); - - // content::RenderViewHostObserver overrides. - virtual void RenderViewHostInitialized() OVERRIDE; - virtual void RenderViewHostDestroyed(content::RenderViewHost* rvh) OVERRIDE; - virtual void Navigate(const GURL& url) OVERRIDE; - virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; - - private: - // Does extension-specific initialization when a new renderer process is - // created by a RenderViewHost. - void InitRenderViewForExtensions(); - // Gets the extension or app (if any) that is associated with the RVH. - const extensions::Extension* GetExtension(); - // Cleans up when a RenderViewHost is removed, or on destruction. - void RemoveRenderViewHostForExtensions(content::RenderViewHost* rvh); - - void OnFocusedNodeTouched(bool editable); - - void OnRequestThumbnailForContextNodeACK(const SkBitmap& bitmap, - const gfx::Size& original_size); - - Profile* profile_; - chrome_browser_net::Predictor* predictor_; - - DISALLOW_COPY_AND_ASSIGN(ChromeRenderViewHostObserver); -}; - -#endif // CHROME_BROWSER_RENDERER_HOST_CHROME_RENDER_VIEW_HOST_OBSERVER_H_ diff --git a/chrome/browser/renderer_host/pepper/device_id_fetcher.cc b/chrome/browser/renderer_host/pepper/device_id_fetcher.cc index 42754fa3a2..5f3a1c22e1 100644 --- a/chrome/browser/renderer_host/pepper/device_id_fetcher.cc +++ b/chrome/browser/renderer_host/pepper/device_id_fetcher.cc @@ -10,7 +10,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/common/pref_names.h" #if defined(OS_CHROMEOS) -#include "chromeos/cryptohome/cryptohome_library.h" +#include "chromeos/cryptohome/system_salt_getter.h" #endif #include "components/user_prefs/pref_registry_syncable.h" #include "content/public/browser/browser_context.h" @@ -42,7 +42,7 @@ void GetMachineIDAsync(const DeviceIDFetcher::IDCallback& callback) { #if defined(OS_WIN) && defined(ENABLE_RLZ) rlz_lib::GetMachineId(&result); #elif defined(OS_CHROMEOS) - result = chromeos::CryptohomeLibrary::Get()->GetSystemSaltSync(); + result = chromeos::SystemSaltGetter::Get()->GetSystemSaltSync(); if (result.empty()) { // cryptohome must not be running; re-request after a delay. const int64 kRequestSystemSaltDelayMs = 500; diff --git a/chrome/browser/renderer_host/pepper/pepper_extensions_common_message_filter.cc b/chrome/browser/renderer_host/pepper/pepper_extensions_common_message_filter.cc index e7661283a9..1ce27bfa4e 100644 --- a/chrome/browser/renderer_host/pepper/pepper_extensions_common_message_filter.cc +++ b/chrome/browser/renderer_host/pepper/pepper_extensions_common_message_filter.cc @@ -15,7 +15,8 @@ #include "content/public/browser/browser_ppapi_host.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_view_host.h" -#include "content/public/browser/render_view_host_observer.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_observer.h" #include "extensions/common/constants.h" #include "ipc/ipc_message.h" #include "ipc/ipc_message_macros.h" @@ -27,13 +28,15 @@ namespace chrome { class PepperExtensionsCommonMessageFilter::DispatcherOwner - : public content::RenderViewHostObserver, + : public content::WebContentsObserver, public ExtensionFunctionDispatcher::Delegate { public: DispatcherOwner(PepperExtensionsCommonMessageFilter* message_filter, Profile* profile, - content::RenderViewHost* view_host) - : content::RenderViewHostObserver(view_host), + content::RenderViewHost* view_host, + content::WebContents* web_contents) + : content::WebContentsObserver(web_contents), + render_view_host_(view_host), message_filter_(message_filter), dispatcher_(profile, this) { } @@ -42,6 +45,13 @@ class PepperExtensionsCommonMessageFilter::DispatcherOwner message_filter_->DetachDispatcherOwner(); } + // content::WebContentsObserver implementation. + virtual void RenderViewDeleted( + content::RenderViewHost* render_view_host) OVERRIDE { + if (render_view_host == render_view_host_) + delete this; + } + // ExtensionFunctionDispatcher::Delegate implementation. virtual extensions::WindowController* GetExtensionWindowController( ) const OVERRIDE { @@ -55,11 +65,10 @@ class PepperExtensionsCommonMessageFilter::DispatcherOwner } ExtensionFunctionDispatcher* dispatcher() { return &dispatcher_; } - content::RenderViewHost* render_view_host() { - return content::RenderViewHostObserver::render_view_host(); - } + content::RenderViewHost* render_view_host() { return render_view_host_; } private: + content::RenderViewHost* render_view_host_; scoped_refptr<PepperExtensionsCommonMessageFilter> message_filter_; ExtensionFunctionDispatcher dispatcher_; @@ -154,6 +163,8 @@ void PepperExtensionsCommonMessageFilter::EnsureDispatcherOwnerInitialized() { render_process_id_, render_view_id_); if (!view_host) return; + content::WebContents* web_contents = + content::WebContents::FromRenderViewHost(view_host); if (!document_url_.SchemeIs(extensions::kExtensionScheme)) return; @@ -164,7 +175,8 @@ void PepperExtensionsCommonMessageFilter::EnsureDispatcherOwnerInitialized() { Profile* profile = profile_manager->GetProfile(profile_directory_); // It will be automatically destroyed when |view_host| goes away. - dispatcher_owner_ = new DispatcherOwner(this, profile, view_host); + dispatcher_owner_ = new DispatcherOwner( + this, profile, view_host, web_contents); } void PepperExtensionsCommonMessageFilter::DetachDispatcherOwner() { diff --git a/chrome/browser/renderer_host/pepper/pepper_platform_verification_message_filter.cc b/chrome/browser/renderer_host/pepper/pepper_platform_verification_message_filter.cc index 23c2b7a29b..e51c4e519f 100644 --- a/chrome/browser/renderer_host/pepper/pepper_platform_verification_message_filter.cc +++ b/chrome/browser/renderer_host/pepper/pepper_platform_verification_message_filter.cc @@ -43,9 +43,6 @@ int32_t PepperPlatformVerificationMessageFilter::OnResourceMessageReceived( DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); IPC_BEGIN_MESSAGE_MAP(PepperPlatformVerificationMessageFilter, msg) - PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( - PpapiHostMsg_PlatformVerification_CanChallengePlatform, - OnCanChallengePlatform) PPAPI_DISPATCH_HOST_RESOURCE_CALL( PpapiHostMsg_PlatformVerification_ChallengePlatform, OnChallengePlatform) @@ -54,18 +51,6 @@ int32_t PepperPlatformVerificationMessageFilter::OnResourceMessageReceived( return PP_ERROR_FAILED; } -int32_t PepperPlatformVerificationMessageFilter::OnCanChallengePlatform( - ppapi::host::HostMessageContext* context) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - if (!pv_) - pv_.reset(new PlatformVerificationFlow()); - pv_->CheckPlatformState(base::Bind( - &PepperPlatformVerificationMessageFilter::CanChallengePlatformCallback, - this, - context->MakeReplyMessageContext())); - return PP_OK_COMPLETIONPENDING; -} - int32_t PepperPlatformVerificationMessageFilter::OnChallengePlatform( ppapi::host::HostMessageContext* context, const std::string& service_id, @@ -101,16 +86,6 @@ int32_t PepperPlatformVerificationMessageFilter::OnChallengePlatform( return PP_OK_COMPLETIONPENDING; } -void PepperPlatformVerificationMessageFilter::CanChallengePlatformCallback( - ppapi::host::ReplyMessageContext reply_context, - bool can_challenge_platform) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - reply_context.params.set_result(PP_OK); - SendReply(reply_context, - PpapiHostMsg_PlatformVerification_CanChallengePlatformReply( - can_challenge_platform)); -} - void PepperPlatformVerificationMessageFilter::ChallengePlatformCallback( ppapi::host::ReplyMessageContext reply_context, chromeos::attestation::PlatformVerificationFlow::Result challenge_result, diff --git a/chrome/browser/renderer_host/pepper/pepper_platform_verification_message_filter.h b/chrome/browser/renderer_host/pepper/pepper_platform_verification_message_filter.h index f13d3203c4..a5ffe5074c 100644 --- a/chrome/browser/renderer_host/pepper/pepper_platform_verification_message_filter.h +++ b/chrome/browser/renderer_host/pepper/pepper_platform_verification_message_filter.h @@ -38,15 +38,11 @@ class PepperPlatformVerificationMessageFilter const IPC::Message& msg, ppapi::host::HostMessageContext* context) OVERRIDE; - int32_t OnCanChallengePlatform(ppapi::host::HostMessageContext* context); int32_t OnChallengePlatform(ppapi::host::HostMessageContext* context, const std::string& service_id, const std::vector<uint8_t>& challenge); // PlatformVerificationFlow callbacks. - void CanChallengePlatformCallback( - ppapi::host::ReplyMessageContext reply_context, - bool can_challenge_platform); void ChallengePlatformCallback( ppapi::host::ReplyMessageContext reply_context, chromeos::attestation::PlatformVerificationFlow::Result challenge_result, diff --git a/chrome/browser/resources/chromeos/about_sys.html b/chrome/browser/resources/chromeos/about_sys.html index 4626fe9810..392bd36646 100644 --- a/chrome/browser/resources/chromeos/about_sys.html +++ b/chrome/browser/resources/chromeos/about_sys.html @@ -22,8 +22,7 @@ <p id="status"></p> <table class="list" id="details"> <tr jsselect="details"> - <td class="name"<div - jscontent="statName"></div></td> + <td class="name"><div jscontent="statName"></div></td> <td class="button-cell"><button jsvalues="id:statName + '-value-btn'" class="expand-status"></button></td> <td class="number"><div class="stat-value" jscontent="statValue" diff --git a/chrome/browser/resources/chromeos/cryptohome.html b/chrome/browser/resources/chromeos/cryptohome.html index 5743596da2..c6f57654a0 100644 --- a/chrome/browser/resources/chromeos/cryptohome.html +++ b/chrome/browser/resources/chromeos/cryptohome.html @@ -10,7 +10,7 @@ <div id="refresh-message"> (To auto-refresh this page: about:cryptohome/<secs>) </div> - <h3>CryptohomeLibrary:</h3> + <h3>Cryptohome:</h3> <table> <tr> <td>IsMounted</td> diff --git a/chrome/browser/resources/chromeos/diagnostics/main.js b/chrome/browser/resources/chromeos/diagnostics/main.js index 09f27818c3..d8ccf5b9bc 100644 --- a/chrome/browser/resources/chromeos/diagnostics/main.js +++ b/chrome/browser/resources/chromeos/diagnostics/main.js @@ -24,10 +24,12 @@ cr.define('diag', function() { * List of network adapter types. */ DiagPage.AdapterType = [ - {adapter: 'wlan0', name: localStrings.getString('wlan0'), kind: 'wifi'}, - {adapter: 'eth0', name: localStrings.getString('eth0'), kind: 'ethernet'}, - {adapter: 'eth1', name: localStrings.getString('eth1'), kind: 'ethernet'}, - {adapter: 'wwan0', name: localStrings.getString('wwan0'), kind: '3g'}, + {adapter: 'wifi', name: localStrings.getString('wifi'), kind: 'wifi'}, + {adapter: 'ethernet', name: localStrings.getString('ethernet1'), + kind: 'ethernet'}, + {adapter: 'ethernet2', name: localStrings.getString('ethernet2'), + kind: 'ethernet'}, + {adapter: 'cellular', name: localStrings.getString('3g'), kind: '3g'}, ]; /** @@ -105,30 +107,49 @@ cr.define('diag', function() { }, /** - * Updates the connectivity status with netif information. - * @param {Object} netifStatus Dictionary of network adapter status. + * Updates the connectivity status with the device information. + * @param {Object} deviceStatus Dictionary of network adapter status. */ - setNetifStatus_: function(netifStatus) { + setDeviceStatus: function(deviceStatus) { // Hide the "loading" message and show the "choose-adapter" message. $('loading').hidden = true; $('choose-adapter').hidden = false; - // Update netif state. - var foundValidIp = false; + // Reset all adapters status. + var adapterLookup = {}; for (var i = 0; i < DiagPage.AdapterType.length; i++) { - var adapterType = DiagPage.AdapterType[i]; - var status = netifStatus[adapterType.adapter]; - if (!status) - this.adapterStatus_[i] = DiagPage.AdapterStatus.NOT_FOUND; - else if (!status.flags || status.flags.indexOf('up') == -1) - this.adapterStatus_[i] = DiagPage.AdapterStatus.DISABLED; - else if (!status.ipv4) - this.adapterStatus_[i] = DiagPage.AdapterStatus.NO_IP; - else - this.adapterStatus_[i] = DiagPage.AdapterStatus.VALID_IP; + this.adapterStatus_[i] = DiagPage.AdapterStatus.NOT_FOUND; + adapterLookup[DiagPage.AdapterType[i].adapter] = i; + } - if (this.adapterStatus_[i] == DiagPage.AdapterStatus.VALID_IP) - foundValidIp = true; + // Update adapter status from data. + var foundValidIp = false; + for (var devicePath in deviceStatus) { + var device = deviceStatus[devicePath]; + var type = device['Type']; + var idx = adapterLookup[type]; + if (idx == null) { + console.warning('Unexpected adapter type: ' + type); + continue; + } + + // Special case for multiple ethernet adapters. + if (type == 'ethernet' && + this.adapterStatus_[idx] != DiagPage.AdapterStatus.NOT_FOUND) { + type = 'ethernet2'; + idx = adapterLookup[type]; + } + + this.adapterStatus_[idx] = device['Powered'] == true ? + DiagPage.AdapterStatus.NO_IP : DiagPage.AdapterStatus.DISABLED; + var ipconfigs = device['ipconfigs']; + for (var ipconfigPath in ipconfigs) { + var ipconfig = ipconfigs[ipconfigPath]; + if (ipconfig['Address']) { + this.adapterStatus_[idx] = DiagPage.AdapterStatus.VALID_IP; + foundValidIp = true; + } + } } // If we have valid IP, start ping test. @@ -313,8 +334,8 @@ cr.define('diag', function() { } }; - DiagPage.setNetifStatus = function(netifStatus) { - DiagPage.getInstance().setNetifStatus_(netifStatus); + DiagPage.setDeviceStatus = function(deviceStatus) { + DiagPage.getInstance().setDeviceStatus(deviceStatus); } DiagPage.setTestICMPStatus = function(testICMPStatus) { diff --git a/chrome/browser/resources/chromeos/drive_internals.html b/chrome/browser/resources/chromeos/drive_internals.html index 6b25ee5d6a..d1bf25500b 100644 --- a/chrome/browser/resources/chromeos/drive_internals.html +++ b/chrome/browser/resources/chromeos/drive_internals.html @@ -92,7 +92,7 @@ <table> <tbody id='cache-contents'> <tr> - <th>Resource ID</th> + <th>Local ID</th> <th>MD5</th> <th>Present</th> <th>Pinned</th> diff --git a/chrome/browser/resources/chromeos/drive_internals.js b/chrome/browser/resources/chromeos/drive_internals.js index 46c2fee688..c8736212e9 100644 --- a/chrome/browser/resources/chromeos/drive_internals.js +++ b/chrome/browser/resources/chromeos/drive_internals.js @@ -91,7 +91,7 @@ function updateFileSystemContents(directoryContentsAsText) { */ function updateCacheContents(cacheEntry) { var tr = document.createElement('tr'); - tr.appendChild(createElementFromText('td', cacheEntry.resource_id)); + tr.appendChild(createElementFromText('td', cacheEntry.local_id)); tr.appendChild(createElementFromText('td', cacheEntry.md5)); tr.appendChild(createElementFromText('td', cacheEntry.is_present)); tr.appendChild(createElementFromText('td', cacheEntry.is_pinned)); diff --git a/chrome/browser/resources/chromeos/login/bubble.js b/chrome/browser/resources/chromeos/login/bubble.js index 89a20f864a..cce6f8d23e 100644 --- a/chrome/browser/resources/chromeos/login/bubble.js +++ b/chrome/browser/resources/chromeos/login/bubble.js @@ -50,6 +50,8 @@ cr.define('cr.ui', function() { window.addEventListener('blur', this.handleWindowBlur_.bind(this)); this.addEventListener('webkitTransitionEnd', this.handleTransitionEnd_.bind(this)); + // Guard timer for 200ms + epsilon. + ensureTransitionEndEvent(this, 250); }, /** diff --git a/chrome/browser/resources/chromeos/login/header_bar.js b/chrome/browser/resources/chromeos/login/header_bar.js index 82a04c4867..06e7434da3 100644 --- a/chrome/browser/resources/chromeos/login/header_bar.js +++ b/chrome/browser/resources/chromeos/login/header_bar.js @@ -222,6 +222,9 @@ cr.define('login', function() { launcher.removeEventListener('webkitTransitionEnd', f); callback(); }); + // Guard timer for 2 seconds + 200 ms + epsilon. + ensureTransitionEndEvent(launcher, 2250); + this.classList.remove('login-header-bar-animate-slow'); this.classList.add('login-header-bar-animate-fast'); this.classList.add('login-header-bar-hidden'); diff --git a/chrome/browser/resources/chromeos/login/screen_locally_managed_user_creation.css b/chrome/browser/resources/chromeos/login/screen_locally_managed_user_creation.css index 86b5c4344c..15ed19c947 100644 --- a/chrome/browser/resources/chromeos/login/screen_locally_managed_user_creation.css +++ b/chrome/browser/resources/chromeos/login/screen_locally_managed_user_creation.css @@ -36,6 +36,12 @@ overflow-x: auto; } +#managed-user-creation .button-link { + font-size: small; + padding: 0 20px; + position: absolute; +} + .below-marketing::-webkit-scrollbar { width: 8px; } @@ -191,6 +197,31 @@ input.managed-user-creation-manager-password, margin-left: 10px !important; } +.import-pod { + height: 32px; + opacity: 0.8; + padding: 6px; + width: 626px; +} + +.import-pod .import-pod-name { + color: #000; + display: inline; + max-height: 30px; + vertical-align: top; +} + +.import-pod.imported .import-pod-name { + color: rgb(141, 141, 141); +} + +.import-pod .import-pod-image { + border: 1px solid gray; + display: inline; + height: 30px; + width: 30px; +} + .manager-pod { height: 32px; opacity: 0.8; @@ -265,6 +296,16 @@ input.managed-user-creation-manager-password, opacity: 1; } +.import-pod.imported.focused { + background-color: rgb(238, 238, 238); + opacity: 1; +} + +.import-pod.focused { + background-color: rgb(66, 129, 244); + opacity: 1; +} + .manager-pod.focused .managed-user-creation-manager-email { color: #fff; } @@ -273,6 +314,13 @@ input.managed-user-creation-manager-password, color: #fff; } +#managed-user-creation-import-pane { + border: 1px solid #c8c8c8; + height: 400px; + overflow-x: hidden; + overflow-y: auto; +} + .manager-pod .password-error, #managed-user-creation .password-error, #managed-user-creation .duplicate-name { diff --git a/chrome/browser/resources/chromeos/login/screen_locally_managed_user_creation.html b/chrome/browser/resources/chromeos/login/screen_locally_managed_user_creation.html index 5c0b4ed34d..a3bbf47e75 100644 --- a/chrome/browser/resources/chromeos/login/screen_locally_managed_user_creation.html +++ b/chrome/browser/resources/chromeos/login/screen_locally_managed_user_creation.html @@ -82,6 +82,21 @@ </div> </div> </div> + + <div id="managed-user-creation-import" class="page-no-marketing" hidden> + <div class="logo-padded-text"> + <div> + <div class="page-title" + i18n-content="importExistingSupervisedUserTitle"></div> + <div class="page-title-explanation" + i18n-content="importExistingSupervisedUserText"></div> + </div> + </div> + <div class="logo-padded-text"> + <div id="managed-user-creation-import-pane"></div> + </div> + </div> + <div id="managed-user-creation-created" class="step-no-logo" hidden> <div class="marketing"> <img src="chrome://theme/IDR_SUPERVISED_ILLUSTRATION_DONE"> @@ -115,6 +130,12 @@ <div class="id-spinner inline-spinner"></div> </div> </div> + <div class="template-import-supervised-user-link button-link" hidden> + <a href="#" class="signin-link" i18n-content="importSupervisedUserLink"></a> + </div> + <div class="template-create-supervised-user-link button-link" hidden> + <a href="#" class="signin-link" i18n-content="createSupervisedUserLink"></a> + </div> <div id="managed-user-creation-manager-template" hidden class="manager-pod"> <div class="managed-user-creation-manager-info-block"> @@ -129,4 +150,8 @@ i18n-values="placeholder:createManagedUserManagerPasswordHint" /> </div> </div> + <div id="managed-user-creation-import-template" hidden class="import-pod"> + <img class="import-pod-image"></img> + <div class="import-pod-name"></div> + </div> </div>
\ No newline at end of file diff --git a/chrome/browser/resources/chromeos/login/screen_locally_managed_user_creation.js b/chrome/browser/resources/chromeos/login/screen_locally_managed_user_creation.js index 762d000936..950415efca 100644 --- a/chrome/browser/resources/chromeos/login/screen_locally_managed_user_creation.js +++ b/chrome/browser/resources/chromeos/login/screen_locally_managed_user_creation.js @@ -10,6 +10,7 @@ login.createScreen('LocallyManagedUserCreationScreen', 'managed-user-creation', function() { var MAX_NAME_LENGTH = 50; var UserImagesGrid = options.UserImagesGrid; + var ButtonImages = UserImagesGrid.ButtonImages; var ManagerPod = cr.ui.define(function() { var node = $('managed-user-creation-manager-template').cloneNode(true); @@ -174,7 +175,7 @@ login.createScreen('LocallyManagedUserCreationScreen', }, selectPod: function(podToSelect) { - if ((this.selectedPod_ == podToSelect) && (podToSelect != null)) { + if ((this.selectedPod_ == podToSelect) && !!podToSelect) { podToSelect.focusInput(); return; } @@ -186,7 +187,7 @@ login.createScreen('LocallyManagedUserCreationScreen', pod.passwordBlock.hidden = true; } } - if (podToSelect == null) + if (!podToSelect) return; podToSelect.classList.add('focused'); podToSelect.passwordBlock.hidden = false; @@ -194,13 +195,209 @@ login.createScreen('LocallyManagedUserCreationScreen', podToSelect.focusInput(); chrome.send('managerSelectedOnLocallyManagedUserCreationFlow', [podToSelect.user.username]); + }, + }; + + var ImportPod = cr.ui.define(function() { + var node = $('managed-user-creation-import-template').cloneNode(true); + node.removeAttribute('id'); + node.removeAttribute('hidden'); + return node; + }); + + /** + * UI element for displaying single supervised user in list of possible users + * for importing existing users. + * @type {Object} + */ + ImportPod.prototype = { + __proto__: HTMLDivElement.prototype, + /** @override */ + decorate: function() { + // Mousedown has to be used instead of click to be able to prevent 'focus' + // event later. + this.addEventListener('mousedown', + this.handleMouseDown_.bind(this)); + }, + + /** + * Updates UI elements from user data. + */ + update: function() { + this.imageElement.src = this.user.avatarurl; + this.nameElement.textContent = this.user.name; + if (this.user.exists) { + if (this.user.conflict == 'imported') { + this.nameElement.textContent = + loadTimeData.getStringF('importUserExists', this.user.name); + } else { + this.nameElement.textContent = + loadTimeData.getStringF('importUsernameExists', this.user.name); + } + } + this.classList.toggle('imported', this.user.exists); + }, + + /** + * Gets image element. + * @type {!HTMLImageElement} + */ + get imageElement() { + return this.querySelector('.import-pod-image'); + }, + + /** + * Gets name element. + * @type {!HTMLDivElement} + */ + get nameElement() { + return this.querySelector('.import-pod-name'); + }, + + /** @override */ + handleMouseDown_: function(e) { + this.parentNode.selectPod(this); + // Prevent default so that we don't trigger 'focus' event. + e.preventDefault(); + }, + + /** + * The user that this pod represents. + * @type {Object} + */ + user_: undefined, + + get user() { + return this.user_; + }, + + set user(userDict) { + this.user_ = userDict; + this.update(); + }, + }; + + var ImportPodList = cr.ui.define('div'); + + /** + * UI element for selecting existing supervised user for import. + * @type {Object} + */ + ImportPodList.prototype = { + __proto__: HTMLDivElement.prototype, + + selectedPod_: null, + + /** @override */ + decorate: function() { + }, + + /** + * Returns all the pods in this pod list. + * @type {NodeList} + */ + get pods() { + return this.children; + }, + + addPod: function(user) { + var importPod = new ImportPod({user: user}); + this.appendChild(importPod); + importPod.update(); + }, + + clearPods: function() { + this.innerHTML = ''; + this.selectedPod_ = null; + }, + + scrollIntoView: function(pod) { + scroller = this.parentNode; + var itemHeight = pod.getBoundingClientRect().height; + var scrollTop = scroller.scrollTop; + var top = pod.offsetTop - scroller.offsetTop; + var clientHeight = scroller.clientHeight; + + var self = scroller; + + // Function to adjust the tops of viewport and row. + function scrollToAdjustTop() { + self.scrollTop = top; + return true; + }; + // Function to adjust the bottoms of viewport and row. + function scrollToAdjustBottom() { + var cs = getComputedStyle(self); + var paddingY = parseInt(cs.paddingTop, 10) + + parseInt(cs.paddingBottom, 10); + + if (top + itemHeight > scrollTop + clientHeight - paddingY) { + self.scrollTop = top + itemHeight - clientHeight + paddingY; + return true; + } + return false; + }; + + // Check if the entire of given indexed row can be shown in the viewport. + if (itemHeight <= clientHeight) { + if (top < scrollTop) + return scrollToAdjustTop(); + if (scrollTop + clientHeight < top + itemHeight) + return scrollToAdjustBottom(); + } else { + if (scrollTop < top) + return scrollToAdjustTop(); + if (top + itemHeight < scrollTop + clientHeight) + return scrollToAdjustBottom(); + } + return false; + }, + + /** + * @param {Element} podToSelect - pod to select, can be null. + */ + selectPod: function(podToSelect) { + if ((this.selectedPod_ == podToSelect) && !!podToSelect) { + return; + } + this.selectedPod_ = podToSelect; + for (var i = 0; i < this.pods.length; i++) { + var pod = this.pods[i]; + if (pod != podToSelect) + pod.classList.remove('focused'); + } + if (!podToSelect) + return; + podToSelect.classList.add('focused'); + var screen = $('managed-user-creation'); + if (!this.selectedPod_) { + screen.getScreenButton('import').disabled = true; + } else { + screen.getScreenButton('import').disabled = + this.selectedPod_.user.exists; + if (!this.selectedPod_.user.exists) { + chrome.send('userSelectedForImportInManagedUserCreationFlow', + [podToSelect.user.id]); + } + } + }, + + selectUser: function(user_id) { + for (var i = 0, pod; pod = this.pods[i]; ++i) { + if (pod.user.id == user_id) { + this.selectPod(pod); + this.scrollIntoView(pod); + break; + } + } }, }; return { EXTERNAL_API: [ 'loadManagers', + 'managedUserSuggestImport', 'managedUserNameError', 'managedUserNameOk', 'showErrorPage', @@ -215,13 +412,16 @@ login.createScreen('LocallyManagedUserCreationScreen', 'showPage', 'setDefaultImages', 'setCameraPresent', + 'setExistingManagedUsers', ], lastVerifiedName_: null, lastIncorrectUserName_: null, managerList_: null, + importList_: null, currentPage_: null, + imagesRequested_: false, // Contains data that can be auto-shared with handler. context_: {}, @@ -229,8 +429,10 @@ login.createScreen('LocallyManagedUserCreationScreen', /** @override */ decorate: function() { this.managerList_ = new ManagerPodList(); - $('managed-user-creation-managers-pane'). - appendChild(this.managerList_); + $('managed-user-creation-managers-pane').appendChild(this.managerList_); + + this.importList_ = new ImportPodList(); + $('managed-user-creation-import-pane').appendChild(this.importList_); var userNameField = $('managed-user-creation-name'); var passwordField = $('managed-user-creation-password'); @@ -284,13 +486,14 @@ login.createScreen('LocallyManagedUserCreationScreen', imageGrid.previewElement = previewElement; imageGrid.selectionType = 'default'; + imageGrid.addEventListener('activate', + this.handleActivate_.bind(this)); imageGrid.addEventListener('select', this.handleSelect_.bind(this)); imageGrid.addEventListener('phototaken', this.handlePhotoTaken_.bind(this)); imageGrid.addEventListener('photoupdated', this.handlePhotoUpdated_.bind(this)); - // Set the title for camera item in the grid. imageGrid.setCameraTitles( loadTimeData.getString('takePhoto'), @@ -315,7 +518,6 @@ login.createScreen('LocallyManagedUserCreationScreen', 'webkitTransitionEnd', function(e) { previewElement.classList.remove('animation'); }); - chrome.send('supervisedUserGetImages'); }, buttonIds: [], @@ -449,6 +651,23 @@ login.createScreen('LocallyManagedUserCreationScreen', var status = this.makeFromTemplate('status-container', 'status'); buttons.push(status); + var importLink = this.makeFromTemplate('import-supervised-user-link', + 'import-link'); + importLink.hidden = true; + buttons.push(importLink); + var linkElement = importLink.querySelector('.signin-link'); + linkElement.addEventListener('click', + this.importLinkPressed_.bind(this)); + + var createLink = this.makeFromTemplate('create-supervised-user-link', + 'create-link'); + createLink.hidden = true; + buttons.push(createLink); + + linkElement = createLink.querySelector('.signin-link'); + linkElement.addEventListener('click', + this.createLinkPressed_.bind(this)); + buttons.push(this.makeButton( 'start', 'managedUserCreationFlow', @@ -471,6 +690,13 @@ login.createScreen('LocallyManagedUserCreationScreen', [])); buttons.push(this.makeButton( + 'import', + 'managedUserCreationFlow', + this.importButtonPressed_.bind(this), + ['import'], + [])); + + buttons.push(this.makeButton( 'gotit', 'managedUserCreationFlow', this.gotItButtonPressed_.bind(this), @@ -528,6 +754,24 @@ login.createScreen('LocallyManagedUserCreationScreen', }, /** + * Does sanity check and calls backend with selected existing supervised + * user id to import user. + * @private + */ + importSupervisedUser_: function() { + var selectedPod = this.importList_.selectedPod_; + if (!selectedPod) + return; + var userId = selectedPod.user.id; + + this.disabled = true; + this.context_.importUserId = userId; + this.context_.managedName = selectedPod.user.name; + + chrome.send('importSupervisedUser', [userId]); + }, + + /** * Calls backend part to check if current user name is valid/not taken. * Results in call to either managedUserNameOk or managedUserNameError. * @private @@ -568,6 +812,7 @@ login.createScreen('LocallyManagedUserCreationScreen', * @param {string} errorText - reason why this name is invalid. */ managedUserNameError: function(name, errorText) { + this.disabled = false; this.lastIncorrectUserName_ = name; this.lastVerifiedName_ = null; @@ -581,7 +826,36 @@ login.createScreen('LocallyManagedUserCreationScreen', 12, 4); this.setButtonDisabledStatus('next', true); } + }, + + managedUserSuggestImport: function(name, user_id) { this.disabled = false; + this.lastIncorrectUserName_ = name; + this.lastVerifiedName_ = null; + + var userNameField = $('managed-user-creation-name'); + var creationScreen = this; + + if (userNameField.value == this.lastIncorrectUserName_) { + this.nameErrorVisible = true; + var link = this.ownerDocument.createElement('div'); + link.innerHTML = loadTimeData.getStringF( + 'importBubbleText', + '<a class="signin-link" href="#">', + name, + '</a>'); + link.querySelector('.signin-link').addEventListener('click', + function(e) { + creationScreen.handleSuggestImport_(user_id); + e.stopPropagation(); + }); + $('bubble').showContentForElement( + $('managed-user-creation-name'), + cr.ui.Bubble.Attachment.RIGHT, + link, + 12, 4); + this.setButtonDisabledStatus('next', true); + } }, /** @@ -676,13 +950,19 @@ login.createScreen('LocallyManagedUserCreationScreen', this.disabled = false; this.updateText_(); $('bubble').hide(); + if (!this.imagesRequested_) { + chrome.send('supervisedUserGetImages'); + this.imagesRequested_ = true; + } var pageNames = ['intro', 'manager', 'username', + 'import', 'error', 'created']; var pageButtons = {'intro' : 'start', 'error' : 'error', + 'import' : 'import', 'created' : 'gotit'}; this.hideStatus_(); for (i in pageNames) { @@ -699,11 +979,14 @@ login.createScreen('LocallyManagedUserCreationScreen', button.disabled = false; } - var pagesWithCancel = ['intro', 'manager', 'username', 'error']; + var pagesWithCancel = ['intro', 'manager', 'username', 'error', 'import']; var cancelButton = $('cancel-add-user-button'); cancelButton.hidden = pagesWithCancel.indexOf(visiblePage) < 0; cancelButton.disabled = false; + this.getScreenElement('import-link').hidden = true; + this.getScreenElement('create-link').hidden = true; + if (pageButtons[visiblePage]) this.getScreenButton(pageButtons[visiblePage]).focus(); @@ -725,11 +1008,20 @@ login.createScreen('LocallyManagedUserCreationScreen', chrome.send('supervisedUserSelectImage', [selected.url, 'default']); this.getScreenElement('image-grid').redraw(); + this.checkUserName_(); this.updateNextButtonForUser_(); this.getScreenElement('name').focus(); + this.getScreenElement('import-link').hidden = + this.importList_.pods.length == 0; } else { this.getScreenElement('image-grid').stopCamera(); } + if (visiblePage == 'import') { + this.getScreenElement('create-link').hidden = false; + this.getScreenButton('import').disabled = + !this.importList_.selectedPod_ || + this.importList_.selectedPod_.user.exists; + } chrome.send('currentSupervisedUserPage', [this.currentPage_]); }, @@ -760,6 +1052,27 @@ login.createScreen('LocallyManagedUserCreationScreen', this.validateAndCreateLocallyManagedUser_(); } }, + + importButtonPressed_: function() { + this.importSupervisedUser_(); + }, + + importLinkPressed_: function() { + this.setVisiblePage_('import'); + }, + + handleSuggestImport_: function(user_id) { + this.setVisiblePage_('import'); + this.importList_.selectUser(user_id); + }, + + createLinkPressed_: function() { + this.setVisiblePage_('username'); + this.lastIncorrectUserName_ = null; + this.lastVerifiedName_ = null; + this.checkUserName_(); + }, + prevButtonPressed_: function() { this.setVisiblePage_('intro'); }, @@ -771,6 +1084,8 @@ login.createScreen('LocallyManagedUserCreationScreen', statusText.classList.remove('error'); status.querySelector('.id-spinner').hidden = false; status.hidden = false; + this.getScreenElement('import-link').hidden = true; + this.getScreenElement('create-link').hidden = true; }, showStatusError: function(text) { @@ -780,6 +1095,8 @@ login.createScreen('LocallyManagedUserCreationScreen', statusText.classList.add('error'); status.querySelector('.id-spinner').hidden = true; status.hidden = false; + this.getScreenElement('import-link').hidden = true; + this.getScreenElement('create-link').hidden = true; }, hideStatus_: function() { @@ -971,6 +1288,16 @@ login.createScreen('LocallyManagedUserCreationScreen', this.imagesData_ = imagesData; }, + + handleActivate_: function() { + var imageGrid = this.getScreenElement('image-grid'); + if (imageGrid.selectedItemUrl == ButtonImages.TAKE_PHOTO) { + this.handleTakePhoto_(); + return; + } + this.nextButtonPressed_(); + }, + /** * Handles selection change. * @param {Event} e Selection change event. @@ -1029,6 +1356,28 @@ login.createScreen('LocallyManagedUserCreationScreen', setCameraPresent: function(present) { this.getScreenElement('image-grid').cameraPresent = present; }, + + setExistingManagedUsers: function(users) { + var userList = users; + + userList.sort(function(a, b) { + // Put existing users last. + if (a.exists != b.exists) + return a.exists ? 1 : -1; + // Sort rest by name. + return a.name.localeCompare(b.name, [], {sensitivity: 'base'}); + }); + + this.importList_.clearPods(); + for (var i = 0; i < userList.length; ++i) + this.importList_.addPod(userList[i]); + + if (userList.length == 1) + this.importList_.selectPod(this.managerList_.pods[0]); + + if (userList.length > 0 && this.currentPage_ == 'username') + this.getScreenElement('import-link').hidden = false; + }, }; }); diff --git a/chrome/browser/resources/chromeos/login/user_pod_row.css b/chrome/browser/resources/chromeos/login/user_pod_row.css index f833c3d28d..428288ffb8 100644 --- a/chrome/browser/resources/chromeos/login/user_pod_row.css +++ b/chrome/browser/resources/chromeos/login/user_pod_row.css @@ -349,7 +349,10 @@ html[oobe=old] .pod.focused .action-box-area { .pod.public-account .name, .side-pane-name { -webkit-padding-end: 16px; + height: 42px; outline: none; + overflow: hidden; + text-overflow: ellipsis; } .learn-more, diff --git a/chrome/browser/resources/chromeos/login/user_pod_row.js b/chrome/browser/resources/chromeos/login/user_pod_row.js index 7969895a09..f5d24c8938 100644 --- a/chrome/browser/resources/chromeos/login/user_pod_row.js +++ b/chrome/browser/resources/chromeos/login/user_pod_row.js @@ -1569,6 +1569,8 @@ cr.define('login', function() { } } }); + // Guard timer for 1 second -- it would conver all possible animations. + ensureTransitionEndEvent(focusedPod, 1000); } }, diff --git a/chrome/browser/resources/chromeos/login/user_pod_template.html b/chrome/browser/resources/chromeos/login/user_pod_template.html index 6a150bb895..6a05bdae48 100644 --- a/chrome/browser/resources/chromeos/login/user_pod_template.html +++ b/chrome/browser/resources/chromeos/login/user_pod_template.html @@ -42,8 +42,10 @@ <div class="side-pane-divider"></div> <div class="side-pane-container"> <div class="side-pane-contents"> - <div class="side-pane-name"></div> - <div class="side-pane-learn-more"></div> + <div class="side-pane-name-plus-learn"> + <div class="side-pane-name"></div> + <div class="side-pane-learn-more"></div> + </div> <p class="info"></p> <p class="reminder" i18n-content="publicAccountReminder"></p> <button class="enter-button" diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/js/util.js b/chrome/browser/resources/chromeos/wallpaper_manager/js/util.js index 4d07a5820c..397f1805e4 100644 --- a/chrome/browser/resources/chromeos/wallpaper_manager/js/util.js +++ b/chrome/browser/resources/chromeos/wallpaper_manager/js/util.js @@ -86,8 +86,7 @@ WallpaperUtil.fetchURL = function(url, type, onSuccess, onFailure, opt_xhr) { */ WallpaperUtil.setOnlineWallpaper = function(url, layout, onSuccess, onFailure) { var self = this; - chrome.wallpaperPrivate.setWallpaperIfExists(url, layout, - Constants.WallpaperSourceEnum.Online, function(exists) { + chrome.wallpaperPrivate.setWallpaperIfExists(url, layout, function(exists) { if (exists) { onSuccess(); return; diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_manager.js b/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_manager.js index 9f1d66127b..b7a19ed214 100644 --- a/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_manager.js +++ b/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_manager.js @@ -243,8 +243,7 @@ function WallpaperManager(dialogDom) { }); window.addEventListener('offline', function() { - chrome.wallpaperPrivate.getOfflineWallpaperList( - Constants.WallpaperSourceEnum.Online, function(lists) { + chrome.wallpaperPrivate.getOfflineWallpaperList(function(lists) { if (!self.downloadedListMap_) self.downloadedListMap_ = {}; for (var i = 0; i < lists.length; i++) { @@ -395,8 +394,7 @@ function WallpaperManager(dialogDom) { // If device is offline, gets the available offline wallpaper list first. // Wallpapers which are not in the list will display a grayscaled // thumbnail. - chrome.wallpaperPrivate.getOfflineWallpaperList( - Constants.WallpaperSourceEnum.Online, function(lists) { + chrome.wallpaperPrivate.getOfflineWallpaperList(function(lists) { if (!self.downloadedListMap_) self.downloadedListMap_ = {}; for (var i = 0; i < lists.length; i++) @@ -490,7 +488,6 @@ function WallpaperManager(dialogDom) { chrome.wallpaperPrivate.setWallpaperIfExists(wallpaperURL, selectedItem.layout, - selectedItem.source, function(exists) { if (exists) { self.currentWallpaper_ = wallpaperURL; diff --git a/chrome/browser/resources/downloads/downloads.css b/chrome/browser/resources/downloads/downloads.css index b0327921c9..bbc2c1065b 100644 --- a/chrome/browser/resources/downloads/downloads.css +++ b/chrome/browser/resources/downloads/downloads.css @@ -126,6 +126,10 @@ html[dir=rtl] .download.otr > .show-dangerous { opacity: 1; } +.malware-description { + color: rgb(196, 42, 23); +} + .progress { height: 48px; left: 0; diff --git a/chrome/browser/resources/downloads/downloads.js b/chrome/browser/resources/downloads/downloads.js index 7522c5f2a9..ecbb73c108 100644 --- a/chrome/browser/resources/downloads/downloads.js +++ b/chrome/browser/resources/downloads/downloads.js @@ -355,14 +355,32 @@ function Download(download) { this.danger_ = createElementWithClassName('div', 'show-dangerous'); this.node.appendChild(this.danger_); + this.dangerNodeImg_ = createElementWithClassName('img', 'icon'); + this.danger_.appendChild(this.dangerNodeImg_); + this.dangerDesc_ = document.createElement('div'); this.danger_.appendChild(this.dangerDesc_); - this.dangerSave_ = createButton(this.saveDangerous_.bind(this), + // Buttons for the malicious case. + this.malwareNodeControls_ = createElementWithClassName('div', 'controls'); + this.malwareSave_ = createLink( + this.saveDangerous_.bind(this), + loadTimeData.getString('danger_restore')); + this.malwareNodeControls_.appendChild(this.malwareSave_); + this.malwareDiscard_ = createLink( + this.discardDangerous_.bind(this), + loadTimeData.getString('control_removefromlist')); + this.malwareNodeControls_.appendChild(this.malwareDiscard_); + this.danger_.appendChild(this.malwareNodeControls_); + + // Buttons for the dangerous but not malicious case. + this.dangerSave_ = createButton( + this.saveDangerous_.bind(this), loadTimeData.getString('danger_save')); this.danger_.appendChild(this.dangerSave_); - this.dangerDiscard_ = createButton(this.discardDangerous_.bind(this), + this.dangerDiscard_ = createButton( + this.discardDangerous_.bind(this), loadTimeData.getString('danger_discard')); this.danger_.appendChild(this.dangerDiscard_); @@ -426,7 +444,6 @@ Download.prototype.update = function(download) { this.state_ = download.state; this.fileExternallyRemoved_ = download.file_externally_removed; this.dangerType_ = download.danger_type; - this.finchString_ = download.finch_string; this.lastReasonDescription_ = download.last_reason_text; this.byExtensionId_ = download.by_ext_id; this.byExtensionName_ = download.by_ext_name; @@ -440,28 +457,7 @@ Download.prototype.update = function(download) { this.received_ = download.received; if (this.state_ == Download.States.DANGEROUS) { - if (this.dangerType_ == Download.DangerType.DANGEROUS_FILE) { - this.dangerDesc_.textContent = loadTimeData.getStringF('danger_file_desc', - this.fileName_); - } else if (this.dangerType_ == Download.DangerType.DANGEROUS_URL) { - this.dangerDesc_.textContent = loadTimeData.getString('danger_url_desc'); - } else if (this.dangerType_ == Download.DangerType.DANGEROUS_CONTENT || - this.dangerType_ == Download.DangerType.DANGEROUS_HOST) { - this.dangerDesc_.textContent = loadTimeData.getStringF( - 'danger_content_desc', this.fileName_); - } else if (this.dangerType_ == Download.DangerType.UNCOMMON_CONTENT) { - this.dangerDesc_.textContent = loadTimeData.getStringF( - 'danger_uncommon_desc', this.fileName_); - } else if (this.dangerType_ == Download.DangerType.POTENTIALLY_UNWANTED) { - this.dangerDesc_.textContent = loadTimeData.getStringF( - 'danger_potentially_unwanted_desc', this.fileName_); - } - if (this.finchString_) { - // Finch trial overrides the normal display string. - this.dangerDesc_.textContent = this.finchString_; - } - this.danger_.style.display = 'block'; - this.safe_.style.display = 'none'; + this.updateDangerousFile(); } else { downloads.scheduleIconLoad(this.nodeImg_, 'chrome://fileicon/' + @@ -567,6 +563,67 @@ Download.prototype.update = function(download) { }; /** + * Decorates the icons, strings, and buttons for a download to reflect the + * danger level of a file. Dangerous & malicious files are treated differently. + */ +Download.prototype.updateDangerousFile = function() { + switch (this.dangerType_) { + case Download.DangerType.DANGEROUS_FILE: { + this.dangerDesc_.textContent = loadTimeData.getStringF( + 'danger_file_desc', this.fileName_); + break; + } + case Download.DangerType.DANGEROUS_URL: { + this.dangerDesc_.textContent = loadTimeData.getString('danger_url_desc'); + break; + } + case Download.DangerType.DANGEROUS_CONTENT: // Fall through. + case Download.DangerType.DANGEROUS_HOST: { + this.dangerDesc_.textContent = loadTimeData.getStringF( + 'danger_content_desc', this.fileName_); + break; + } + case Download.DangerType.UNCOMMON_CONTENT: { + this.dangerDesc_.textContent = loadTimeData.getStringF( + 'danger_uncommon_desc', this.fileName_); + break; + } + case Download.DangerType.POTENTIALLY_UNWANTED: { + this.dangerDesc_.textContent = loadTimeData.getStringF( + 'danger_potentially_unwanted_desc', this.fileName_); + break; + } + } + + if (this.dangerType_ == Download.DangerType.DANGEROUS_FILE) { + downloads.scheduleIconLoad( + this.dangerNodeImg_, + 'chrome://theme/IDR_WARNING?scale=' + window.devicePixelRatio + 'x'); + } else { + downloads.scheduleIconLoad( + this.dangerNodeImg_, + 'chrome://theme/IDR_SAFEBROWSING_WARNING?scale=' + + window.devicePixelRatio + 'x'); + this.dangerDesc_.className = 'malware-description'; + } + + if (this.dangerType_ == Download.DangerType.DANGEROUS_CONTENT || + this.dangerType_ == Download.DangerType.DANGEROUS_HOST || + this.dangerType_ == Download.DangerType.DANGEROUS_URL) { + this.malwareNodeControls_.style.display = 'block'; + this.dangerDiscard_.style.display = 'none'; + this.dangerSave_.style.display = 'none'; + } else { + this.malwareNodeControls_.style.display = 'none'; + this.dangerDiscard_.style.display = 'inline'; + this.dangerSave_.style.display = 'inline'; + } + + this.danger_.style.display = 'block'; + this.safe_.style.display = 'none'; +}; + +/** * Removes applicable bits from the DOM in preparation for deletion. */ Download.prototype.clear = function() { @@ -579,6 +636,9 @@ Download.prototype.clear = function() { this.controlPause_.onclick = null; this.controlResume_.onclick = null; this.dangerDiscard_.onclick = null; + this.dangerSave_.onclick = null; + this.malwareDiscard_.onclick = null; + this.malwareSave_.onclick = null; this.node.innerHTML = ''; }; diff --git a/chrome/browser/resources/extensions/extension_command_list.js b/chrome/browser/resources/extensions/extension_command_list.js index 3f1b05ff25..3d6d779210 100644 --- a/chrome/browser/resources/extensions/extension_command_list.js +++ b/chrome/browser/resources/extensions/extension_command_list.js @@ -262,6 +262,29 @@ cr.define('options', function() { commandClear.title = loadTimeData.getString('extensionCommandsDelete'); commandClear.addEventListener('click', this.handleClear_.bind(this)); + if (command.scope_ui_visible) { + var commandScope = node.querySelector('.command-scope'); + commandScope.hidden = false; + commandScope.id = this.createElementId_( + 'toggleCommandScope', command.extension_id, command.command_name); + if (command.global) { + // TODO(finnur): Use another icon, this is just a placeholder. + commandScope.src = 'chrome://theme/IDR_ACCESSED_COOKIES'; + commandScope.title = + loadTimeData.getString('extensionCommandsGlobalTooltip'); + } else { + commandScope.src = 'chrome://theme/IDR_PRODUCT_LOGO_16'; + var tooltip = command.extension_action ? + 'extensionCommandsNotGlobalPermanentTooltip' : + 'extensionCommandsNotGlobalTooltip'; + commandScope.title = loadTimeData.getString(tooltip); + } + if (!command.extension_action) { + commandScope.addEventListener( + 'click', this.handleToggleCommandScope_.bind(this)); + } + } + this.appendChild(node); }, @@ -423,6 +446,17 @@ cr.define('options', function() { }, /** + * A handler for the toggling the scope of the command. + * @param {Event} event The mouse event to consider. + * @private + */ + handleToggleCommandScope_: function(event) { + var parsed = this.parseElementId_('toggleCommandScope', event.target.id); + chrome.send('toggleCommandScope', + [parsed.extensionId, parsed.commandName]); + }, + + /** * A utility function to create a unique element id based on a namespace, * extension id and a command name. * @param {string} namespace The namespace to prepend the id with. diff --git a/chrome/browser/resources/extensions/extension_commands_overlay.css b/chrome/browser/resources/extensions/extension_commands_overlay.css index 13624174b8..098619d244 100644 --- a/chrome/browser/resources/extensions/extension_commands_overlay.css +++ b/chrome/browser/resources/extensions/extension_commands_overlay.css @@ -56,6 +56,11 @@ margin-top: 1px; } +.command-scope { + padding-left: 0.3em; + padding-top: 0.6em; +} + .capturing { background: rgb(243, 244, 255); border: solid 1px rgb(140, 147, 255); diff --git a/chrome/browser/resources/extensions/extension_commands_overlay.html b/chrome/browser/resources/extensions/extension_commands_overlay.html index 8bd0af637a..b07e838410 100644 --- a/chrome/browser/resources/extensions/extension_commands_overlay.html +++ b/chrome/browser/resources/extensions/extension_commands_overlay.html @@ -46,6 +46,7 @@ src="chrome://theme/IDR_EXTENSION_COMMAND_CLOSE" ><span class="command-shortcut-text" tabindex="0" ></span></span></span> + <img class="command-scope" width="16" height="16" hidden> </div> </div> </div> diff --git a/chrome/browser/resources/feedback/js/feedback.js b/chrome/browser/resources/feedback/js/feedback.js index db9932ab28..a559be6957 100644 --- a/chrome/browser/resources/feedback/js/feedback.js +++ b/chrome/browser/resources/feedback/js/feedback.js @@ -208,6 +208,9 @@ function performanceFeedbackChanged() { * .) Screenshot taken -> . Show Feedback window. */ function initialize() { + // TODO(rkc): Remove logging once crbug.com/284662 is closed. + console.log('FEEDBACK_DEBUG: feedback.js: initialize()'); + // Add listener to receive the feedback info object. chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { if (request.sentFromEventPage) { @@ -260,6 +263,8 @@ function initialize() { }); window.addEventListener('DOMContentLoaded', function() { + // TODO(rkc): Remove logging once crbug.com/284662 is closed. + console.log('FEEDBACK_DEBUG: feedback.js: DOMContentLoaded'); // Ready to receive the feedback object. chrome.runtime.sendMessage({ready: true}); diff --git a/chrome/browser/resources/file_manager/css/file_manager.css b/chrome/browser/resources/file_manager/css/file_manager.css index b057fb015c..5bba9a943c 100644 --- a/chrome/browser/resources/file_manager/css/file_manager.css +++ b/chrome/browser/resources/file_manager/css/file_manager.css @@ -1925,6 +1925,15 @@ menuitem#thumbnail-view[lead]:not([disabled]) { display: none; } +#conflict-confirm-dialog .apply-all-line { + margin: 8px; + text-align: end; +} + +#conflict-confirm-dialog input { + width: auto; +} + /* Progress center */ #progress-center { @@ -1983,6 +1992,8 @@ menuitem#thumbnail-view[lead]:not([disabled]) { display: block; height: 17px; opacity: 1; + overflow: hidden; + text-overflow: ellipsis; white-space: nowrap; } diff --git a/chrome/browser/resources/file_manager/css/file_types.css b/chrome/browser/resources/file_manager/css/file_types.css index 91f5be131a..91459ed92b 100644 --- a/chrome/browser/resources/file_manager/css/file_types.css +++ b/chrome/browser/resources/file_manager/css/file_types.css @@ -173,6 +173,20 @@ list.autocomplete-suggestions [selected] [file-type-icon='gtable'] { url('../images/files/file_types/200/gtable_white.png') 2x); } +[file-type-icon='gform'] { + background-image: -webkit-image-set( + url('../images/files/file_types/100/form.png') 1x, + url('../images/files/file_types/200/form.png') 2x); +} + +tree:focus .tree-item[selected] > .tree-row > [file-type-icon='gform'], +list:focus [selected] [file-type-icon='gform'], +list.autocomplete-suggestions [selected] [file-type-icon='gform'] { + background-image: -webkit-image-set( + url('../images/files/file_types/100/form_white.png') 1x, + url('../images/files/file_types/200/form_white.png') 2x); +} + [file-type-icon='image'] { background-image: -webkit-image-set( url('../images/files/file_types/100/image.png') 1x, diff --git a/chrome/browser/resources/file_manager/css/gallery.css b/chrome/browser/resources/file_manager/css/gallery.css index ebad7bd2a3..7680f4d310 100644 --- a/chrome/browser/resources/file_manager/css/gallery.css +++ b/chrome/browser/resources/file_manager/css/gallery.css @@ -74,41 +74,41 @@ body { .gallery[tools] .image-container[cursor='move'] { cursor: -webkit-image-set( url('../images/gallery/cursor_move.png') 1x, - url('../images/gallery/2x/cursor_move.png') 2x) 15 15; + url('../images/gallery/2x/cursor_move.png') 2x) 15 15, auto; } .gallery[tools] .image-container[cursor='crop'] { cursor: -webkit-image-set( url('../images/gallery/cursor_crop.png') 1x, - url('../images/gallery/2x/cursor_crop.png') 2x) 15 15; + url('../images/gallery/2x/cursor_crop.png') 2x) 15 15, auto; } .gallery[tools] .image-container[cursor='n-resize'], .gallery[tools] .image-container[cursor='s-resize'] { cursor: -webkit-image-set( url('../images/gallery/cursor_updown.png') 1x, - url('../images/gallery/2x/cursor_updown.png') 2x) 15 15; + url('../images/gallery/2x/cursor_updown.png') 2x) 15 15, auto; } .gallery[tools] .image-container[cursor='e-resize'], .gallery[tools] .image-container[cursor='w-resize'] { cursor: -webkit-image-set( url('../images/gallery/cursor_leftright.png') 1x, - url('../images/gallery/2x/cursor_leftright.png') 2x) 15 15; + url('../images/gallery/2x/cursor_leftright.png') 2x) 15 15, auto; } .gallery[tools] .image-container[cursor='nw-resize'], .gallery[tools] .image-container[cursor='se-resize'] { cursor: -webkit-image-set( url('../images/gallery/cursor_nwse.png') 1x, - url('../images/gallery/2x/cursor_nwse.png') 2x) 15 15; + url('../images/gallery/2x/cursor_nwse.png') 2x) 15 15, auto; } .gallery[tools] .image-container[cursor='ne-resize'], .gallery[tools] .image-container[cursor='sw-resize'] { cursor: -webkit-image-set( url('../images/gallery/cursor_swne.png') 1x, - url('../images/gallery/2x/cursor_swne.png') 2x) 15 15; + url('../images/gallery/2x/cursor_swne.png') 2x) 15 15, auto; } .gallery .image-container > .image { diff --git a/chrome/browser/resources/file_manager/js/background.js b/chrome/browser/resources/file_manager/js/background.js index d63ab6210f..379b2fd288 100644 --- a/chrome/browser/resources/file_manager/js/background.js +++ b/chrome/browser/resources/file_manager/js/background.js @@ -594,6 +594,7 @@ function initApp() { // Fetch strings and initialize the context menu. queue.run(function(callback) { chrome.fileBrowserPrivate.getStrings(function(strings) { + loadTimeData.data = strings; initContextMenu(strings); chrome.storage.local.set({strings: strings}, callback); }); diff --git a/chrome/browser/resources/file_manager/js/file_grid.js b/chrome/browser/resources/file_manager/js/file_grid.js index 5d31df09b2..2daec0cd4a 100644 --- a/chrome/browser/resources/file_manager/js/file_grid.js +++ b/chrome/browser/resources/file_manager/js/file_grid.js @@ -43,6 +43,7 @@ FileGrid.decorate = function(self, metadataCache) { self.scrollBar_ = new MainPanelScrollBar(); self.scrollBar_.initialize(self.parentNode, self); + self.setBottomMarginForPanel(0); self.itemConstructor = function(entry) { var item = self.ownerDocument.createElement('LI'); @@ -228,7 +229,9 @@ FileGrid.Item.decorate = function(li, entry, grid) { * @param {number} margin Margin to be set in px. */ FileGrid.prototype.setBottomMarginForPanel = function(margin) { - this.style.paddingBottom = margin + 'px'; + // +20 bottom margin is needed to match the bottom margin size with the + // margin between its items. + this.style.paddingBottom = (margin + 20) + 'px'; this.scrollBar_.setBottomMarginForPanel(margin); }; diff --git a/chrome/browser/resources/file_manager/js/file_manager.js b/chrome/browser/resources/file_manager/js/file_manager.js index d0ed3f3c75..95c76b4114 100644 --- a/chrome/browser/resources/file_manager/js/file_manager.js +++ b/chrome/browser/resources/file_manager/js/file_manager.js @@ -15,6 +15,28 @@ */ function FileManager() { this.initializeQueue_ = new AsyncUtil.Group(); + + /** + * Current list type. + * @type {ListType} + * @private + */ + this.listType_ = null; + + /** + * Whether to suppress the focus moving or not. + * This is used to filter out focusing by mouse. + * @type {boolean} + * @private + */ + this.suppressFocus_ = false; + + /** + * SelectionHandler. + * @type {SelectionHandler} + * @private + */ + this.selectionHandler_ = null; } /** @@ -161,8 +183,6 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52; // Get startup preferences. this.viewOptions_ = {}; group.add(function(done) { - this.dialogType = this.params_.type || DialogType.FULL_PAGE; - this.startupPrefName_ = 'file-manager-' + this.dialogType; util.platform.getPreference(this.startupPrefName_, function(value) { // Load the global default options. try { @@ -251,7 +271,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52; dm.addEventListener('scan-cancelled', this.onScanCancelled_.bind(this)); dm.addEventListener('scan-updated', this.onScanUpdated_.bind(this)); dm.addEventListener('rescan-completed', - this.refreshCurrentDirectoryMetadata_.bind(this)); + this.onRescanCompleted_.bind(this)); var sm = this.directoryModel_.getFileListSelection(); sm.addEventListener('change', function() { @@ -461,10 +481,6 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52; }; FileManager.prototype.onMaximize = function() { - // Do not maximize when running via chrome://files in a browser. - if (util.platform.runningInBrowser()) - return; - var appWindow = chrome.app.window.current(); if (appWindow.isMaximized()) appWindow.restore(); @@ -473,10 +489,6 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52; }; FileManager.prototype.onClose = function() { - // Do not close when running via chrome://files in a browser. - if (util.platform.runningInBrowser()) - return; - window.close(); }; @@ -546,6 +558,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52; FileManager.prototype.initializeUI = function(dialogDom, callback) { this.dialogDom_ = dialogDom; + this.document_ = this.dialogDom_.ownerDocument; this.initializeQueue_.add( this.initEssentialUI_.bind(this), @@ -580,6 +593,12 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52; {}; this.defaultPath = this.params_.defaultPath; } + + // Initialize the member variables that depend this.params_. + this.dialogType = this.params_.type || DialogType.FULL_PAGE; + this.startupPrefName_ = 'file-manager-' + this.dialogType; + this.fileTypes_ = this.params_.typeList || []; + callback(); }; @@ -653,20 +672,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52; * @private */ FileManager.prototype.initEssentialUI_ = function(callback) { - this.listType_ = null; - - this.filesystemObserverId_ = null; - this.driveObserverId_ = null; - - this.document_ = this.dialogDom_.ownerDocument; - this.dialogType = this.params_.type || DialogType.FULL_PAGE; - this.startupPrefName_ = 'file-manager-' + this.dialogType; - - // Used to filter out focusing by mouse. - this.suppressFocus_ = false; - // Optional list of file types. - this.fileTypes_ = this.params_.typeList || []; metrics.recordEnum('Create', this.dialogType, [DialogType.SELECT_FOLDER, DialogType.SELECT_UPLOAD_FOLDER, @@ -675,34 +681,14 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52; DialogType.SELECT_OPEN_MULTI_FILE, DialogType.FULL_PAGE]); - this.selectionHandler_ = null; - + // Create the metadata cache. this.metadataCache_ = MetadataCache.createFull(); - this.hasFooterPanel_ = - this.dialogType == DialogType.SELECT_SAVEAS_FILE || - this.dialogType == DialogType.SELECT_FOLDER; - - // If the footer panel exists, the buttons are placed there. Otherwise, - // the buttons are on the preview panel. - var parentPanelOfButtons = this.dialogDom_.querySelector( - !this.hasFooterPanel_ ? '.preview-panel' : '.dialog-footer'); - parentPanelOfButtons.classList.add('button-panel'); - this.fileTypeSelector_ = parentPanelOfButtons.querySelector('.file-type'); - this.okButton_ = parentPanelOfButtons.querySelector('.ok'); - this.cancelButton_ = parentPanelOfButtons.querySelector('.cancel'); - - // Pre-populate the static localized strings. - i18nTemplate.process(this.document_, loadTimeData); - - // Initialize the header. - this.dialogDom_.querySelector('#app-name').innerText = - chrome.runtime.getManifest().name; - - this.initDialogType_(); - // Create the root view of FileManager. - this.ui_ = new FileManagerUI(this.dialogDom_); + this.ui_ = new FileManagerUI(this.dialogDom_, this.dialogType); + this.fileTypeSelector_ = this.ui_.fileTypeSelector; + this.okButton_ = this.ui_.okButton; + this.cancelButton_ = this.ui_.cancelButton; // Show the window as soon as the UI pre-initialization is done. if (this.dialogType == DialogType.FULL_PAGE && @@ -798,13 +784,6 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52; this.backgroundPage_.progressCenter.getSummarizedItem()); } this.backgroundPage_.progressCenter.addEventListener( - ProgressCenterEvent.ITEM_ADDED, - function(event) { - this.progressCenterPanel_.updateItem( - event.item, - this.backgroundPage_.progressCenter.getSummarizedItem()); - }.bind(this)); - this.backgroundPage_.progressCenter.addEventListener( ProgressCenterEvent.ITEM_UPDATED, function(event) { this.progressCenterPanel_.updateItem( @@ -1114,8 +1093,6 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52; for (var i = 0; i < cm.totalSize; i++) { prefs.columns.push(cm.getWidth(i)); } - if (DialogType.isModal(this.dialogType)) - prefs.listType = this.listType; // Save the global default. util.platform.setPreference(this.startupPrefName_, JSON.stringify(prefs)); @@ -1576,50 +1553,6 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52; }; /** - * Tweak the UI to become a particular kind of dialog, as determined by the - * dialog type parameter passed to the constructor. - * - * @private - */ - FileManager.prototype.initDialogType_ = function() { - var defaultTitle; - var okLabel = str('OPEN_LABEL'); - - switch (this.dialogType) { - case DialogType.SELECT_FOLDER: - defaultTitle = str('SELECT_FOLDER_TITLE'); - break; - - case DialogType.SELECT_UPLOAD_FOLDER: - defaultTitle = str('SELECT_UPLOAD_FOLDER_TITLE'); - okLabel = str('UPLOAD_LABEL'); - break; - - case DialogType.SELECT_OPEN_FILE: - defaultTitle = str('SELECT_OPEN_FILE_TITLE'); - break; - - case DialogType.SELECT_OPEN_MULTI_FILE: - defaultTitle = str('SELECT_OPEN_MULTI_FILE_TITLE'); - break; - - case DialogType.SELECT_SAVEAS_FILE: - defaultTitle = str('SELECT_SAVEAS_FILE_TITLE'); - okLabel = str('SAVE_LABEL'); - break; - - case DialogType.FULL_PAGE: - break; - - default: - throw new Error('Unknown dialog type: ' + this.dialogType); - } - - this.okButton_.textContent = okLabel; - this.dialogDom_.setAttribute('type', this.dialogType); - }; - - /** * Unmounts device. * @param {string} path Path to a volume to unmount. */ @@ -2732,6 +2665,15 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52; }; /** + * Handle the 'rescan-completed' from the DirectoryModel. + * @private + */ + FileManager.prototype.onRescanCompleted_ = function() { + this.refreshCurrentDirectoryMetadata_(); + this.selectionHandler_.onFileSelectionChanged(); + }; + + /** * @private */ FileManager.prototype.cancelSpinnerTimeout_ = function() { @@ -3666,6 +3608,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52; } this.defaultActionMenuItem_.label = defaultItem.title; + this.defaultActionMenuItem_.disabled = !!defaultItem.disabled; this.defaultActionMenuItem_.taskId = defaultItem.taskId; } @@ -3674,6 +3617,8 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52; this.openWithCommand_.canExecuteChange(); this.openWithCommand_.setHidden(!(defaultItem && isMultiple)); + this.openWithCommand_.disabled = defaultItem && !!defaultItem.disabled; + this.defaultActionMenuItem_.hidden = !defaultItem; defaultActionSeparator.hidden = !defaultItem; }; diff --git a/chrome/browser/resources/file_manager/js/file_operation_manager.js b/chrome/browser/resources/file_manager/js/file_operation_manager.js index 48a8b7b8a3..3f17f51bff 100644 --- a/chrome/browser/resources/file_manager/js/file_operation_manager.js +++ b/chrome/browser/resources/file_manager/js/file_operation_manager.js @@ -275,6 +275,8 @@ function FileOperationManager() { this.unloadTimeout_ = null; this.eventRouter_ = new FileOperationManager.EventRouter(); + + Object.seal(this); } /** @@ -315,14 +317,16 @@ FileOperationManager.EventRouter.prototype.__proto__ = cr.EventTarget.prototype; * "ERROR" or "CANCELLED". TODO(hidehiko): Use enum. * @param {Object} status Current FileOperationManager's status. See also * FileOperationManager.getStatus(). + * @param {string} taskId ID of task related with the event. * @param {FileOperationManager.Error=} opt_error The info for the error. This * should be set iff the reason is "ERROR". */ FileOperationManager.EventRouter.prototype.sendProgressEvent = function( - reason, status, opt_error) { + reason, status, taskId, opt_error) { var event = new Event('copy-progress'); event.reason = reason; event.status = status; + event.taskId = taskId; if (opt_error) event.error = opt_error; this.dispatchEvent(event); @@ -349,9 +353,10 @@ FileOperationManager.EventRouter.prototype.sendEntryChangedEvent = function( * or "ERROR". TODO(hidehiko): Use enum. * @param {Array.<string>} urls An array of URLs which are affected by delete * operation. + * @param {string} taskId ID of task related with the event. */ FileOperationManager.EventRouter.prototype.sendDeleteEvent = function( - reason, urls) { + reason, urls, taskId) { var event = new Event('delete'); event.reason = reason; event.urls = urls; @@ -1083,9 +1088,10 @@ FileOperationManager.prototype.requestCancel = function(opt_callback) { * @private */ FileOperationManager.prototype.doCancel_ = function() { + var taskId = this.copyTasks_[0].taskId; this.resetQueue_(); this.cancelRequested_ = false; - this.eventRouter_.sendProgressEvent('CANCELLED', this.getStatus()); + this.eventRouter_.sendProgressEvent('CANCELLED', this.getStatus(), taskId); }; /** @@ -1121,6 +1127,7 @@ FileOperationManager.prototype.paste = function( this.eventRouter_.sendProgressEvent( 'ERROR', this.getStatus(), + this.generateTaskId_(null), new FileOperationManager.Error( util.FileOperationErrorType.FILESYSTEM_ERROR, error)); }.bind(this); @@ -1213,6 +1220,7 @@ FileOperationManager.prototype.queueCopy_ = function( task = new FileOperationManager.CopyTask(entries, targetDirEntry); } + task.taskId = this.generateTaskId_(this.copyTasks_); task.initialize(function() { this.copyTasks_.push(task); this.maybeScheduleCloseBackgroundPage_(); @@ -1223,7 +1231,9 @@ FileOperationManager.prototype.queueCopy_ = function( } else { // Force to update the progress of butter bar when there are new tasks // coming while servicing current task. - this.eventRouter_.sendProgressEvent('PROGRESS', this.getStatus()); + this.eventRouter_.sendProgressEvent('PROGRESS', + this.getStatus(), + task.taskId); } }.bind(this)); @@ -1240,7 +1250,9 @@ FileOperationManager.prototype.serviceAllTasks_ = function() { var self = this; var onTaskProgress = function() { - self.eventRouter_.sendProgressEvent('PROGRESS', self.getStatus()); + self.eventRouter_.sendProgressEvent('PROGRESS', + self.getStatus(), + self.copyTasks_[0].taskId); }; var onEntryChanged = function(kind, entry) { @@ -1248,9 +1260,13 @@ FileOperationManager.prototype.serviceAllTasks_ = function() { }; var onTaskError = function(err) { + var taskId = self.copyTasks_[0].taskId; if (self.maybeCancel_()) return; - self.eventRouter_.sendProgressEvent('ERROR', self.getStatus(), err); + self.eventRouter_.sendProgressEvent('ERROR', + self.getStatus(), + taskId, + err); self.resetQueue_(); }; @@ -1259,12 +1275,15 @@ FileOperationManager.prototype.serviceAllTasks_ = function() { return; // The task at the front of the queue is completed. Pop it from the queue. + var taskId = self.copyTasks_[0].taskId; self.copyTasks_.shift(); self.maybeScheduleCloseBackgroundPage_(); if (!self.copyTasks_.length) { // All tasks have been serviced, clean up and exit. - self.eventRouter_.sendProgressEvent('SUCCESS', self.getStatus()); + self.eventRouter_.sendProgressEvent('SUCCESS', + self.getStatus(), + taskId); self.resetQueue_(); return; } @@ -1273,14 +1292,18 @@ FileOperationManager.prototype.serviceAllTasks_ = function() { // right after one task finished in the queue. We treat all tasks as one // big task logically, so there is only one BEGIN/SUCCESS event pair for // these continuous tasks. - self.eventRouter_.sendProgressEvent('PROGRESS', self.getStatus()); + self.eventRouter_.sendProgressEvent('PROGRESS', + self.getStatus(), + self.copyTasks_[0].taskId); self.copyTasks_[0].run( onEntryChanged, onTaskProgress, onTaskSuccess, onTaskError); }; // If the queue size is 1 after pushing our task, it was empty before, // so we need to kick off queue processing and dispatch BEGIN event. - this.eventRouter_.sendProgressEvent('BEGIN', this.getStatus()); + this.eventRouter_.sendProgressEvent('BEGIN', + this.getStatus(), + self.copyTasks_[0].taskId); this.copyTasks_[0].run( onEntryChanged, onTaskProgress, onTaskSuccess, onTaskError); }; @@ -1296,7 +1319,10 @@ FileOperationManager.DELETE_TIMEOUT = 30 * 1000; * @param {Array.<Entry>} entries The entries. */ FileOperationManager.prototype.deleteEntries = function(entries) { - var task = { entries: entries }; + var task = { + entries: entries, + taskId: this.generateTaskId_(this.deleteTasks_) + }; this.deleteTasks_.push(task); this.maybeScheduleCloseBackgroundPage_(); if (this.deleteTasks_.length == 1) @@ -1320,10 +1346,12 @@ FileOperationManager.prototype.serviceAllDeleteTasks_ = function() { }; var onTaskSuccess = function() { - var urls = getTaskUrls(this.deleteTasks_.shift()); + var urls = getTaskUrls(this.deleteTasks_[0]); + var taskId = this.deleteTasks_[0].taskId; + this.deleteTasks_.shift(); if (!this.deleteTasks_.length) { // All tasks have been serviced, clean up and exit. - this.eventRouter_.sendDeleteEvent('SUCCESS', urls); + this.eventRouter_.sendDeleteEvent('SUCCESS', urls, taskId); this.maybeScheduleCloseBackgroundPage_(); return; } @@ -1332,21 +1360,28 @@ FileOperationManager.prototype.serviceAllDeleteTasks_ = function() { // right after one task finished in the queue. We treat all tasks as one // big task logically, so there is only one BEGIN/SUCCESS event pair for // these continuous tasks. - this.eventRouter_.sendDeleteEvent('PROGRESS', urls); + this.eventRouter_.sendDeleteEvent('PROGRESS', + urls, + this.deleteTasks_[0].taskId); this.serviceDeleteTask_(this.deleteTasks_[0], onTaskSuccess, onTaskFailure); }.bind(this); var onTaskFailure = function(error) { var urls = getTaskUrls(this.deleteTasks_[0]); + var taskId = this.deleteTasks_[0].taskId; this.deleteTasks_ = []; - this.eventRouter_.sendDeleteEvent('ERROR', urls); + this.eventRouter_.sendDeleteEvent('ERROR', + urls, + taskId); this.maybeScheduleCloseBackgroundPage_(); }.bind(this); // If the queue size is 1 after pushing our task, it was empty before, // so we need to kick off queue processing and dispatch BEGIN event. - this.eventRouter_.sendDeleteEvent('BEGIN', getTaskUrls(this.deleteTasks_[0])); + this.eventRouter_.sendDeleteEvent('BEGIN', + getTaskUrls(this.deleteTasks_[0]), + this.deleteTasks_[0].taskId); this.serviceDeleteTask_(this.deleteTasks_[0], onTaskSuccess, onTaskFailure); }; @@ -1409,6 +1444,7 @@ FileOperationManager.prototype.zipSelection = function( var self = this; var zipTask = new FileOperationManager.ZipTask( selectionEntries, dirEntry, dirEntry); + zipTask.taskId = this.generateTaskId_(this.copyTasks_); zipTask.zip = true; zipTask.initialize(function() { self.copyTasks_.push(zipTask); @@ -1419,7 +1455,28 @@ FileOperationManager.prototype.zipSelection = function( } else { // Force to update the progress of butter bar when there are new tasks // coming while servicing current task. - self.eventRouter_.sendProgressEvent('PROGRESS', self.getStatus()); + self.eventRouter_.sendProgressEvent('PROGRESS', + self.getStatus(), + self.copyTasks_[0].taskId); } }); }; + +/** + * Generates new task ID. + * + * TODO(hirono): Remove the queue argument. The ID should be generated + * independenting on the queue. + * @param {Array.<FileOperationManager.Task>} queue Qeueu that the task is + * inserted to. + * @return {string} New task ID. + * @private + */ +FileOperationManager.prototype.generateTaskId_ = function(queue) { + if (queue) { + queue.taskIdCounter = queue.taskIdCounter || 0; + if (!queue.length) + queue.taskIdCounter++; + } + return 'file-operation-' + queue.taskIdCounter; +}; diff --git a/chrome/browser/resources/file_manager/js/file_selection.js b/chrome/browser/resources/file_manager/js/file_selection.js index d814b6b91a..34a75e1a0d 100644 --- a/chrome/browser/resources/file_manager/js/file_selection.js +++ b/chrome/browser/resources/file_manager/js/file_selection.js @@ -164,6 +164,29 @@ function FileSelectionHandler(fileManager) { } /** + * Create the temporary disabled action menu item. + * @return {Object} Created disabled item. + * @private + */ +FileSelectionHandler.createTemporaryDisabledActionMenuItem_ = function() { + if (!FileSelectionHandler.cachedDisabledActionMenuItem_) { + FileSelectionHandler.cachedDisabledActionMenuItem_ = { + label: str('ACTION_OPEN'), + disabled: true + }; + } + + return FileSelectionHandler.cachedDisabledActionMenuItem_; +}; + +/** + * Cached the temporary disabled action menu item. Used inside + * FileSelectionHandler.createTemporaryDisabledActionMenuItem_(). + * @private + */ +FileSelectionHandler.cachedDisabledActionMenuItem_ = null; + +/** * FileSelectionHandler extends cr.EventTarget. */ FileSelectionHandler.prototype.__proto__ = cr.EventTarget.prototype; @@ -226,6 +249,17 @@ FileSelectionHandler.prototype.onFileSelectionChanged = function(event) { } this.lastFileSelectionTime_ = now; + if (this.fileManager_.dialogType === DialogType.FULL_PAGE && + selection.directoryCount === 0 && selection.fileCount > 0) { + // Show disabled items for position calculation of the menu. They will be + // overridden in this.updateFileSelectionAsync(). + this.fileManager_.updateContextMenuActionItems( + FileSelectionHandler.createTemporaryDisabledActionMenuItem_(), true); + } else { + // Update context menu. + this.fileManager_.updateContextMenuActionItems(null, false); + } + this.selectionUpdateTimer_ = setTimeout(function() { this.selectionUpdateTimer_ = null; if (this.selection == selection) @@ -296,8 +330,8 @@ FileSelectionHandler.prototype.updateFileSelectionAsync = function(selection) { if (this.selection != selection) return; // Update the file tasks. - if (this.fileManager_.dialogType == DialogType.FULL_PAGE && - selection.directoryCount == 0 && selection.fileCount > 0) { + if (this.fileManager_.dialogType === DialogType.FULL_PAGE && + selection.directoryCount === 0 && selection.fileCount > 0) { selection.createTasks(function() { if (this.selection != selection) return; @@ -325,9 +359,6 @@ FileSelectionHandler.prototype.updateFileSelectionAsync = function(selection) { if (this.fileManager_.commandHandler) this.fileManager_.commandHandler.updateAvailability(); - // Update context menu. - this.fileManager_.updateContextMenuActionItems(null, false); - // Inform tests it's OK to click buttons now. if (selection.totalCount > 0) { chrome.test.sendMessage('selection-change-complete'); diff --git a/chrome/browser/resources/file_manager/js/file_tasks.js b/chrome/browser/resources/file_manager/js/file_tasks.js index 41176b6341..2584d06f62 100644 --- a/chrome/browser/resources/file_manager/js/file_tasks.js +++ b/chrome/browser/resources/file_manager/js/file_tasks.js @@ -244,9 +244,6 @@ FileTasks.prototype.processTasks_ = function(tasks) { } else if (taskParts[2] == 'view-in-browser') { task.iconType = 'generic'; task.title = loadTimeData.getString('ACTION_VIEW'); - } else if (taskParts[2] == 'install-crx') { - task.iconType = 'generic'; - task.title = loadTimeData.getString('INSTALL_CRX'); } } diff --git a/chrome/browser/resources/file_manager/js/file_transfer_controller.js b/chrome/browser/resources/file_manager/js/file_transfer_controller.js index 008147446e..106ac0dd5f 100644 --- a/chrome/browser/resources/file_manager/js/file_transfer_controller.js +++ b/chrome/browser/resources/file_manager/js/file_transfer_controller.js @@ -291,7 +291,6 @@ FileTransferController.prototype = { */ onDragStart_: function(list, event) { // Check if a drag selection should be initiated or not. - // TODO(hirono): Support drag selection on the grid view. crbug.com/247278 if (list.shouldStartDragSelection(event)) { this.dragSelector_.startDragSelection(list, event); return; diff --git a/chrome/browser/resources/file_manager/js/file_type.js b/chrome/browser/resources/file_manager/js/file_type.js index a30523549c..ea0ae9278e 100644 --- a/chrome/browser/resources/file_manager/js/file_type.js +++ b/chrome/browser/resources/file_manager/js/file_type.js @@ -89,6 +89,8 @@ FileType.types = [ subtype: 'table', pattern: /\.gtable$/i}, {type: 'hosted', icon: 'glink', name: 'GLINK_DOCUMENT_FILE_TYPE', subtype: 'glink', pattern: /\.glink$/i}, + {type: 'hosted', icon: 'gform', name: 'GFORM_DOCUMENT_FILE_TYPE', + subtype: 'form', pattern: /\.gform$/i}, // Others {type: 'document', icon: 'pdf', name: 'PDF_DOCUMENT_FILE_TYPE', diff --git a/chrome/browser/resources/file_manager/js/main_scripts.js b/chrome/browser/resources/file_manager/js/main_scripts.js index 69b7efd558..eb335f509a 100644 --- a/chrome/browser/resources/file_manager/js/main_scripts.js +++ b/chrome/browser/resources/file_manager/js/main_scripts.js @@ -105,6 +105,7 @@ //<include src="text_measure.js"/> //<include src="tree.css.js"/> //<include src="ui/breadcrumbs_controller.js"/> +//<include src="ui/conflict_dialog.js"/> //<include src="ui/file_manager_ui.js"/> //<include src="ui/preview_panel.js"/> //<include src="ui/progress_center_panel.js"/> diff --git a/chrome/browser/resources/file_manager/js/progress_center.js b/chrome/browser/resources/file_manager/js/progress_center.js index 71e485e1ce..43c8b5c58b 100644 --- a/chrome/browser/resources/file_manager/js/progress_center.js +++ b/chrome/browser/resources/file_manager/js/progress_center.js @@ -12,13 +12,6 @@ var ProgressCenter = function() { cr.EventTarget.call(this); /** - * ID counter. - * @type {number} - * @private - */ - this.idCounter_ = 1; - - /** * Default container. * @type {ProgressItemContainer} * @private @@ -90,34 +83,19 @@ ProgressCenter.prototype = { }; /** - * Adds an item to the progress center. - * @param {ProgressItem} item Item to be added. - */ -ProgressCenter.prototype.addItem = function(item) { - // If application window is opening, the item is displayed in the window. - // Otherwise the item is displayed in notification. - item.id = this.idCounter_++; - item.container = this.targetContainer_; - this.items_.push(item); - - if (item.status !== ProgressItemState.PROGRESSING) - this.resetTimeout_.request(ProgressCenter.RESET_DELAY_TIME_MS_); - - var event = new Event(ProgressCenterEvent.ITEM_ADDED); - event.item = item; - this.dispatchEvent(event); -}; - -/** * Updates the item in the progress center. + * If the item has a new ID, the item is added to the item list. * - * @param {ProgressCenterItem} item New contents of the item. + * @param {ProgressCenterItem} item Updated item. */ ProgressCenter.prototype.updateItem = function(item) { var index = this.getItemIndex_(item.id); - if (index === -1) - return; - this.items_[index] = item; + if (index === -1) { + item.container = this.targetContainer_; + this.items_.push(item); + } else { + this.items_[index] = item; + } if (item.status !== ProgressItemState.PROGRESSING) this.resetTimeout_.request(ProgressCenter.RESET_DELAY_TIME_MS_); @@ -129,12 +107,12 @@ ProgressCenter.prototype.updateItem = function(item) { /** * Requests to cancel the progress item. - * @param {number} id Progress ID to be requested to cancel. + * @param {string} id Progress ID to be requested to cancel. */ ProgressCenter.prototype.requestCancel = function(id) { - var index = this.getItemIndex_(id); - if (this.items_[index].cancelCallback) - this.items_[index].cancelCallback(); + var item = this.getItemById(id); + if (item && item.cancelCallback) + item.cancelCallback(); }; /** @@ -163,20 +141,6 @@ ProgressCenter.prototype.switchContainer = function(newContainer) { }; /** - * Obtains item index that have the specifying ID. - * @param {number} id Item ID. - * @return {number} Item index. Returns -1 If the item is not found. - * @private - */ -ProgressCenter.prototype.getItemIndex_ = function(id) { - for (var i = 0; i < this.items_.length; i++) { - if (this.items_[i].id === id) - return i; - } - return -1; -}; - -/** * Obtains the summarized item to be displayed in the closed progress center * panel. * @return {ProgressCenterItem} Summarized item. Returns null if there is no @@ -230,6 +194,30 @@ ProgressCenter.prototype.getSummarizedItem = function() { }; /** + * Obtains item by ID. + * @param {string} id ID of progress item. + * @return {ProgressCenterItem} Progress center item having the specified + * ID. Null if the item is not found. + */ +ProgressCenter.prototype.getItemById = function(id) { + return this.items_[this.getItemIndex_(id)]; +}; + +/** + * Obtains item index that have the specifying ID. + * @param {string} id Item ID. + * @return {number} Item index. Returns -1 If the item is not found. + * @private + */ +ProgressCenter.prototype.getItemIndex_ = function(id) { + for (var i = 0; i < this.items_.length; i++) { + if (this.items_[i].id === id) + return i; + } + return -1; +}; + +/** * Passes the item to the ChromeOS's message center. * * TODO(hirono): Implement the method. @@ -266,18 +254,11 @@ ProgressCenter.prototype.reset_ = function() { */ var ProgressCenterHandler = function(fileOperationManager, progressCenter) { /** - * Copying progress item. - * @type {ProgressCenterItem} - * @private - */ - this.copyingItem_ = null; - - /** - * Deleting progress item. - * @type {ProgressCenterItem} + * Number of deleted files. + * @type {number} * @private */ - this.deletingItem_ = null; + this.totalDeleted_ = 0; /** * File operation manager. @@ -304,61 +285,133 @@ var ProgressCenterHandler = function(fileOperationManager, progressCenter) { }; /** + * Generate a progress message from the event. + * @param {Event} event Progress event. + * @return {string} message. + * @private + */ +ProgressCenterHandler.getMessage_ = function(event) { + if (event.reason === 'ERROR') { + switch (event.error.code) { + case util.FileOperationErrorType.TARGET_EXISTS: + var name = event.error.data.name; + if (event.error.data.isDirectory) + name += '/'; + switch (event.status.operationType) { + case 'COPY': return strf('COPY_TARGET_EXISTS_ERROR', name); + case 'MOVE': return strf('MOVE_TARGET_EXISTS_ERROR', name); + case 'ZIP': return strf('ZIP_TARGET_EXISTS_ERROR', name); + default: return strf('TRANSFER_TARGET_EXISTS_ERROR', name); + } + + case util.FileOperationErrorType.FILESYSTEM_ERROR: + var detail = util.getFileErrorString(event.error.data.code); + switch (event.status.operationType) { + case 'COPY': return strf('COPY_FILESYSTEM_ERROR', detail); + case 'MOVE': return strf('MOVE_FILESYSTEM_ERROR', detail); + case 'ZIP': return strf('ZIP_FILESYSTEM_ERROR', detail); + default: return strf('TRANSFER_FILESYSTEM_ERROR', detail); + } + + default: + switch (event.status.operationType) { + case 'COPY': return strf('COPY_UNEXPECTED_ERROR', event.error); + case 'MOVE': return strf('MOVE_UNEXPECTED_ERROR', event.error); + case 'ZIP': return strf('ZIP_UNEXPECTED_ERROR', event.error); + default: return strf('TRANSFER_UNEXPECTED_ERROR', event.error); + } + } + } else if (event.status.numRemainingItems === 1) { + var name = event.status.processingEntry.name; + switch (event.status.operationType) { + case 'COPY': return strf('COPY_FILE_NAME', name); + case 'MOVE': return strf('MOVE_FILE_NAME', name); + case 'ZIP': return strf('ZIP_FILE_NAME', name); + default: return strf('TRANSFER_FILE_NAME', name); + } + } else { + var remainNumber = event.status.numRemainingItems; + switch (event.status.operationType) { + case 'COPY': return strf('COPY_ITEMS_REMAINING', remainNumber); + case 'MOVE': return strf('MOVE_ITEMS_REMAINING', remainNumber); + case 'ZIP': return strf('ZIP_ITEMS_REMAINING', remainNumber); + default: return strf('TRANSFER_ITEMS_REMAINING', remainNumber); + } + } +}; + +/** + * Generate a delete message from the event. + * @param {Event} event Progress event. + * @param {number} totalDeleted Total number of deleted files. + * @return {string} message. + * @private + */ +ProgressCenterHandler.getDeleteMessage_ = function(event, totalDeleted) { + if (totalDeleted === 1) { + var fullPath = util.extractFilePath(event.urls[0]); + var fileName = PathUtil.split(fullPath).pop(); + return strf('DELETED_MESSAGE', fileName); + } else { + return strf('DELETED_MESSAGE_PLURAL', totalDeleted); + } +}; + +/** * Handles the copy-progress event. * @param {Event} event The copy-progress event. * @private */ ProgressCenterHandler.prototype.onCopyProgress_ = function(event) { var progressCenter = this.progressCenter_; + var item; switch (event.reason) { case 'BEGIN': - if (this.copyingItem_) { - console.error('Previous copy is not completed.'); - return; - } - this.copyingItem_ = new ProgressCenterItem(); - // TODO(hirono): Specifying the correct message. - this.copyingItem_.message = 'Copying ...'; - this.copyingItem_.progressMax = event.status.totalBytes; - this.copyingItem_.progressValue = event.status.processedBytes; - this.copyingItem_.cancelCallback = function() { + item = new ProgressCenterItem(); + item.id = event.taskId; + item.message = ProgressCenterHandler.getMessage_(event); + item.progressMax = event.status.totalBytes; + item.progressValue = event.status.processedBytes; + item.cancelCallback = function(inItem) { this.fileOperationManager_.requestCancel(function() { - this.copyingItem_.message = 'Canceled.'; - this.copyingItem_.state = ProgressItemState.CANCELED; - progressCenter.updateItem(this.copyingItem_); - this.copyingItem_ = null; + inItem.message = strf('COPY_CANCELLED'); + inItem.state = ProgressItemState.CANCELED; + progressCenter.updateItem(inItem); }.bind(this)); - }.bind(this); + }.bind(this, item); - progressCenter.addItem(this.copyingItem_); + progressCenter.updateItem(item); break; case 'PROGRESS': - if (!this.copyingItem_) { + item = progressCenter.getItemById(event.taskId); + if (!item) { console.error('Cannot find copying item.'); return; } - this.copyingItem_.progressValue = event.status.processedBytes; - progressCenter.updateItem(this.copyingItem_); + item.message = ProgressCenterHandler.getMessage_(event); + item.progressValue = event.status.processedBytes; + progressCenter.updateItem(item); break; case 'SUCCESS': case 'ERROR': - if (!this.copyingItem_) { - console.error('Cannot find copying item.'); - return; + item = progressCenter.getItemById(event.taskId); + if (!item) { + // ERROR events can be dispatched before BEGIN events. + item = new ProgressCenterItem(); + item.id = event.taskId; + item.progressMax = 1; } - // TODO(hirono): Replace the message with the string assets. if (event.reason === 'SUCCESS') { - this.copyingItem_.message = 'Complete.'; - this.copyingItem_.state = ProgressItemState.COMPLETE; - this.copyingItem_.progressValue = this.copyingItem_.progressMax; + // TODO(hirono): Add a message for complete. + item.state = ProgressItemState.COMPLETE; + item.progressValue = item.progressMax; } else { - this.copyingItem_.message = 'Error.'; - this.copyingItem_.state = ProgressItemState.ERROR; + item.message = ProgressCenterHandler.getMessage_(event); + item.state = ProgressItemState.ERROR; } - progressCenter.updateItem(this.copyingItem_); - this.copyingItem_ = null; + progressCenter.updateItem(item); break; } }; @@ -369,43 +422,50 @@ ProgressCenterHandler.prototype.onCopyProgress_ = function(event) { * @private */ ProgressCenterHandler.prototype.onDeleteProgress_ = function(event) { + var progressCenter = this.progressCenter_; + var item; switch (event.reason) { case 'BEGIN': - if (this.deletingItem_) { - console.error('Previous delete is not completed.'); - return; - } - this.deletingItem_ = new ProgressCenterItem(); + this.totalDeleted_ = 0; + item = new ProgressCenterItem(); + item.id = event.taskId; // TODO(hirono): Specifying the correct message. - this.deletingItem_.message = 'Deleting...'; - this.deletingItem_.progressMax = 100; - progressCenter.addItem(this.deletingItem_); + item.message = + ProgressCenterHandler.getDeleteMessage_(event, this.totalDeleted_); + item.progressMax = 100; + progressCenter.updateItem(item); break; case 'PROGRESS': - if (!this.deletingItem_) { + item = progressCenter.getItemById(event.taskId); + if (!item) { console.error('Cannot find deleting item.'); return; } - progressCenter.updateItem(this.deletingItem_); + this.totalDeleted_ += event.urls.length; + item.message = + ProgressCenterHandler.getDeleteMessage_(event, this.totalDeleted_); + progressCenter.updateItem(item); break; case 'SUCCESS': case 'ERROR': - if (!this.deletingItem_) { + item = progressCenter.getItemById(event.taskId); + if (!item) { console.error('Cannot find deleting item.'); return; } if (event.reason === 'SUCCESS') { - this.deletingItem_.message = 'Complete.'; - this.deletingItem_.state = ProgressItemState.COMPLETE; - this.deletingItem_.progressValue = this.deletingItem_.progressMax; + this.totalDeleted_ += event.urls.length; + item.message = + ProgressCenterHandler.getDeleteMessage_(event, this.totalDeleted_); + item.state = ProgressItemState.COMPLETE; + item.progressValue = item.progressMax; } else { - this.deletingItem_.message = 'Error.'; - this.deletingItem_.state = ProgressItemState.ERROR; + item.message = str('DELETE_ERROR'); + item.state = ProgressItemState.ERROR; } - progressCenter.updateItem(this.deletingItem_); - this.deletingItem_ = null; + progressCenter.updateItem(item); break; } }; diff --git a/chrome/browser/resources/file_manager/js/progress_center_common.js b/chrome/browser/resources/file_manager/js/progress_center_common.js index bf7696d102..608d2dbf06 100644 --- a/chrome/browser/resources/file_manager/js/progress_center_common.js +++ b/chrome/browser/resources/file_manager/js/progress_center_common.js @@ -21,11 +21,6 @@ var ProgressItemContainer = Object.freeze({ */ var ProgressCenterEvent = Object.freeze({ /** - * Background page notifies item added to application windows. - */ - ITEM_ADDED: 'itemAdded', - - /** * Background page notifies item update to application windows. */ ITEM_UPDATED: 'itemUpdated', @@ -55,7 +50,7 @@ var ProgressItemState = Object.freeze({ var ProgressCenterItem = function() { /** * Item ID. - * @type {?number} + * @type {string} * @private */ this.id_ = null; @@ -108,7 +103,7 @@ var ProgressCenterItem = function() { ProgressCenterItem.prototype = { /** * Setter of Item ID. - * @param {number} value New value of ID. + * @param {string} value New value of ID. */ set id(value) { if (!this.id_) @@ -119,7 +114,7 @@ ProgressCenterItem.prototype = { /** * Getter of Item ID. - * @return {number} Item ID. + * @return {string} Item ID. */ get id() { return this.id_; diff --git a/chrome/browser/resources/file_manager/js/tree.css.js b/chrome/browser/resources/file_manager/js/tree.css.js index d641d4cea2..6b89acbdb8 100644 --- a/chrome/browser/resources/file_manager/js/tree.css.js +++ b/chrome/browser/resources/file_manager/js/tree.css.js @@ -54,6 +54,6 @@ } prepareTriangle( - 'tree-triangle', 'rgba(122, 122, 122, 0.6)', 'rgba(0, 0, 0, 0.6)'); + 'tree-triangle', 'rgba(122, 122, 122, 0.6)', 'rgba(0, 0, 0, 0)'); prepareTriangle('tree-triangle-inverted', '#ffffff', '#ffffff'); })(); diff --git a/chrome/browser/resources/file_manager/js/ui/conflict_dialog.js b/chrome/browser/resources/file_manager/js/ui/conflict_dialog.js new file mode 100644 index 0000000000..9bbd39a23c --- /dev/null +++ b/chrome/browser/resources/file_manager/js/ui/conflict_dialog.js @@ -0,0 +1,135 @@ +// Copyright 2013 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. + +'use strict'; + +/** + * Dialog to confirm the operation for conflicted file operations. + * + * @param {HTMLElement} parentNode Node to be parent for this dialog. + * @constructor + * @extends {FileManagerDialogBase} + */ +function ConflictDialog(parentNode) { + FileManagerDialogBase.call(this, parentNode); + + /** + * Callback to be called when the showing task is completed. The first + * argument is which button is pressed. The second argument is whether to + * apply all or not. + * + * @type {function(ConflictDialog.Result, boolean)} + * @private + */ + this.callback_ = null; + + /** + * Checkbox to specify whether to apply the selection to all entries or not. + * @type {HTMLElement} + * @private + */ + this.applyAllCheckbox_ = parentNode.ownerDocument.createElement('input'); + this.applyAllCheckbox_.id = 'conflict-confirm-dialog-apply-all-checkbox'; + this.applyAllCheckbox_.type = 'checkbox'; + + // Apply all line. + var applyAllLabel = parentNode.ownerDocument.createElement('label'); + applyAllLabel.textContent = str('CONFLICT_DIALOG_APPLY_TO_ALL'); + applyAllLabel.setAttribute('for', this.applyAllCheckbox_.id); + var applyAllLine = parentNode.ownerDocument.createElement('div'); + applyAllLine.className = 'apply-all-line'; + applyAllLine.appendChild(this.applyAllCheckbox_); + applyAllLine.appendChild(applyAllLabel); + + /** + * Element of the keep both button. + * @type {HTMLElement} + * @private + */ + this.keepBothButton_ = parentNode.ownerDocument.createElement('button'); + this.keepBothButton_.textContent = str('CONFLICT_DIALOG_KEEP_BOTH'); + this.keepBothButton_.addEventListener( + 'click', + this.hideWithResult_.bind(this, ConflictDialog.Result.KEEP_BOTH)); + + /** + * Element of the replace button. + * @type {HTMLElement} + * @private + */ + this.replaceButton_ = parentNode.ownerDocument.createElement('button'); + this.replaceButton_.textContent = str('CONFLICT_DIALOG_REPLACE'); + this.replaceButton_.addEventListener( + 'click', + this.hideWithResult_.bind(this, ConflictDialog.Result.REPLACE)); + + // Buttons line. + var buttons = this.okButton_.parentNode; + buttons.replaceChild(this.keepBothButton_, this.okButton_); + buttons.appendChild(this.replaceButton_); + + // Frame + this.frame_.id = 'conflict-confirm-dialog'; + this.frame_.insertBefore(applyAllLine, buttons); +} + +/** + * Result of conflict confirm dialogs. + * @enum {string} + * @const + */ +ConflictDialog.Result = Object.freeze({ + KEEP_BOTH: 'keepBoth', + CANCEL: 'cancel', + REPLACE: 'replace' +}); + +ConflictDialog.prototype = { + __proto__: FileManagerDialogBase.prototype +}; + +/** + * Shows the conflict confirm dialog. + * + * @param {string} fileName Filename that is conflicted. + * @param {function(ConflictDialog.Result, boolean)} callback Complete + * callbak. See also ConflictDialog#callback_. + * @return {boolean} True if the dialog can show successfully. False if the + * dialog failed to show due to an existing dialog. + */ +ConflictDialog.prototype.show = function(fileName, callback) { + if (this.callback_) + return false; + + this.callback_ = callback; + FileManagerDialogBase.prototype.showOkCancelDialog.call( + this, + str('CONFLICT_DIALOG_TITLE'), + strf('CONFLICT_DIALOG_MESSAGE', fileName)); + return true; +}; + +/** + * Handles cancellation. + * @param {Event} event Click event. + * @private + */ +ConflictDialog.prototype.onCancelClick_ = function(event) { + this.hideWithResult_(ConflictDialog.Result.CANCEL); +}; + +/** + * Hides the dialog box with the result. + * @param {ConflictDialog.Result} result Result. + * @private + */ +ConflictDialog.prototype.hideWithResult_ = function(result) { + this.hide(function() { + if (!this.callback_) + return; + this.callback_(result, this.applyAllCheckbox_.checked); + this.callback_ = null; + this.applyAllCheckbox_.checked = false; + }.bind(this)); +}; diff --git a/chrome/browser/resources/file_manager/js/ui/file_manager_ui.js b/chrome/browser/resources/file_manager/js/ui/file_manager_ui.js index 1a894d3b5b..99f8a608a7 100644 --- a/chrome/browser/resources/file_manager/js/ui/file_manager_ui.js +++ b/chrome/browser/resources/file_manager/js/ui/file_manager_ui.js @@ -8,9 +8,10 @@ * The root of the file manager's view managing the DOM of Files.app. * * @param {HTMLElement} element Top level element of Files.app. + * @param {DialogType} dialogType Dialog type. * @constructor. */ -var FileManagerUI = function(element) { +var FileManagerUI = function(element, dialogType) { /** * Top level element of Files.app. * @type {HTMLElement} @@ -19,6 +20,13 @@ var FileManagerUI = function(element) { this.element_ = element; /** + * Dialog type. + * @type {DialogType} + * @private + */ + this.dialogType_ = dialogType; + + /** * Error dialog. * @type {ErrorDialog} */ @@ -61,12 +69,105 @@ var FileManagerUI = function(element) { this.suggestAppsDialog = null; /** + * Conflict dialog. + * @type {ConflictDialog} + */ + this.conflictDialog = null; + + /** * Search box. * @type {SearchBox} */ this.searchBox = null; + /** + * File type selector in the footer. + * @type {HTMLElement} + */ + this.fileTypeSelector = null; + + /** + * OK button in the footer. + * @type {HTMLElement} + */ + this.okButton = null; + + /** + * Cancel button in the footer. + * @type {HTMLElement} + */ + this.cancelButton = null; + Object.seal(this); + + // Initialize the header. + this.element_.querySelector('#app-name').innerText = + chrome.runtime.getManifest().name; + + // Initialize dialog type. + this.initDialogType_(); + + // Pre-populate the static localized strings. + i18nTemplate.process(this.element_.ownerDocument, loadTimeData); +}; + +/** + * Tweak the UI to become a particular kind of dialog, as determined by the + * dialog type parameter passed to the constructor. + * + * @private + */ +FileManagerUI.prototype.initDialogType_ = function() { + // Obtain elements. + var hasFooterPanel = + this.dialogType_ == DialogType.SELECT_SAVEAS_FILE || + this.dialogType_ == DialogType.SELECT_FOLDER; + + // If the footer panel exists, the buttons are placed there. Otherwise, + // the buttons are on the preview panel. + var parentPanelOfButtons = this.element_.ownerDocument.querySelector( + !hasFooterPanel ? '.preview-panel' : '.dialog-footer'); + parentPanelOfButtons.classList.add('button-panel'); + this.fileTypeSelector = parentPanelOfButtons.querySelector('.file-type'); + this.okButton = parentPanelOfButtons.querySelector('.ok'); + this.cancelButton = parentPanelOfButtons.querySelector('.cancel'); + + // Set attributes. + var defaultTitle; + var okLabel = str('OPEN_LABEL'); + + switch (this.dialogType_) { + case DialogType.SELECT_FOLDER: + defaultTitle = str('SELECT_FOLDER_TITLE'); + break; + + case DialogType.SELECT_UPLOAD_FOLDER: + defaultTitle = str('SELECT_UPLOAD_FOLDER_TITLE'); + okLabel = str('UPLOAD_LABEL'); + break; + + case DialogType.SELECT_OPEN_FILE: + defaultTitle = str('SELECT_OPEN_FILE_TITLE'); + break; + + case DialogType.SELECT_OPEN_MULTI_FILE: + defaultTitle = str('SELECT_OPEN_MULTI_FILE_TITLE'); + break; + + case DialogType.SELECT_SAVEAS_FILE: + defaultTitle = str('SELECT_SAVEAS_FILE_TITLE'); + okLabel = str('SAVE_LABEL'); + break; + + case DialogType.FULL_PAGE: + break; + + default: + throw new Error('Unknown dialog type: ' + this.dialogType); + } + + this.okButton.textContent = okLabel; + this.element_.setAttribute('type', this.dialogType_); }; /** @@ -89,6 +190,7 @@ FileManagerUI.prototype.initDialogs = function() { new cr.filebrowser.DefaultActionDialog(this.element_); this.suggestAppsDialog = new SuggestAppsDialog( this.element_, appState.suggestAppsDialogState || {}); + this.conflictDialog = new ConflictDialog(this.element_); }; /** diff --git a/chrome/browser/resources/file_manager/js/ui/progress_center_panel.js b/chrome/browser/resources/file_manager/js/ui/progress_center_panel.js index f50d6d5de0..e8e9405122 100644 --- a/chrome/browser/resources/file_manager/js/ui/progress_center_panel.js +++ b/chrome/browser/resources/file_manager/js/ui/progress_center_panel.js @@ -8,7 +8,7 @@ * Progress center panel. * * @param {HTMLElement} element DOM Element of the process center panel. - * @param {function(number)} cancelCallback Callback to becalled with the ID of + * @param {function(string)} cancelCallback Callback to becalled with the ID of * the progress item when the cancel button is clicked. * @constructor */ @@ -31,7 +31,6 @@ var ProgressCenterPanel = function(element, cancelCallback) { element.addEventListener('click', this.onClick_.bind(this)); element.addEventListener( 'webkitTransitionEnd', this.onItemTransitionEnd_.bind(this)); - }; /** @@ -41,7 +40,7 @@ var ProgressCenterPanel = function(element, cancelCallback) { * @type {boolean} * @private */ -ProgressCenterPanel.ENABLED_ = false; +ProgressCenterPanel.ENABLED_ = true; /** * Update item element. @@ -80,7 +79,6 @@ ProgressCenterPanel.updateItemElement_ = function(element, item) { previousWidthRate > item.progressRateByPercent ? '0' : null; track.style.width = item.progressRateByPercent + '%'; }, 0); - // track.style.transitionDuration = null; element.setAttribute('data-progress-id', item.id); element.setAttribute('data-progress-max', item.progressMax); element.setAttribute('data-progress-value', item.progressValue); @@ -123,12 +121,12 @@ ProgressCenterPanel.prototype.reset = function() { /** * Gets an item element having the specified ID. - * @param {number} id progress item ID. + * @param {string} id progress item ID. * @return {HTMLElement} Item element having the ID. * @private */ ProgressCenterPanel.prototype.getItemElement_ = function(id) { - var query = 'li[data-progress-id="$ID"]'.replace('$ID', id); + var query = 'li[data-progress-id="' + id + '"]'; return this.openView_.querySelector(query); }; @@ -193,6 +191,6 @@ ProgressCenterPanel.prototype.onClick_ = function(event) { else if ((event.target.classList.contains('toggle') && this.closeView_.classList.contains('single')) || event.target.classList.contains('cancel')) - this.cancelCallback_(parseInt( - event.target.parentNode.parentNode.getAttribute('data-progress-id'))); + this.cancelCallback_( + event.target.parentNode.parentNode.getAttribute('data-progress-id')); }; diff --git a/chrome/browser/resources/file_manager/js/util.js b/chrome/browser/resources/file_manager/js/util.js index 5e1c4498bd..9f6e88f7ef 100644 --- a/chrome/browser/resources/file_manager/js/util.js +++ b/chrome/browser/resources/file_manager/js/util.js @@ -725,8 +725,8 @@ function strf(id, var_args) { */ util.platform = { /** - * @return {boolean} True if Files.app is running via "chrome://files", open - * files or select folder dialog. False otherwise. + * @return {boolean} True if Files.app is running as an open files or a select + * folder dialog. False otherwise. */ runningInBrowser: function() { return !window.appID; diff --git a/chrome/browser/resources/file_manager/main.html b/chrome/browser/resources/file_manager/main.html index ee6830152d..af0940b512 100644 --- a/chrome/browser/resources/file_manager/main.html +++ b/chrome/browser/resources/file_manager/main.html @@ -118,6 +118,7 @@ <script src="js/text_measure.js"></script> <script src="js/tree.css.js"></script> <script src="js/ui/breadcrumbs_controller.js"></script> + <script src="js/ui/conflict_dialog.js"></script> <script src="js/ui/file_manager_ui.js"></script> <script src="js/ui/preview_panel.js"></script> <script src="js/ui/progress_center_panel.js"></script> @@ -303,7 +304,7 @@ </div> <div id="progress-center-open-view"></div> </div> - <div id="butter-bar-container"> + <div id="butter-bar-container" hidden> <div id="butter-bar"> <div class="content"> <div class="butter-message"></div> diff --git a/chrome/browser/resources/file_manager/manifest.json b/chrome/browser/resources/file_manager/manifest.json index 06236a774f..07b6d88b81 100644 --- a/chrome/browser/resources/file_manager/manifest.json +++ b/chrome/browser/resources/file_manager/manifest.json @@ -120,14 +120,6 @@ ] }, { - "id": "install-crx", - "default_title": "__MSG_INSTALL_CRX__", - "default_icon": "images/filetype_generic.png", - "file_filters": [ - "filesystem:*.crx" - ] - }, - { "id": "gallery", "default_title": "__MSG_OPEN_ACTION__", "default_icon": "images/filetype_image.png", @@ -162,7 +154,8 @@ "default_icon": "images/filetype_generic.png", "file_filters": [ "filesystem:*.gdraw", - "filesystem:*.gtable" + "filesystem:*.gtable", + "filesystem:*.gform" ] }, { @@ -213,9 +206,6 @@ "file_filters": [] } ], - "chrome_url_overrides": { - "files": "main.html" - }, // Required to import scripts in a web worker. Note, that in Apps v2, it is // enough that anything is passed to web_accessible_resources. If there is // at least any file, then all files are allowed. http://crbug.com/179127. @@ -223,6 +213,7 @@ "app": { "background": { "scripts": [ + "chrome://resources/js/load_time_data.js", "chrome://resources/js/cr.js", "chrome://resources/js/cr/event_target.js", "chrome://resources/js/cr/ui/array_data_model.js", diff --git a/chrome/browser/resources/gaia_auth/inline_injected.js b/chrome/browser/resources/gaia_auth/inline_injected.js index a56336d56d..2f0d020b17 100644 --- a/chrome/browser/resources/gaia_auth/inline_injected.js +++ b/chrome/browser/resources/gaia_auth/inline_injected.js @@ -28,10 +28,13 @@ return; } + var checkboxElement = $('advanced-box'); + var chooseWhatToSync = checkboxElement && checkboxElement.checked; var msg = {method: 'attemptLogin', email: gaiaLoginForm['Email'].value, password: gaiaLoginForm['Passwd'].value, - attemptToken: new Date().getTime()}; + attemptToken: new Date().getTime(), + chooseWhatToSync: chooseWhatToSync}; extWindow.postMessage(msg, 'chrome://inline-login'); console.log('Credentials sent'); diff --git a/chrome/browser/resources/gaia_auth/main.css b/chrome/browser/resources/gaia_auth/main.css index dda571fca4..397947616e 100644 --- a/chrome/browser/resources/gaia_auth/main.css +++ b/chrome/browser/resources/gaia_auth/main.css @@ -18,6 +18,8 @@ iframe { webview { display: inline-block; - height: 300px; - width: 300px; + height: 100%; + margin: 0; + padding: 0; + width: 100%; } diff --git a/chrome/browser/resources/gaia_auth/main.js b/chrome/browser/resources/gaia_auth/main.js index 2169174e1e..1f41917417 100644 --- a/chrome/browser/resources/gaia_auth/main.js +++ b/chrome/browser/resources/gaia_auth/main.js @@ -39,9 +39,9 @@ Authenticator.prototype = { samlSupportChannel_: null, GAIA_URL: 'https://accounts.google.com/', - GAIA_PAGE_PATH: 'ServiceLogin?service=chromeoslogin' + - '&skipvpage=true&sarp=1&rm=hide', + GAIA_PAGE_PATH: 'ServiceLogin?skipvpage=true&sarp=1&rm=hide', PARENT_PAGE: 'chrome://oobe/', + SERVICE_ID: 'chromeoslogin', CONTINUE_URL: Authenticator.THIS_EXTENSION_ORIGIN + '/success.html', initialize: function() { @@ -50,7 +50,11 @@ Authenticator.prototype = { this.gaiaUrl_ = params.gaiaUrl || this.GAIA_URL; this.inputLang_ = params.hl; this.inputEmail_ = params.email; + this.service_ = params.service || this.SERVICE_ID; this.continueUrl_ = params.continueUrl || this.CONTINUE_URL; + this.continueUrlWithoutParams_ = + this.continueUrl_.substring(0, this.continueUrl_.indexOf('?')) || + this.continueUrl_; this.inlineMode_ = params.inlineMode; document.addEventListener('DOMContentLoaded', this.onPageLoad.bind(this)); @@ -74,8 +78,9 @@ Authenticator.prototype = { getFrameUrl_: function() { var url = this.gaiaUrl_; - url += this.GAIA_PAGE_PATH + '&continue=' + - encodeURIComponent(this.continueUrl_); + url += this.GAIA_PAGE_PATH + + '&service=' + encodeURIComponent(this.service_) + + '&continue=' + encodeURIComponent(this.continueUrl_); if (this.inputLang_) url += '&hl=' + encodeURIComponent(this.inputLang_); @@ -94,7 +99,8 @@ Authenticator.prototype = { gaiaFrame.contentWindow.postMessage('', gaiaFrame.src); }); this.onLoginUILoaded(); - } else if (gaiaFrame.src.lastIndexOf(this.continueUrl_, 0) == 0) { + } else if (gaiaFrame.src.lastIndexOf( + this.continueUrlWithoutParams_, 0) == 0) { // Detect when login is finished by the load stop event of the continue // URL. Cannot reuse the login complete flow in success.html, because // webview does not support extension pages yet. diff --git a/chrome/browser/resources/gaia_auth_host/gaia_auth_host.js b/chrome/browser/resources/gaia_auth_host/gaia_auth_host.js index edcb4133a5..20eb832df2 100644 --- a/chrome/browser/resources/gaia_auth_host/gaia_auth_host.js +++ b/chrome/browser/resources/gaia_auth_host/gaia_auth_host.js @@ -56,6 +56,7 @@ cr.define('cr.login', function() { 'gaiaUrl', // Gaia url to use; 'hl', // Language code for the user interface; 'email', // Pre-fill the email field in Gaia UI; + 'service', // Name of Gaia service; 'continueUrl' // Continue url to use; ]; @@ -290,6 +291,7 @@ cr.define('cr.login', function() { if (e.origin == GAIA_ORIGIN && msg.method == 'attemptLogin') { this.email_ = msg.email; this.password_ = msg.password; + this.chooseWhatToSync_ = msg.chooseWhatToSync; return; } @@ -305,7 +307,8 @@ cr.define('cr.login', function() { this.onAuthSuccess_({email: msg.email || this.email_, password: msg.password || this.password_, authCode: msg.authCode, - useOffline: msg.method == 'offlineLogin'}); + useOffline: msg.method == 'offlineLogin', + chooseWhatToSync: this.chooseWhatToSync_}); return; } diff --git a/chrome/browser/resources/google_now/background.js b/chrome/browser/resources/google_now/background.js index 1b7ba8503a..ef01369e63 100644 --- a/chrome/browser/resources/google_now/background.js +++ b/chrome/browser/resources/google_now/background.js @@ -81,10 +81,17 @@ var DISMISS_CARD_TASK_NAME = 'dismiss-card'; var RETRY_DISMISS_TASK_NAME = 'retry-dismiss'; var STATE_CHANGED_TASK_NAME = 'state-changed'; var SHOW_ON_START_TASK_NAME = 'show-cards-on-start'; +var ON_PUSH_MESSAGE_START_TASK_NAME = 'on-push-message'; var LOCATION_WATCH_NAME = 'location-watch'; /** + * Chrome push messaging subchannel for messages causing an immediate poll. + */ +var SUBCHANNEL_ID_POLL_NOW = 0; + +/** +/** * Notification as it's sent by the server. * * @typedef {{ @@ -152,7 +159,6 @@ wrapper.instrumentChromeApiFunction( 'notifications.onButtonClicked.addListener', 0); wrapper.instrumentChromeApiFunction('notifications.onClicked.addListener', 0); wrapper.instrumentChromeApiFunction('notifications.onClosed.addListener', 0); -wrapper.instrumentChromeApiFunction('omnibox.onInputEntered.addListener', 0); wrapper.instrumentChromeApiFunction( 'preferencesPrivate.googleGeolocationAccessEnabled.get', 1); @@ -160,6 +166,7 @@ wrapper.instrumentChromeApiFunction( 'preferencesPrivate.googleGeolocationAccessEnabled.onChange.addListener', 0); wrapper.instrumentChromeApiFunction('permissions.contains', 1); +wrapper.instrumentChromeApiFunction('pushMessaging.onMessage.addListener', 0); wrapper.instrumentChromeApiFunction('runtime.onInstalled.addListener', 0); wrapper.instrumentChromeApiFunction('runtime.onStartup.addListener', 0); wrapper.instrumentChromeApiFunction('tabs.create', 1); @@ -515,25 +522,53 @@ function parseAndShowNotificationCards(response) { } /** + * Requests notification cards from the server for specified groups. + * @param {Array.<string>} groupNames Names of groups that need to be refreshed. + */ +function requestNotificationGroups(groupNames) { + console.log('requestNotificationGroups from ' + NOTIFICATION_CARDS_URL + + ', groupNames=' + JSON.stringify(groupNames)); + + recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_TOTAL); + + var requestParameters = '?timeZoneOffsetMs=' + + (-new Date().getTimezoneOffset() * MS_IN_MINUTE); + + groupNames.forEach(function(groupName) { + requestParameters += ('&requestTypes=' + groupName); + }); + + console.log('requestNotificationGroups: request=' + requestParameters); + + var request = buildServerRequest('GET', 'notifications' + requestParameters); + + request.onloadend = function(event) { + console.log('requestNotificationGroups-onloadend ' + request.status); + if (request.status == HTTP_OK) { + recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_SUCCESS); + parseAndShowNotificationCards(request.response); + } + }; + + setAuthorization(request, function(success) { + if (success) + request.send(); + }); +} + +/** * Requests notification cards from the server. * @param {Location} position Location of this computer. */ function requestNotificationCards(position) { - console.log('requestNotificationCards ' + JSON.stringify(position) + - ' from ' + NOTIFICATION_CARDS_URL); - - if (!NOTIFICATION_CARDS_URL) - return; - - recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_TOTAL); + console.log('requestNotificationCards ' + JSON.stringify(position)); instrumented.storage.local.get('notificationGroups', function(items) { console.log('requestNotificationCards-storage-get ' + JSON.stringify(items)); items = items || {}; - var requestParameters = '?timeZoneOffsetMs=' + - (-new Date().getTimezoneOffset() * MS_IN_MINUTE); + var groupsToRequest = []; if (items.notificationGroups) { var now = Date.now(); @@ -541,27 +576,11 @@ function requestNotificationCards(position) { for (var groupName in items.notificationGroups) { var group = items.notificationGroups[groupName]; if (group.nextPollTime <= now) - requestParameters += ('&requestTypes=' + groupName); + groupsToRequest.push(groupName); } } - console.log('requestNotificationCards: request=' + requestParameters); - - var request = buildServerRequest('GET', - 'notifications' + requestParameters); - - request.onloadend = function(event) { - console.log('requestNotificationCards-onloadend ' + request.status); - if (request.status == HTTP_OK) { - recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_SUCCESS); - parseAndShowNotificationCards(request.response); - } - }; - - setAuthorization(request, function(success) { - if (success) - request.send(); - }); + requestNotificationGroups(groupsToRequest); }); } @@ -942,7 +961,7 @@ function updateRunningState( function onStateChange() { tasks.add(STATE_CHANGED_TASK_NAME, function() { authenticationManager.isSignedIn(function(token) { - var signedIn = !!token && !!NOTIFICATION_CARDS_URL; + var signedIn = !!token; instrumented.metricsPrivate.getVariationParams( 'GoogleNow', function(response) { @@ -1030,7 +1049,24 @@ instrumented.location.onLocationUpdate.addListener(function(position) { updateNotificationsCards(position); }); -instrumented.omnibox.onInputEntered.addListener(function(text) { - localStorage['server_url'] = NOTIFICATION_CARDS_URL = text; - initialize(); +instrumented.pushMessaging.onMessage.addListener(function(message) { + // message.payload will be '' when the extension first starts. + // Each time after signing in, we'll get latest payload for all channels. + // So, we need to poll the server only when the payload is non-empty and has + // changed. + console.log('pushMessaging.onMessage ' + JSON.stringify(message)); + if (message.subchannelId == SUBCHANNEL_ID_POLL_NOW && message.payload) { + tasks.add(ON_PUSH_MESSAGE_START_TASK_NAME, function() { + instrumented.storage.local.get('lastPollNowPayload', function(items) { + if (items && items.lastPollNowPayload != message.payload) { + chrome.storage.local.set({lastPollNowPayload: message.payload}); + + updateCardsAttempts.isRunning(function(running) { + if (running) + requestNotificationGroups([]); + }); + } + }); + }); + } }); diff --git a/chrome/browser/resources/google_now/background_test_util.js b/chrome/browser/resources/google_now/background_test_util.js index 4f5134bbed..2d7109ee1d 100644 --- a/chrome/browser/resources/google_now/background_test_util.js +++ b/chrome/browser/resources/google_now/background_test_util.js @@ -22,8 +22,8 @@ mockChromeEvent(instrumented, 'location.onLocationUpdate'); mockChromeEvent(instrumented, 'notifications.onButtonClicked'); mockChromeEvent(instrumented, 'notifications.onClicked'); mockChromeEvent(instrumented, 'notifications.onClosed'); -mockChromeEvent(instrumented, 'omnibox.onInputEntered'); mockChromeEvent( instrumented, 'preferencesPrivate.googleGeolocationAccessEnabled.onChange'); +mockChromeEvent(instrumented, 'pushMessaging.onMessage'); mockChromeEvent(instrumented, 'runtime.onInstalled'); mockChromeEvent(instrumented, 'runtime.onStartup'); diff --git a/chrome/browser/resources/google_now/background_unittest.gtestjs b/chrome/browser/resources/google_now/background_unittest.gtestjs index 21bd188282..a0c41ca729 100644 --- a/chrome/browser/resources/google_now/background_unittest.gtestjs +++ b/chrome/browser/resources/google_now/background_unittest.gtestjs @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(robliao,vadimt): Determine the granularity of testing to perform. + /** * Test fixture for background.js. * @constructor @@ -413,50 +415,13 @@ function expectInitialization(mockApisObj) { TEST_F( 'GoogleNowBackgroundUnitTest', - 'Initialize_ToastStateEmpty1', - function() { - // Tests the case when the user isn't signed in and NOTIFICATION_CARDS_URL - // is not set. Since NOTIFICATION_CARDS_URL is empty, - // nothing should start. - - // Setup and expectations. - NOTIFICATION_CARDS_URL = undefined; - var testIdentityToken = undefined; - var testGeolocationPref = false; - var testExperimentVariationParams = {}; - - mockInitializeDependencies(this); - - this.mockGlobals.expects(once()).recordEvent( - GoogleNowEvent.EXTENSION_START); - - this.mockGlobals.expects(once()).recordEvent( - GoogleNowEvent.STOPPED); - - expectInitialization(this.mockApis); - - expectStateMachineCalls( - this, - testIdentityToken, - testGeolocationPref, - testExperimentVariationParams); - - // TODO(robliao,vadimt): Determine the granularity of testing to perform. - - // Invoking the tested function. - initialize(); - }); - -TEST_F( - 'GoogleNowBackgroundUnitTest', - 'Initialize_ToastStateEmpty2', + 'Initialize_ToastStateEmpty', function() { - // Tests the case when NOTIFICATION_CARDS_URL is but getAuthToken fails - // most likely because the user is not signed in. In this case, the - // function should quietly exit after finding out that getAuthToken fails. + // Tests the case when getAuthToken fails most likely because the user is + // not signed in. In this case, the function should quietly exit after + // finding out that getAuthToken fails. // Setup and expectations. - NOTIFICATION_CARDS_URL = 'https://some.server.url.com'; var testIdentityToken = undefined; var testGeolocationPref = false; var testExperimentVariationParams = {}; @@ -483,14 +448,13 @@ TEST_F( TEST_F( 'GoogleNowBackgroundUnitTest', - 'DISABLED_Initialize_ToastStateEmpty3', + 'DISABLED_Initialize_ToastStateEmpty2', function() { - // Tests the case when NOTIFICATION_CARDS_URL is set, getAuthToken - // succeeds, and the user has never responded to the toast. + // Tests the case when getAuthToken succeeds, and the user has never + // responded to the toast. // In this case, the function should invoke showWelcomeToast(). // Setup and expectations. - NOTIFICATION_CARDS_URL = 'https://some.server.url.com'; var testIdentityToken = 'some identity token'; var testGeolocationPref = false; var testExperimentVariationParams = {}; @@ -525,7 +489,6 @@ TEST_F('GoogleNowBackgroundUnitTest', 'Initialize_RunGoogleNow', function() { // of the required state is fulfilled. // Setup and expectations. - NOTIFICATION_CARDS_URL = 'https://some.server.url.com'; var testIdentityToken = 'some identity token'; var testGeolocationPref = true; var testExperimentVariationParams = {}; @@ -557,7 +520,6 @@ TEST_F( // Geolocation Preference after the user responded to the toast. // Setup and expectations. - NOTIFICATION_CARDS_URL = 'https://some.server.url.com'; var testIdentityToken = 'some identity token'; var testGeolocationPref = false; var testExperimentVariationParams = {}; diff --git a/chrome/browser/resources/google_now/manifest.json b/chrome/browser/resources/google_now/manifest.json index 3bbf433483..d0668ef07d 100644 --- a/chrome/browser/resources/google_now/manifest.json +++ b/chrome/browser/resources/google_now/manifest.json @@ -1,21 +1,22 @@ { - //chrome-extension://pmofbkohncoogjjhahejjfbppikbjigm + //chrome-extension://pafkbggdmjlpgkdkcbjmhmfcdpncadgh "name": "Google Now", "version": "1.2.0.1", "description": "Integrates Google Now into Chrome.", - "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDh/njCc/GTuP7uYix39uinhkX+7RyEoMaOQyoc065gsQ5b9cLpZToK78xgpsc9ThrpLVDOwqz6cGeLgIk+kPeRMQe07T+/mh3U5klegDx9pfr9T3aiRNQnJYJv8niVs9aJ/sBSqxtHt2LlhEt9ajFXue7Q3LkzyTlXpxoEFH1keQIDAQAB", + "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkhqJr32OFD/bMXW4Md7jMfd7LbwHXVc6x5bBQG5U+dloofoxrICDR20yur/40mQ8O//0sS1b8srvbab1CRlSrxoNCr9T80NAkfzx0gHyVS+p1Zow+1FzLMu9PiGwwFyN80HIB7GI/dIa0wC9K/2OrrzcHEhVH96DacTtWQqjfDVtZPjT7Xwv23dgoWcpbkRC86jMJot3dmX9xnn0KzoVc9gDOHSIkBLbkkr6Sp3LGXCCM4L0DJgxdFwaLr5WBzgC3y5x0/wwPIwN4PtIaK3BhH6njlksfnKwwIJ9iRT41V4BqbWu4mszO/7VJ3HJyw2DBpIc2grU9ZRRxrV3fRQG4wIDAQAB", "permissions": [ "alarms", + "identity", "location", "metricsPrivate", "notifications", "preferencesPrivate", + "pushMessaging", "storage", "tabs", // TODO(vadimt): Replace <all_urls> with real URL patterns once we know // them. - "<all_urls>", - "identity" + "<all_urls>" ], "optional_permissions": ["background"], "manifest_version": 2, @@ -23,8 +24,6 @@ "scripts": ["utility.js", "cards.js", "background.js"], "persistent": false }, - // TODO(vadimt): Remove using chrome.omnibox. - "omnibox": { "keyword" : "gn_url" }, "oauth2": { "auto_approve": true, "scopes": ["https://www.googleapis.com/auth/googlenow"] diff --git a/chrome/browser/resources/google_now/utility.js b/chrome/browser/resources/google_now/utility.js index a8e96d7d69..418b6f3098 100644 --- a/chrome/browser/resources/google_now/utility.js +++ b/chrome/browser/resources/google_now/utility.js @@ -22,18 +22,28 @@ * when a task completes. */ -// TODO(vadimt): Figure out the server name. Use it in the manifest and for -// NOTIFICATION_CARDS_URL. Meanwhile, to use the feature, you need to manually -// set the server name via local storage. +// TODO(vadimt): Use server name in the manifest. /** * Notification server URL. */ -var NOTIFICATION_CARDS_URL = localStorage['server_url']; +var NOTIFICATION_CARDS_URL = 'https://www.googleapis.com/chromenow/v1beta1'; var DEBUG_MODE = localStorage['debug_mode']; /** + * Initializes for debug or release modes of operation. + */ +function initializeDebug() { + if (DEBUG_MODE) { + NOTIFICATION_CARDS_URL = + localStorage['server_url'] || NOTIFICATION_CARDS_URL; + } +} + +initializeDebug(); + +/** * Builds an error object with a message that may be sent to the server. * @param {string} message Error message. This message may be sent to the * server. @@ -89,8 +99,8 @@ function sendErrorReport(error) { if (topFrame) { // Examples of a frame: // 1. '\n at someFunction (chrome-extension:// - // pmofbkohncoogjjhahejjfbppikbjigm/background.js:915:15)\n' - // 2. '\n at chrome-extension://pmofbkohncoogjjhahejjfbppikbjigm/ + // pafkbggdmjlpgkdkcbjmhmfcdpncadgh/background.js:915:15)\n' + // 2. '\n at chrome-extension://pafkbggdmjlpgkdkcbjmhmfcdpncadgh/ // utility.js:269:18\n' // 3. '\n at Function.target.(anonymous function) (extensions:: // SafeBuiltins:19:14)\n' @@ -107,7 +117,7 @@ function sendErrorReport(error) { var topFrameElements = errorLocation.split(':'); // topFrameElements is an array that ends like: - // [N-3] //pmofbkohncoogjjhahejjfbppikbjigm/utility.js + // [N-3] //pafkbggdmjlpgkdkcbjmhmfcdpncadgh/utility.js // [N-2] 308 // [N-1] 19 if (topFrameElements.length >= 3) { diff --git a/chrome/browser/resources/history/history.js b/chrome/browser/resources/history/history.js index 15884eeef7..a524b7e614 100644 --- a/chrome/browser/resources/history/history.js +++ b/chrome/browser/resources/history/history.js @@ -1544,21 +1544,19 @@ function load() { cr.ui.FocusManager.disableMouseFocusOnButtons(); if (isMobileVersion()) { - if (searchField) { - // Move the search box out of the header. - var resultsDisplay = $('results-display'); - resultsDisplay.parentNode.insertBefore($('search-field'), resultsDisplay); - - window.addEventListener( - 'resize', historyView.updateClearBrowsingDataButton_); - - // When the search field loses focus, add a delay before updating the - // visibility, otherwise the button will flash on the screen before the - // keyboard animates away. - searchField.addEventListener('blur', function() { - setTimeout(historyView.updateClearBrowsingDataButton_, 250); - }); - } + // Move the search box out of the header. + var resultsDisplay = $('results-display'); + resultsDisplay.parentNode.insertBefore($('search-field'), resultsDisplay); + + window.addEventListener( + 'resize', historyView.updateClearBrowsingDataButton_); + + // When the search field loses focus, add a delay before updating the + // visibility, otherwise the button will flash on the screen before the + // keyboard animates away. + searchField.addEventListener('blur', function() { + setTimeout(historyView.updateClearBrowsingDataButton_, 250); + }); // Move the button to the bottom of the page. $('history-page').appendChild($('clear-browsing-data')); diff --git a/chrome/browser/resources/inspect/OWNERS b/chrome/browser/resources/inspect/OWNERS index 2abc7be24f..c847e419ec 100644 --- a/chrome/browser/resources/inspect/OWNERS +++ b/chrome/browser/resources/inspect/OWNERS @@ -1 +1,2 @@ +kaznacheev@chromium.org pfeldman@chromium.org diff --git a/chrome/browser/resources/inspect/inspect.css b/chrome/browser/resources/inspect/inspect.css index 2883653f92..6c4c680223 100644 --- a/chrome/browser/resources/inspect/inspect.css +++ b/chrome/browser/resources/inspect/inspect.css @@ -17,6 +17,7 @@ img { height: 16px; padding-left: 2px; padding-right: 5px; + position: absolute; width: 16px; } @@ -153,6 +154,10 @@ img { margin-right: 0.5em; } +.subrow.main { + margin-left: 21px; +} + .webview-thumbnail { -webkit-box-align: center; -webkit-box-orient: horizontal; diff --git a/chrome/browser/resources/inspect/inspect.js b/chrome/browser/resources/inspect/inspect.js index bd1e769b0b..988b425cdf 100644 --- a/chrome/browser/resources/inspect/inspect.js +++ b/chrome/browser/resources/inspect/inspect.js @@ -313,11 +313,13 @@ function addToPagesList(data) { } function addToExtensionsList(data) { - addTargetToList(data, $('extensions-list'), ['name', 'url']); + var row = addTargetToList(data, $('extensions-list'), ['name', 'url']); + addFavicon(row, data); } function addToAppsList(data) { var row = addTargetToList(data, $('apps-list'), ['name', 'url']); + addFavicon(row, data); if (data.guests) { Array.prototype.forEach.call(data.guests, function(guest) { var guestRow = addTargetToList(guest, row, ['name', 'url']); diff --git a/chrome/browser/resources/media/webrtc_logs.css b/chrome/browser/resources/media/webrtc_logs.css index 49e6aa9e1a..a2dd3e4ac1 100644 --- a/chrome/browser/resources/media/webrtc_logs.css +++ b/chrome/browser/resources/media/webrtc_logs.css @@ -41,8 +41,3 @@ html[dir=rtl] h1 { #log-list > div:not(:last-child) { border-bottom: 1px solid #bbb; } - -#disabled-mode h2 { - color: rgb(141, 51, 42); - font-size: 125%; -} diff --git a/chrome/browser/resources/media/webrtc_logs.html b/chrome/browser/resources/media/webrtc_logs.html index 59517c2518..0e6241778c 100644 --- a/chrome/browser/resources/media/webrtc_logs.html +++ b/chrome/browser/resources/media/webrtc_logs.html @@ -11,15 +11,9 @@ </head> <body i18n-values=".style.fontFamily:fontfamily;.style.fontSize:fontsize"> <header><h1 i18n-content="webrtcLogsTitle"></h1></header> - <div id="enabled-mode"> - <h2 id="log-banner"></h2> - <div id="log-list"></div> - <p id="no-logs" i18n-content="noLogsMessage" hidden></p> - </div> - <div id="disabled-mode" hidden> - <h2 i18n-content="disabledHeader"></h2> - <p i18n-values=".innerHTML:disabledMessage"></p> - </div> + <h2 id="log-banner"></h2> + <div id="log-list"></div> + <p id="no-logs" i18n-content="noLogsMessage" hidden></p> <script src="chrome://resources/js/i18n_template2.js"></script> <script src="chrome://resources/js/jstemplate_compiled.js"></script> </body> diff --git a/chrome/browser/resources/media/webrtc_logs.js b/chrome/browser/resources/media/webrtc_logs.js index 8537551713..508af7d00c 100644 --- a/chrome/browser/resources/media/webrtc_logs.js +++ b/chrome/browser/resources/media/webrtc_logs.js @@ -11,22 +11,15 @@ function requestUploads() { /** * Callback from backend with the list of uploads. Builds the UI. - * @param {boolean} enabled Whether or not uploading is enabled. * @param {array} uploads The list of uploads. * @param {string} version The browser version. */ -function updateWebRtcLogsList(enabled, uploads, version) { +function updateWebRtcLogsList(uploads, version) { $('log-banner').textContent = loadTimeData.getStringF('webrtcLogCountFormat', uploads.length); var logSection = $('log-list'); - $('enabled-mode').hidden = !enabled; - $('disabled-mode').hidden = enabled; - - if (!enabled) - return; - // Clear any previous list. logSection.textContent = ''; diff --git a/chrome/browser/resources/net_internals/cros_log_analyzer_view.html b/chrome/browser/resources/net_internals/cros_log_analyzer_view.html index 4c18f5c536..19c76ffe51 100644 --- a/chrome/browser/resources/net_internals/cros_log_analyzer_view.html +++ b/chrome/browser/resources/net_internals/cros_log_analyzer_view.html @@ -46,7 +46,7 @@ <label for="checkbox-info">Info</label> </span> <span class="cros-log-analyzer-filter-level-block"> - <input type='checkbox' id='checkbox-unknown'checked=true> + <input type='checkbox' id='checkbox-unknown' checked=true> <label for="checkbox-unknown">Unknown</label> </span> </div> diff --git a/chrome/browser/resources/net_internals/cros_log_visualizer_view.html b/chrome/browser/resources/net_internals/cros_log_visualizer_view.html index 0803884e5f..ee6aa32dff 100644 --- a/chrome/browser/resources/net_internals/cros_log_visualizer_view.html +++ b/chrome/browser/resources/net_internals/cros_log_visualizer_view.html @@ -45,7 +45,7 @@ <label for="checkbox-info">Info</label> </span> <span class="cros-log-visualizer-filter-level-block"> - <input type='checkbox' id='checkbox-unknown'checked=true> + <input type='checkbox' id='checkbox-unknown' checked=true> <label for="checkbox-unknown">Unknown</label> </span> </div> diff --git a/chrome/browser/resources/options/browser_options.css b/chrome/browser/resources/options/browser_options.css index 13ad987c9a..d2a120762a 100644 --- a/chrome/browser/resources/options/browser_options.css +++ b/chrome/browser/resources/options/browser_options.css @@ -402,3 +402,12 @@ list:not([disabled]) > .network-group[selected] { /* Same as .settings-row {margin}. */ -webkit-border-vertical-spacing: 0.65em; } + +#accessibility-autoclick .controlled-setting-with-label { + -webkit-box-align: baseline; +} + +#accessibility-autoclick label + select { + /* Same as .controlled-setting-with-label > input + span. */ + -webkit-margin-start: 0.6em; +} diff --git a/chrome/browser/resources/options/browser_options.html b/chrome/browser/resources/options/browser_options.html index 06abb969b1..5fd32d5d04 100644 --- a/chrome/browser/resources/options/browser_options.html +++ b/chrome/browser/resources/options/browser_options.html @@ -747,6 +747,46 @@ </label> </div> </div> + <div class="option-name" id="accessibility-autoclick" hidden> + <div class="checkbox"> + <span class="controlled-setting-with-label"> + <input id="accessibility-autoclick-check" + pref="settings.a11y.autoclick" type="checkbox"> + <span> + <div> + <div> + <label for="accessibility-autoclick-check" + i18n-content="accessibilityAutoclick"> + </label> + <span class="controlled-setting-indicator" + pref="settings.a11y.autoclick"> + </span> + </div> + <div> + <label for="accessibility-autoclick-dropdown" + i18n-content="accessibilityAutoclickDropdown"> + </label> + <select id="accessibility-autoclick-dropdown" class="control" + data-type="number" + pref="settings.a11y.autoclick_delay_ms"> + <option value="200" + i18n-content="autoclickDelayExtremelyShort"></option> + <option value="400" + i18n-content="autoclickDelayVeryShort"></option> + <option value="600" i18n-content="autoclickDelayShort"></option> + <option value="800" i18n-content="autoclickDelayLong"></option> + <option value="1000" + i18n-content="autoclickDelayVeryLong"></option> + </select> + <span class="controlled-setting-indicator" + pref="settings.a11y.autoclick_delay_ms"> + </span> + </div> + </div> + </span> + </span> + </div> + </div> </section> <if expr="pp_ifdef('chromeos')"> <section id="factory-reset-section" hidden> diff --git a/chrome/browser/resources/options/browser_options.js b/chrome/browser/resources/options/browser_options.js index 9a5d39a00e..270ac8bd4b 100644 --- a/chrome/browser/resources/options/browser_options.js +++ b/chrome/browser/resources/options/browser_options.js @@ -106,7 +106,7 @@ cr.define('options', function() { Preferences.getInstance().addEventListener('session.restore_on_startup', this.onRestoreOnStartupChanged_.bind(this)); Preferences.getInstance().addEventListener( - 'session.urls_to_restore_on_startup', + 'session.startup_urls', function(event) { $('startup-set-pages').disabled = event.value.disabled; }); @@ -445,6 +445,8 @@ cr.define('options', function() { }; $('accessibility-sticky-keys').hidden = !loadTimeData.getBoolean('enableStickyKeys'); + $('accessibility-autoclick').hidden = + !loadTimeData.getBoolean('enableAutoclick'); } // Display management section (CrOS only). diff --git a/chrome/browser/resources/options/chromeos/internet_detail.js b/chrome/browser/resources/options/chromeos/internet_detail.js index 9b3d45edd3..cbc07f21d2 100644 --- a/chrome/browser/resources/options/chromeos/internet_detail.js +++ b/chrome/browser/resources/options/chromeos/internet_detail.js @@ -73,6 +73,17 @@ cr.define('options.internet', function() { return value ? String(value) : ''; } + /** + * Sends the 'checked' state of a control to chrome for a network. + * @param {string} path The service path of the network. + * @param {string} message The message to send to chrome. + * @param {HTMLInputElement} checkbox The checkbox storing the value to send. + */ + function sendCheckedIfEnabled(path, message, checkbox) { + if (!checkbox.hidden && !checkbox.disabled) + chrome.send(message, [path, checkbox.checked ? 'true' : 'false']); + } + ///////////////////////////////////////////////////////////////////////////// // DetailsInternetPage class: @@ -708,28 +719,22 @@ cr.define('options.internet', function() { var data = $('connection-state').data; var servicePath = data.servicePath; if (data.type == Constants.TYPE_WIFI) { - chrome.send('setPreferNetwork', - [servicePath, - $('prefer-network-wifi').checked ? 'true' : 'false']); - chrome.send('setAutoConnect', - [servicePath, - $('auto-connect-network-wifi').checked ? 'true' : 'false']); + sendCheckedIfEnabled(servicePath, 'setPreferNetwork', + $('prefer-network-wifi')); + sendCheckedIfEnabled(servicePath, 'setAutoConnect', + $('auto-connect-network-wifi')); } else if (data.type == Constants.TYPE_WIMAX) { - chrome.send('setAutoConnect', - [servicePath, - $('auto-connect-network-wimax').checked ? 'true' : 'false']); + sendCheckedIfEnabled(servicePath, 'setAutoConnect', + $('auto-connect-network-wimax')); } else if (data.type == Constants.TYPE_CELLULAR) { - chrome.send('setAutoConnect', - [servicePath, - $('auto-connect-network-cellular').checked ? 'true' : - 'false']); + sendCheckedIfEnabled(servicePath, 'setAutoConnect', + $('auto-connect-network-cellular')); } else if (data.type == Constants.TYPE_VPN) { chrome.send('setServerHostname', [servicePath, $('inet-server-hostname').value]); - chrome.send('setAutoConnect', - [servicePath, - $('auto-connect-network-vpn').checked ? 'true' : 'false']); + sendCheckedIfEnabled(servicePath, 'setAutoConnect', + $('auto-connect-network-vpn')); } var nameServerTypes = ['automatic', 'google', 'user']; @@ -1240,7 +1245,8 @@ cr.define('options.internet', function() { indicators[i].handlePrefChange(event); var forElement = $(indicators[i].getAttribute('for')); if (forElement) { - forElement.disabled = propData.controlledBy == 'policy'; + if (propData.controlledBy == 'policy') + forElement.disabled = true; if (forElement.resetHandler) indicators[i].resetHandler = forElement.resetHandler; } diff --git a/chrome/browser/resources/options/options_page.js b/chrome/browser/resources/options/options_page.js index d9d2a34970..c0a1787121 100644 --- a/chrome/browser/resources/options/options_page.js +++ b/chrome/browser/resources/options/options_page.js @@ -226,10 +226,8 @@ cr.define('options', function() { // The page is already in history (the user may have clicked the same link // twice). Do nothing. - if (path == page.name && - !document.documentElement.classList.contains('loading')) { + if (path == page.name && this.loading) return; - } var hash = opt_params && opt_params.ignoreHash ? '' : window.location.hash; @@ -804,6 +802,15 @@ cr.define('options', function() { }, /** + * Whether the page is still loading (i.e. onload hasn't finished running). + * @return {boolean} Whether the page is still loading. + * @private + */ + get loading() { + return document.documentElement.classList.contains('loading'); + }, + + /** * Gets page visibility state. * @type {boolean} */ @@ -874,14 +881,17 @@ cr.define('options', function() { } var self = this; - // TODO(flackr): Use an event delegate to avoid having to subscribe and - // unsubscribe for webkitTransitionEnd events. - container.addEventListener('webkitTransitionEnd', function f(e) { - if (e.target != e.currentTarget || e.propertyName != 'opacity') - return; - container.removeEventListener('webkitTransitionEnd', f); - self.fadeCompleted_(); - }); + var loading = this.loading; + if (!loading) { + // TODO(flackr): Use an event delegate to avoid having to subscribe and + // unsubscribe for webkitTransitionEnd events. + container.addEventListener('webkitTransitionEnd', function f(e) { + if (e.target != e.currentTarget || e.propertyName != 'opacity') + return; + container.removeEventListener('webkitTransitionEnd', f); + self.fadeCompleted_(); + }); + } if (visible) { container.hidden = false; @@ -903,6 +913,8 @@ cr.define('options', function() { document.activeElement.blur(); container.classList.add('transparent'); } + if (loading) + this.fadeCompleted_(); }, /** diff --git a/chrome/browser/resources/options/startup_overlay.html b/chrome/browser/resources/options/startup_overlay.html index f025cca69e..645bb7ce3b 100644 --- a/chrome/browser/resources/options/startup_overlay.html +++ b/chrome/browser/resources/options/startup_overlay.html @@ -5,7 +5,7 @@ its 'controlled-by' attribute will get set when the urls preference is managed by a policy, so that the managed prefs bar will show up. --> - <input type="text" pref="session.urls_to_restore_on_startup" hidden> + <input type="text" pref="session.startup_urls" hidden> <div class="content-area"> <list id="startupPagesList"></list> </div> diff --git a/chrome/browser/resources/options/startup_overlay.js b/chrome/browser/resources/options/startup_overlay.js index dc74f48254..d63b49d102 100644 --- a/chrome/browser/resources/options/startup_overlay.js +++ b/chrome/browser/resources/options/startup_overlay.js @@ -34,7 +34,7 @@ cr.define('options', function() { autocompleteList_: null, startup_pages_pref_: { - 'name': 'session.urls_to_restore_on_startup', + 'name': 'session.startup_urls', 'disabled': false }, @@ -108,7 +108,7 @@ cr.define('options', function() { /** * Handles change events of the preference - * 'session.urls_to_restore_on_startup'. + * 'session.startup_urls'. * @param {event} preference changed event. * @private */ diff --git a/chrome/browser/resources/options/startup_section.html b/chrome/browser/resources/options/startup_section.html index 676dbbdb01..2eaea81c0b 100644 --- a/chrome/browser/resources/options/startup_section.html +++ b/chrome/browser/resources/options/startup_section.html @@ -44,7 +44,7 @@ i18n-content="startupSetPages"> </button> <span class="controlled-setting-indicator" - pref="session.urls_to_restore_on_startup"> + pref="session.startup_urls"> </span> </span> </label> diff --git a/chrome/browser/resources/plugin_metadata/plugins_win.json b/chrome/browser/resources/plugin_metadata/plugins_win.json index 5bdbc8d3c3..bed9d9545e 100644 --- a/chrome/browser/resources/plugin_metadata/plugins_win.json +++ b/chrome/browser/resources/plugin_metadata/plugins_win.json @@ -1,5 +1,5 @@ { - "x-version": 10, + "x-version": 11, "google-talk": { "mime_types": [ ], @@ -54,18 +54,9 @@ ], "versions": [ { - "version": "6.0.450", + "version": "10.45", "status": "requires_authorization", - "comment": "Java SE 6 Update 45. '450' is not a typo." - }, - { - "version": "7", - "status": "out_of_date" - }, - { - "version": "10.21", - "status": "requires_authorization", - "comment": "Java SE 7u17" + "comment": "Java SE 7u45" } ], "lang": "en-US", @@ -164,9 +155,9 @@ ], "versions": [ { - "version": "12.0.2.122", + "version": "12.0.4.144", "status": "requires_authorization", - "reference": "https://www.adobe.com/support/security/bulletins/apsb13-12.html" + "reference": "https://www.adobe.com/support/security/bulletins/apsb13-23.html" } ], "lang": "en-US", @@ -186,33 +177,24 @@ ], "versions": [ { - "version": "9.5.5", - "status": "requires_authorization", - "reference": "https://www.adobe.com/support/security/bulletins/apsb13-15.html" - }, - { - "version": "10", - "status": "out_of_date" - }, - { - "version": "10.1.7", + "version": "10.1.8", "status": "requires_authorization", - "reference": "https://www.adobe.com/support/security/bulletins/apsb13-15.html" + "reference": "https://www.adobe.com/support/security/bulletins/apsb13-22.html" }, { "version": "11", "status": "out_of_date" }, { - "version": "11.0.3", + "version": "11.0.5", "status": "up_to_date", - "reference": "https://www.adobe.com/support/security/bulletins/apsb13-15.html" + "reference": "https://www.adobe.com/support/security/bulletins/apsb13-25.html" } ], "lang": "en-US", "name": "Adobe Reader", "help_url": "https://support.google.com/chrome/?p=plugin_pdf", - "url": "http://ardownload.adobe.com/pub/adobe/reader/win/11.x/11.0.03/en_US/AdbeRdr11003_en_US.exe", + "url": "http://www.adobe.com/support/downloads/detail.jsp?ftpID=5674", "group_name_matcher": "*Adobe Acrobat*" }, "apple-quicktime": { diff --git a/chrome/browser/resources/print_preview/print_preview.css b/chrome/browser/resources/print_preview/print_preview.css index df354a65d6..d430924809 100644 --- a/chrome/browser/resources/print_preview/print_preview.css +++ b/chrome/browser/resources/print_preview/print_preview.css @@ -113,7 +113,7 @@ h1 { #print-preview .navbar-link { -webkit-margin-start: 20px; - height: 29px; + height: 32px; outline: 0; padding: 0; text-align: start; diff --git a/chrome/browser/resources/ssl/blocking.css b/chrome/browser/resources/ssl/blocking.css new file mode 100644 index 0000000000..221dc3487a --- /dev/null +++ b/chrome/browser/resources/ssl/blocking.css @@ -0,0 +1,29 @@ +/* Copyright 2013 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. */ + +.explanation-par { + line-height: 18px; + padding: 0 20px 20px 20px; +} + +#icon-lock { + content: -webkit-image-set( + url('images/1x/locked_page.png') 1x, + url('images/2x/locked_page.png') 2x); + padding-bottom: 15px; + padding-top: 10px; +} + +/* Decrease padding at low sizes. */ +@media (max-width: 640px), +@media (max-height: 640px) { + .explanation-par, + .ssl-help-box-inner { + padding: 0; + } +} + +.ssl-help-box-inner { + padding: 0 20px; +} diff --git a/chrome/browser/resources/ssl/blocking.html b/chrome/browser/resources/ssl/blocking.html new file mode 100644 index 0000000000..004cf225cf --- /dev/null +++ b/chrome/browser/resources/ssl/blocking.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html i18n-values="dir:textDirection"> +<head> + <meta name="viewport" content="width=device-width, initial-scale=1.0, + maximum-scale=1.0, user-scalable=no"> + <title i18n-content="title"></title> + <link rel="stylesheet" href="../../../renderer/resources/neterror.css"> + <link rel="stylesheet" href="blocking.css"> + <script src="../../../../ui/webui/resources/js/util.js"></script> + <script src="../../../renderer/resources/neterror.js"></script> + <script src="ssl_errors_common.js"></script> + <script src="blocking.js"></script> +</head> +<body> + <div id="main-frame-error"> + <div id="box"> + <div id="content-top"> + <h1> + <div> + <img class="icon" id="icon-lock"> + </div> + <span i18n-content="headline"></span> + </h1> + <p i18n-values=".innerHTML:message" class="explanation-par"></p> + <div id="buttons"> + <button id="reload-button" i18n-content="reloadMsg"></button> + <button id="more-less-button" i18n-content="more"></button> + </div> + </div> + <div id="help-box-outer" class="hidden"> + <div id="help-box-inner"> + <div class="ssl-help-box-inner"> + <h2 i18n-content="moreTitle"></h2> + <p i18n-values=".innerHTML:moreMessage"></p> + </div> + <div class="ssl-help-box-inner"> + <h2 i18n-content="techTitle"></h2> + <p> + <span i18n-content="failure"></span><br> + <span i18n-content="errorType"></span><br> + <span i18n-content="subject"></span><br> + <span i18n-content="issuer"></span><br> + <span i18n-content="fingerprint"></span> + </p> + </div> + </div> + </div> + </div> + </div> +</body> +</html> diff --git a/chrome/browser/resources/ssl/blocking.js b/chrome/browser/resources/ssl/blocking.js new file mode 100644 index 0000000000..6442c386fd --- /dev/null +++ b/chrome/browser/resources/ssl/blocking.js @@ -0,0 +1,25 @@ +// Copyright 2013 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. + +function toggleMoreBox() { + var helpBoxOuter = $('help-box-outer'); + helpBoxOuter.classList.toggle('hidden'); + var moreLessButton = $('more-less-button'); + if (helpBoxOuter.classList.contains('hidden')) { + moreLessButton.innerText = templateData.more; + } else { + moreLessButton.innerText = templateData.less; + } +} + +function reloadPage() { + sendCommand(CMD_RELOAD); +} + +function setupEvents() { + $('reload-button').addEventListener('click', reloadPage); + $('more-less-button').addEventListener('click', toggleMoreBox); +} + +document.addEventListener('DOMContentLoaded', setupEvents); diff --git a/chrome/browser/resources/ssl/images/1x/locked_page.png b/chrome/browser/resources/ssl/images/1x/locked_page.png Binary files differnew file mode 100644 index 0000000000..fb7e2185be --- /dev/null +++ b/chrome/browser/resources/ssl/images/1x/locked_page.png diff --git a/chrome/browser/resources/ssl/images/2x/locked_page.png b/chrome/browser/resources/ssl/images/2x/locked_page.png Binary files differnew file mode 100644 index 0000000000..94aa4e970f --- /dev/null +++ b/chrome/browser/resources/ssl/images/2x/locked_page.png diff --git a/chrome/browser/resources/ssl/roadblock.html b/chrome/browser/resources/ssl/roadblock.html index 8bebfc610d..f2c7f4369c 100644 --- a/chrome/browser/resources/ssl/roadblock.html +++ b/chrome/browser/resources/ssl/roadblock.html @@ -78,95 +78,8 @@ } </style> <script src="../../../../ui/webui/resources/js/assert.js"></script> - <script> - // Should match SSLBlockingPageCommands in ssl_blocking_page.cc. - var CMD_DONT_PROCEED = 0; - var CMD_PROCEED = 1; - var CMD_FOCUS = 2; - var CMD_MORE = 3; - - var showedMore = false; - var keyPressState = 0; - var gainFocus = false; - var setupExperiment = false; - - function $(o) { - return document.getElementById(o); - } - - function sendCommand(cmd) { - window.domAutomationController.setAutomationId(1); - window.domAutomationController.send(cmd); - } - - function toggleMoreInfo(collapse) { - $('more-info-long').hidden = collapse; - $('more-info-short').hidden = !collapse; - if (!collapse && !showedMore) { - sendCommand(CMD_MORE); - showedMore = true; - } - } - - // This allows errors to be skippped by typing "proceed" into the page. - function keyPressHandler(e) { - var sequence = 'proceed'; - if (sequence.charCodeAt(keyPressState) == e.keyCode) { - keyPressState++; - if (keyPressState == sequence.length) { - sendCommand(CMD_PROCEED); - keyPressState = 0; - } - } else { - keyPressState = 0; - } - } - - // Supports UMA timing, which starts after the warning is first viewed. - function handleFocusEvent() { - if (gainFocus == false) { - sendCommand(CMD_FOCUS); - gainFocus = true; - } - } - - // UI modifications and event listeners that take place after load. - function setupEvents() { - if (templateData.errorType == "overridable") { - // This is the blocking page you can click through. - $('proceed-button').hidden = false; - $('proceed-button').addEventListener('click', function() { - sendCommand(CMD_PROCEED); - }); - } else { - document.addEventListener('keypress', keyPressHandler); - } - - if ($('more-info-title').textContent == '') { - $('more-info-short').hidden = true; - $('more-info-long').hidden = true; - $('twisty-closed').style.display = 'none'; - } else { - $('more-info-short').addEventListener('click', function() { - toggleMoreInfo(false); - }); - $('more-info-long').addEventListener('click', function() { - toggleMoreInfo(true); - }); - } - - $('exit-button').addEventListener('click', function() { - sendCommand(CMD_DONT_PROCEED); - }); - - document.addEventListener('contextmenu', function(e) { - e.preventDefault(); - }); - } - - window.addEventListener('focus', handleFocusEvent); - document.addEventListener('DOMContentLoaded', setupEvents); - </script> + <script src="ssl_errors_common.js"></script> + <script src="roadblock.js"></script> </head> <body> <div class="box"> diff --git a/chrome/browser/resources/ssl/roadblock.js b/chrome/browser/resources/ssl/roadblock.js new file mode 100644 index 0000000000..8e0d27f15f --- /dev/null +++ b/chrome/browser/resources/ssl/roadblock.js @@ -0,0 +1,41 @@ +// Copyright 2013 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. + +var showedMore = false; + +function toggleMoreInfo(collapse) { + $('more-info-long').hidden = collapse; + $('more-info-short').hidden = !collapse; + if (!collapse && !showedMore) { + sendCommand(CMD_MORE); + showedMore = true; + } +} + +// UI modifications and event listeners that take place after load. +function setupEvents() { + $('proceed-button').hidden = false; + $('proceed-button').addEventListener('click', function() { + sendCommand(CMD_PROCEED); + }); + + if ($('more-info-title').textContent == '') { + $('more-info-short').hidden = true; + $('more-info-long').hidden = true; + $('twisty-closed').style.display = 'none'; + } else { + $('more-info-short').addEventListener('click', function() { + toggleMoreInfo(false); + }); + $('more-info-long').addEventListener('click', function() { + toggleMoreInfo(true); + }); + } + + $('exit-button').addEventListener('click', function() { + sendCommand(CMD_DONT_PROCEED); + }); +} + +document.addEventListener('DOMContentLoaded', setupEvents); diff --git a/chrome/browser/resources/ssl/ssl_errors_common.js b/chrome/browser/resources/ssl/ssl_errors_common.js new file mode 100644 index 0000000000..2c488e3e4f --- /dev/null +++ b/chrome/browser/resources/ssl/ssl_errors_common.js @@ -0,0 +1,44 @@ +// Copyright 2013 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. + +// Should match SSLBlockingPageCommands in ssl_blocking_page.cc. +var CMD_DONT_PROCEED = 0; +var CMD_PROCEED = 1; +var CMD_FOCUS = 2; +var CMD_MORE = 3; +var CMD_RELOAD = 4; + +var keyPressState = 0; + +function $(o) { + return document.getElementById(o); +} + +function sendCommand(cmd) { + window.domAutomationController.setAutomationId(1); + window.domAutomationController.send(cmd); +} + +// This allows errors to be skippped by typing "proceed" into the page. +function keyPressHandler(e) { + var sequence = 'proceed'; + if (sequence.charCodeAt(keyPressState) == e.keyCode) { + keyPressState++; + if (keyPressState == sequence.length) { + sendCommand(CMD_PROCEED); + keyPressState = 0; + } + } else { + keyPressState = 0; + } +} + +function sharedSetup() { + document.addEventListener('contextmenu', function(e) { + e.preventDefault(); + }); + document.addEventListener('keypress', keyPressHandler); +} + +document.addEventListener('DOMContentLoaded', sharedSetup); diff --git a/chrome/browser/search/hotword_service.cc b/chrome/browser/search/hotword_service.cc new file mode 100644 index 0000000000..1ebecaf49c --- /dev/null +++ b/chrome/browser/search/hotword_service.cc @@ -0,0 +1,15 @@ +// Copyright 2013 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/search/hotword_service.h" + +#include "chrome/browser/profiles/profile.h" + + +HotwordService::HotwordService(Profile* profile) + : profile_(profile) { +} + +HotwordService::~HotwordService() { +} diff --git a/chrome/browser/search/hotword_service.h b/chrome/browser/search/hotword_service.h new file mode 100644 index 0000000000..17bf69dfa3 --- /dev/null +++ b/chrome/browser/search/hotword_service.h @@ -0,0 +1,26 @@ +// Copyright 2013 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_SEARCH_HOTWORD_SERVICE_H_ +#define CHROME_BROWSER_SEARCH_HOTWORD_SERVICE_H_ + +#include "base/basictypes.h" +#include "components/browser_context_keyed_service/browser_context_keyed_service.h" + +class Profile; + +// Provides an interface for the Hotword component that does voice triggered +// search. +class HotwordService : public BrowserContextKeyedService { + public: + explicit HotwordService(Profile* profile); + virtual ~HotwordService(); + + private: + Profile* profile_; + + DISALLOW_COPY_AND_ASSIGN(HotwordService); +}; + +#endif // CHROME_BROWSER_SEARCH_HOTWORD_SERVICE_H_ diff --git a/chrome/browser/search/hotword_service_factory.cc b/chrome/browser/search/hotword_service_factory.cc new file mode 100644 index 0000000000..c465e08cc2 --- /dev/null +++ b/chrome/browser/search/hotword_service_factory.cc @@ -0,0 +1,54 @@ +// Copyright 2013 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/search/hotword_service_factory.h" + +#include "base/prefs/pref_service.h" +#include "chrome/browser/profiles/incognito_helpers.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/search/hotword_service.h" +#include "chrome/common/pref_names.h" +#include "components/browser_context_keyed_service/browser_context_dependency_manager.h" +#include "components/user_prefs/pref_registry_syncable.h" + +// static +HotwordService* HotwordServiceFactory::GetForProfile(Profile* profile) { + if (!profile->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled)) + return NULL; + + return static_cast<HotwordService*>( + GetInstance()->GetServiceForBrowserContext(profile, true)); +} + +// static +HotwordServiceFactory* HotwordServiceFactory::GetInstance() { + return Singleton<HotwordServiceFactory>::get(); +} + +HotwordServiceFactory::HotwordServiceFactory() + : BrowserContextKeyedServiceFactory( + "HotwordService", + BrowserContextDependencyManager::GetInstance()) { + // No dependencies. +} + +HotwordServiceFactory::~HotwordServiceFactory() { +} + +void HotwordServiceFactory::RegisterProfilePrefs( + user_prefs::PrefRegistrySyncable* prefs) { + prefs->RegisterBooleanPref(prefs::kHotwordSearchEnabled, + false, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); +} + +content::BrowserContext* HotwordServiceFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return chrome::GetBrowserContextOwnInstanceInIncognito(context); +} + +BrowserContextKeyedService* HotwordServiceFactory::BuildServiceInstanceFor( + content::BrowserContext* profile) const { + return new HotwordService(static_cast<Profile*>(profile)); +} diff --git a/chrome/browser/search/hotword_service_factory.h b/chrome/browser/search/hotword_service_factory.h new file mode 100644 index 0000000000..227a23b1e0 --- /dev/null +++ b/chrome/browser/search/hotword_service_factory.h @@ -0,0 +1,40 @@ +// Copyright 2013 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_SEARCH_HOTWORD_SERVICE_FACTORY_H_ +#define CHROME_BROWSER_SEARCH_HOTWORD_SERVICE_FACTORY_H_ + +#include "base/memory/singleton.h" +#include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h" + +class BrowserContextKeyedService; +class HotwordService; +class Profile; + +// Singleton that owns all HotwordServices and associates them with Profiles. +class HotwordServiceFactory : public BrowserContextKeyedServiceFactory { + public: + // Returns the HotwordService for |profile|. + static HotwordService* GetForProfile(Profile* profile); + + static HotwordServiceFactory* GetInstance(); + + private: + friend struct DefaultSingletonTraits<HotwordServiceFactory>; + + HotwordServiceFactory(); + virtual ~HotwordServiceFactory(); + + // Overrides from BrowserContextKeyedServiceFactory: + virtual void RegisterProfilePrefs( + user_prefs::PrefRegistrySyncable* registry) OVERRIDE; + virtual content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const OVERRIDE; + virtual BrowserContextKeyedService* BuildServiceInstanceFor( + content::BrowserContext* profile) const OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(HotwordServiceFactory); +}; + +#endif // CHROME_BROWSER_SEARCH_HOTWORD_SERVICE_FACTORY_H_ diff --git a/chrome/browser/search/search.cc b/chrome/browser/search/search.cc index c12ad8ef0c..c063cbce1e 100644 --- a/chrome/browser/search/search.cc +++ b/chrome/browser/search/search.cc @@ -35,6 +35,12 @@ #include "grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" +#if defined(ENABLE_MANAGED_USERS) +#include "chrome/browser/managed_mode/managed_mode_url_filter.h" +#include "chrome/browser/managed_mode/managed_user_service.h" +#include "chrome/browser/managed_mode/managed_user_service_factory.h" +#endif + namespace chrome { namespace { @@ -257,6 +263,20 @@ string16 GetSearchTermsImpl(const content::WebContents* contents, return GetSearchTermsFromURL(profile, entry->GetVirtualURL()); } +bool IsURLAllowedForSupervisedUser(const GURL& url, Profile* profile) { +#if defined(ENABLE_MANAGED_USERS) + ManagedUserService* managed_user_service = + ManagedUserServiceFactory::GetForProfile(profile); + ManagedModeURLFilter* url_filter = + managed_user_service->GetURLFilterForUIThread(); + if (url_filter->GetFilteringBehaviorForURL(url) == + ManagedModeURLFilter::BLOCK) { + return false; + } +#endif + return true; +} + } // namespace // Negative start-margin values prevent the "es_sm" parameter from being used. @@ -419,13 +439,18 @@ GURL GetInstantURL(Profile* profile, int start_margin) { // Extended mode requires HTTPS. Force it unless the base URL was overridden // on the command line, in which case we allow HTTP (see comments on // IsSuitableURLForInstant()). - if (instant_url.SchemeIsSecure() || - google_util::StartsWithCommandLineGoogleBaseURL(instant_url)) - return instant_url; - GURL::Replacements replacements; - const std::string secure_scheme(content::kHttpsScheme); - replacements.SetSchemeStr(secure_scheme); - return instant_url.ReplaceComponents(replacements); + if (!instant_url.SchemeIsSecure() && + !google_util::StartsWithCommandLineGoogleBaseURL(instant_url)) { + GURL::Replacements replacements; + const std::string secure_scheme(content::kHttpsScheme); + replacements.SetSchemeStr(secure_scheme); + instant_url = instant_url.ReplaceComponents(replacements); + } + + if (!IsURLAllowedForSupervisedUser(instant_url, profile)) + return GURL(); + + return instant_url; } // Returns URLs associated with the default search engine for |profile|. @@ -460,6 +485,9 @@ GURL GetNewTabPageURL(Profile* profile) { if (!url.is_valid() || !url.SchemeIsSecure()) return GURL(chrome::kChromeSearchLocalNtpUrl); + if (!IsURLAllowedForSupervisedUser(url, profile)) + return GURL(chrome::kChromeSearchLocalNtpUrl); + return url; } diff --git a/chrome/browser/search/search_unittest.cc b/chrome/browser/search/search_unittest.cc index 4c49936db5..af006e6a6a 100644 --- a/chrome/browser/search/search_unittest.cc +++ b/chrome/browser/search/search_unittest.cc @@ -8,6 +8,9 @@ #include "base/metrics/histogram_samples.h" #include "base/metrics/statistics_recorder.h" #include "base/prefs/pref_service.h" +#include "chrome/browser/managed_mode/managed_mode_url_filter.h" +#include "chrome/browser/managed_mode/managed_user_service.h" +#include "chrome/browser/managed_mode/managed_user_service_factory.h" #include "chrome/browser/search/instant_service.h" #include "chrome/browser/search/instant_service_factory.h" #include "chrome/browser/search/search.h" @@ -743,6 +746,25 @@ TEST_F(SearchTest, UseLocalNTPIfNTPURLIsNotSet) { chrome::GetNewTabPageURL(profile())); } +TEST_F(SearchTest, UseLocalNTPIfNTPURLIsBlockedForSupervisedUser) { + EnableInstantExtendedAPIForTesting(); + ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial("InstantExtended", + "Group1 use_cacheable_ntp:1")); + + // Block access to foo.com in the URL filter. + ManagedUserService* managed_user_service = + ManagedUserServiceFactory::GetForProfile(profile()); + ManagedModeURLFilter* url_filter = + managed_user_service->GetURLFilterForUIThread(); + std::map<std::string, bool> hosts; + hosts["foo.com"] = false; + url_filter->SetManualHosts(&hosts); + + EXPECT_EQ(GURL(chrome::kChromeSearchLocalNtpUrl), + chrome::GetNewTabPageURL(profile())); + EXPECT_EQ(GURL(), GetInstantURL(profile(), kDisableStartMargin)); +} + TEST_F(SearchTest, GetInstantURLExtendedEnabled) { // Instant is disabled, so no Instant URL. EXPECT_EQ(GURL(), GetInstantURL(profile(), kDisableStartMargin)); diff --git a/chrome/browser/search_engines/OWNERS b/chrome/browser/search_engines/OWNERS index dd79a14220..09c675917d 100644 --- a/chrome/browser/search_engines/OWNERS +++ b/chrome/browser/search_engines/OWNERS @@ -2,4 +2,7 @@ estade@chromium.org pkasting@chromium.org stevet@chromium.org -per-file *_android.*=yfriedman@chromium.org
\ No newline at end of file +per-file *_android.*=yfriedman@chromium.org + +per-file default_search_policy_handler*=joaodasilva@chromium.org +per-file default_search_policy_handler*=dconnelly@chromium.org diff --git a/chrome/browser/search_engines/default_search_policy_handler.cc b/chrome/browser/search_engines/default_search_policy_handler.cc new file mode 100644 index 0000000000..5a5e1900ad --- /dev/null +++ b/chrome/browser/search_engines/default_search_policy_handler.cc @@ -0,0 +1,323 @@ +// Copyright 2013 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/search_engines/default_search_policy_handler.h" + +#include "base/prefs/pref_value_map.h" +#include "base/stl_util.h" +#include "base/strings/string_util.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/policy/policy_error_map.h" +#include "chrome/browser/policy/policy_map.h" +#include "chrome/browser/search_engines/search_terms_data.h" +#include "chrome/browser/search_engines/template_url.h" +#include "chrome/common/pref_names.h" +#include "content/public/browser/notification_service.h" +#include "grit/generated_resources.h" +#include "policy/policy_constants.h" + +namespace policy { + +// List of policy types to preference names, for policies affecting the default +// search provider. +const PolicyToPreferenceMapEntry kDefaultSearchPolicyMap[] = { + { key::kDefaultSearchProviderEnabled, + prefs::kDefaultSearchProviderEnabled, + Value::TYPE_BOOLEAN }, + { key::kDefaultSearchProviderName, + prefs::kDefaultSearchProviderName, + Value::TYPE_STRING }, + { key::kDefaultSearchProviderKeyword, + prefs::kDefaultSearchProviderKeyword, + Value::TYPE_STRING }, + { key::kDefaultSearchProviderSearchURL, + prefs::kDefaultSearchProviderSearchURL, + Value::TYPE_STRING }, + { key::kDefaultSearchProviderSuggestURL, + prefs::kDefaultSearchProviderSuggestURL, + Value::TYPE_STRING }, + { key::kDefaultSearchProviderInstantURL, + prefs::kDefaultSearchProviderInstantURL, + Value::TYPE_STRING }, + { key::kDefaultSearchProviderIconURL, + prefs::kDefaultSearchProviderIconURL, + Value::TYPE_STRING }, + { key::kDefaultSearchProviderEncodings, + prefs::kDefaultSearchProviderEncodings, + Value::TYPE_LIST }, + { key::kDefaultSearchProviderAlternateURLs, + prefs::kDefaultSearchProviderAlternateURLs, + Value::TYPE_LIST }, + { key::kDefaultSearchProviderSearchTermsReplacementKey, + prefs::kDefaultSearchProviderSearchTermsReplacementKey, + Value::TYPE_STRING }, + { key::kDefaultSearchProviderImageURL, + prefs::kDefaultSearchProviderImageURL, + Value::TYPE_STRING }, + { key::kDefaultSearchProviderNewTabURL, + prefs::kDefaultSearchProviderNewTabURL, + Value::TYPE_STRING }, + { key::kDefaultSearchProviderSearchURLPostParams, + prefs::kDefaultSearchProviderSearchURLPostParams, + Value::TYPE_STRING }, + { key::kDefaultSearchProviderSuggestURLPostParams, + prefs::kDefaultSearchProviderSuggestURLPostParams, + Value::TYPE_STRING }, + { key::kDefaultSearchProviderInstantURLPostParams, + prefs::kDefaultSearchProviderInstantURLPostParams, + Value::TYPE_STRING }, + { key::kDefaultSearchProviderImageURLPostParams, + prefs::kDefaultSearchProviderImageURLPostParams, + Value::TYPE_STRING }, +}; + +// DefaultSearchEncodingsPolicyHandler implementation -------------------------- + +DefaultSearchEncodingsPolicyHandler::DefaultSearchEncodingsPolicyHandler() + : TypeCheckingPolicyHandler(key::kDefaultSearchProviderEncodings, + Value::TYPE_LIST) {} + +DefaultSearchEncodingsPolicyHandler::~DefaultSearchEncodingsPolicyHandler() { +} + +void DefaultSearchEncodingsPolicyHandler::ApplyPolicySettings( + const PolicyMap& policies, PrefValueMap* prefs) { + // The DefaultSearchProviderEncodings policy has type list, but the related + // preference has type string. Convert one into the other here, using + // ';' as a separator. + const Value* value = policies.GetValue(policy_name()); + const ListValue* list; + if (!value || !value->GetAsList(&list)) + return; + + ListValue::const_iterator iter(list->begin()); + ListValue::const_iterator end(list->end()); + std::vector<std::string> string_parts; + for (; iter != end; ++iter) { + std::string s; + if ((*iter)->GetAsString(&s)) { + string_parts.push_back(s); + } + } + std::string encodings = JoinString(string_parts, ';'); + prefs->SetValue(prefs::kDefaultSearchProviderEncodings, + Value::CreateStringValue(encodings)); +} + + +// DefaultSearchPolicyHandler implementation ----------------------------------- + +DefaultSearchPolicyHandler::DefaultSearchPolicyHandler() { + for (size_t i = 0; i < arraysize(kDefaultSearchPolicyMap); ++i) { + const char* policy_name = kDefaultSearchPolicyMap[i].policy_name; + if (policy_name == key::kDefaultSearchProviderEncodings) { + handlers_.push_back(new DefaultSearchEncodingsPolicyHandler()); + } else { + handlers_.push_back(new SimplePolicyHandler( + policy_name, + kDefaultSearchPolicyMap[i].preference_path, + kDefaultSearchPolicyMap[i].value_type)); + } + } +} + +DefaultSearchPolicyHandler::~DefaultSearchPolicyHandler() { + STLDeleteElements(&handlers_); +} + +bool DefaultSearchPolicyHandler::CheckPolicySettings(const PolicyMap& policies, + PolicyErrorMap* errors) { + if (!CheckIndividualPolicies(policies, errors)) + return false; + + if (DefaultSearchProviderIsDisabled(policies)) { + // Add an error for all specified default search policies except + // DefaultSearchProviderEnabled. + + for (std::vector<TypeCheckingPolicyHandler*>::const_iterator handler = + handlers_.begin(); + handler != handlers_.end(); ++handler) { + const char* policy_name = (*handler)->policy_name(); + if (policy_name != key::kDefaultSearchProviderEnabled && + HasDefaultSearchPolicy(policies, policy_name)) { + errors->AddError(policy_name, IDS_POLICY_DEFAULT_SEARCH_DISABLED); + } + } + return true; + } + + const Value* url; + std::string dummy; + if (DefaultSearchURLIsValid(policies, &url, &dummy) || + !AnyDefaultSearchPoliciesSpecified(policies)) + return true; + errors->AddError(key::kDefaultSearchProviderSearchURL, url ? + IDS_POLICY_INVALID_SEARCH_URL_ERROR : IDS_POLICY_NOT_SPECIFIED_ERROR); + return false; +} + +void DefaultSearchPolicyHandler::ApplyPolicySettings(const PolicyMap& policies, + PrefValueMap* prefs) { + if (DefaultSearchProviderIsDisabled(policies)) { + prefs->SetBoolean(prefs::kDefaultSearchProviderEnabled, false); + + // If default search is disabled, the other fields are ignored. + prefs->SetString(prefs::kDefaultSearchProviderName, std::string()); + prefs->SetString(prefs::kDefaultSearchProviderSearchURL, std::string()); + prefs->SetString(prefs::kDefaultSearchProviderSuggestURL, std::string()); + prefs->SetString(prefs::kDefaultSearchProviderIconURL, std::string()); + prefs->SetString(prefs::kDefaultSearchProviderEncodings, std::string()); + prefs->SetString(prefs::kDefaultSearchProviderKeyword, std::string()); + prefs->SetString(prefs::kDefaultSearchProviderInstantURL, std::string()); + prefs->SetString(prefs::kDefaultSearchProviderNewTabURL, std::string()); + prefs->SetValue(prefs::kDefaultSearchProviderAlternateURLs, + new ListValue()); + prefs->SetString( + prefs::kDefaultSearchProviderSearchTermsReplacementKey, std::string()); + prefs->SetString(prefs::kDefaultSearchProviderImageURL, std::string()); + prefs->SetString( + prefs::kDefaultSearchProviderSearchURLPostParams, std::string()); + prefs->SetString( + prefs::kDefaultSearchProviderSuggestURLPostParams, std::string()); + prefs->SetString( + prefs::kDefaultSearchProviderInstantURLPostParams, std::string()); + prefs->SetString( + prefs::kDefaultSearchProviderImageURLPostParams, std::string()); + } else { + // The search URL is required. The other entries are optional. Just make + // sure that they are all specified via policy, so that the regular prefs + // aren't used. + const Value* dummy; + std::string url; + if (DefaultSearchURLIsValid(policies, &dummy, &url)) { + + for (std::vector<TypeCheckingPolicyHandler*>::const_iterator handler = + handlers_.begin(); + handler != handlers_.end(); ++handler) { + (*handler)->ApplyPolicySettings(policies, prefs); + } + + EnsureStringPrefExists(prefs, prefs::kDefaultSearchProviderSuggestURL); + EnsureStringPrefExists(prefs, prefs::kDefaultSearchProviderIconURL); + EnsureStringPrefExists(prefs, prefs::kDefaultSearchProviderEncodings); + EnsureStringPrefExists(prefs, prefs::kDefaultSearchProviderKeyword); + EnsureStringPrefExists(prefs, prefs::kDefaultSearchProviderInstantURL); + EnsureStringPrefExists(prefs, prefs::kDefaultSearchProviderNewTabURL); + EnsureListPrefExists(prefs, prefs::kDefaultSearchProviderAlternateURLs); + EnsureStringPrefExists( + prefs, + prefs::kDefaultSearchProviderSearchTermsReplacementKey); + EnsureStringPrefExists(prefs, prefs::kDefaultSearchProviderImageURL); + EnsureStringPrefExists( + prefs, + prefs::kDefaultSearchProviderSearchURLPostParams); + EnsureStringPrefExists( + prefs, + prefs::kDefaultSearchProviderSuggestURLPostParams); + EnsureStringPrefExists( + prefs, + prefs::kDefaultSearchProviderInstantURLPostParams); + EnsureStringPrefExists( + prefs, + prefs::kDefaultSearchProviderImageURLPostParams); + + // For the name and keyword, default to the host if not specified. If + // there is no host (file: URLs? Not sure), use "_" to guarantee that the + // keyword is non-empty. + std::string name, keyword; + std::string host(GURL(url).host()); + if (host.empty()) + host = "_"; + if (!prefs->GetString(prefs::kDefaultSearchProviderName, &name) || + name.empty()) { + prefs->SetString(prefs::kDefaultSearchProviderName, host); + } + if (!prefs->GetString(prefs::kDefaultSearchProviderKeyword, &keyword) || + keyword.empty()) { + prefs->SetString(prefs::kDefaultSearchProviderKeyword, host); + } + + // And clear the IDs since these are not specified via policy. + prefs->SetString(prefs::kDefaultSearchProviderID, std::string()); + prefs->SetString(prefs::kDefaultSearchProviderPrepopulateID, + std::string()); + } + } + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_DEFAULT_SEARCH_POLICY_CHANGED, + content::NotificationService::AllSources(), + content::NotificationService::NoDetails()); +} + +bool DefaultSearchPolicyHandler::CheckIndividualPolicies( + const PolicyMap& policies, + PolicyErrorMap* errors) { + for (std::vector<TypeCheckingPolicyHandler*>::const_iterator handler = + handlers_.begin(); + handler != handlers_.end(); ++handler) { + if (!(*handler)->CheckPolicySettings(policies, errors)) + return false; + } + return true; +} + +bool DefaultSearchPolicyHandler::HasDefaultSearchPolicy( + const PolicyMap& policies, + const char* policy_name) { + return policies.Get(policy_name) != NULL; +} + +bool DefaultSearchPolicyHandler::AnyDefaultSearchPoliciesSpecified( + const PolicyMap& policies) { + for (std::vector<TypeCheckingPolicyHandler*>::const_iterator handler = + handlers_.begin(); + handler != handlers_.end(); ++handler) { + if (policies.Get((*handler)->policy_name())) + return true; + } + return false; +} + +bool DefaultSearchPolicyHandler::DefaultSearchProviderIsDisabled( + const PolicyMap& policies) { + const Value* provider_enabled = + policies.GetValue(key::kDefaultSearchProviderEnabled); + bool enabled = true; + return provider_enabled && provider_enabled->GetAsBoolean(&enabled) && + !enabled; +} + +bool DefaultSearchPolicyHandler::DefaultSearchURLIsValid( + const PolicyMap& policies, + const Value** url_value, + std::string* url_string) { + *url_value = policies.GetValue(key::kDefaultSearchProviderSearchURL); + if (!*url_value || !(*url_value)->GetAsString(url_string) || + url_string->empty()) + return false; + TemplateURLData data; + data.SetURL(*url_string); + SearchTermsData search_terms_data; + return TemplateURL(NULL, data).SupportsReplacementUsingTermsData( + search_terms_data); +} + +void DefaultSearchPolicyHandler::EnsureStringPrefExists( + PrefValueMap* prefs, + const std::string& path) { + std::string value; + if (!prefs->GetString(path, &value)) + prefs->SetString(path, value); +} + +void DefaultSearchPolicyHandler::EnsureListPrefExists( + PrefValueMap* prefs, + const std::string& path) { + base::Value* value; + base::ListValue* list_value; + if (!prefs->GetValue(path, &value) || !value->GetAsList(&list_value)) + prefs->SetValue(path, new ListValue()); +} + +} // namespace policy diff --git a/chrome/browser/search_engines/default_search_policy_handler.h b/chrome/browser/search_engines/default_search_policy_handler.h new file mode 100644 index 0000000000..c0fe7151de --- /dev/null +++ b/chrome/browser/search_engines/default_search_policy_handler.h @@ -0,0 +1,79 @@ +// Copyright 2013 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_SEARCH_ENGINES_DEFAULT_SEARCH_POLICY_HANDLER_H_ +#define CHROME_BROWSER_SEARCH_ENGINES_DEFAULT_SEARCH_POLICY_HANDLER_H_ + +#include <vector> + +#include "chrome/browser/policy/configuration_policy_handler.h" + +namespace policy { + +// ConfigurationPolicyHandler for the DefaultSearchEncodings policy. +class DefaultSearchEncodingsPolicyHandler + : public TypeCheckingPolicyHandler { + public: + DefaultSearchEncodingsPolicyHandler(); + virtual ~DefaultSearchEncodingsPolicyHandler(); + + // ConfigurationPolicyHandler methods: + virtual void ApplyPolicySettings(const PolicyMap& policies, + PrefValueMap* prefs) OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(DefaultSearchEncodingsPolicyHandler); +}; + +// ConfigurationPolicyHandler for the default search policies. +class DefaultSearchPolicyHandler : public ConfigurationPolicyHandler { + public: + DefaultSearchPolicyHandler(); + virtual ~DefaultSearchPolicyHandler(); + + // ConfigurationPolicyHandler methods: + virtual bool CheckPolicySettings(const PolicyMap& policies, + PolicyErrorMap* errors) OVERRIDE; + virtual void ApplyPolicySettings(const PolicyMap& policies, + PrefValueMap* prefs) OVERRIDE; + + private: + // Calls |CheckPolicySettings()| on each of the handlers in |handlers_| + // and returns whether all of the calls succeeded. + bool CheckIndividualPolicies(const PolicyMap& policies, + PolicyErrorMap* errors); + + // Returns whether there is a value for |policy_name| in |policies|. + bool HasDefaultSearchPolicy(const PolicyMap& policies, + const char* policy_name); + + // Returns whether any default search policies are specified in |policies|. + bool AnyDefaultSearchPoliciesSpecified(const PolicyMap& policies); + + // Returns whether the default search provider is disabled. + bool DefaultSearchProviderIsDisabled(const PolicyMap& policies); + + // Returns whether the default search URL is set and valid. On success, both + // outparams (which must be non-NULL) are filled with the search URL. + bool DefaultSearchURLIsValid(const PolicyMap& policies, + const Value** url_value, + std::string* url_string); + + // Make sure that the |path| is present in |prefs_|. If not, set it to + // a blank string. + void EnsureStringPrefExists(PrefValueMap* prefs, const std::string& path); + + // Make sure that the |path| is present in |prefs_| and is a ListValue. If + // not, set it to an empty list. + void EnsureListPrefExists(PrefValueMap* prefs, const std::string& path); + + // The ConfigurationPolicyHandler handlers for each default search policy. + std::vector<TypeCheckingPolicyHandler*> handlers_; + + DISALLOW_COPY_AND_ASSIGN(DefaultSearchPolicyHandler); +}; + +} // namespace policy + +#endif // CHROME_BROWSER_SEARCH_ENGINES_DEFAULT_SEARCH_POLICY_HANDLER_H_ diff --git a/chrome/browser/search_engines/default_search_policy_handler_unittest.cc b/chrome/browser/search_engines/default_search_policy_handler_unittest.cc new file mode 100644 index 0000000000..ca880432fb --- /dev/null +++ b/chrome/browser/search_engines/default_search_policy_handler_unittest.cc @@ -0,0 +1,294 @@ +// Copyright 2013 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/policy/configuration_policy_pref_store.h" +#include "chrome/browser/policy/configuration_policy_pref_store_unittest.h" +#include "chrome/browser/search_engines/default_search_policy_handler.h" +#include "chrome/common/pref_names.h" +#include "policy/policy_constants.h" + +namespace policy { + +class DefaultSearchPolicyHandlerTest + : public ConfigurationPolicyPrefStoreTest { + public: + DefaultSearchPolicyHandlerTest() { + default_alternate_urls_.AppendString( + "http://www.google.com/#q={searchTerms}"); + default_alternate_urls_.AppendString( + "http://www.google.com/search#q={searchTerms}"); + } + + protected: + static const char kSearchURL[]; + static const char kSuggestURL[]; + static const char kIconURL[]; + static const char kName[]; + static const char kKeyword[]; + static const char kReplacementKey[]; + static const char kImageURL[]; + static const char kImageParams[]; + static const char kNewTabURL[]; + + // Build a default search policy by setting search-related keys in |policy| to + // reasonable values. You can update any of the keys after calling this + // method. + void BuildDefaultSearchPolicy(PolicyMap* policy); + + base::ListValue default_alternate_urls_; +}; + +const char DefaultSearchPolicyHandlerTest::kSearchURL[] = + "http://test.com/search?t={searchTerms}"; +const char DefaultSearchPolicyHandlerTest::kSuggestURL[] = + "http://test.com/sugg?={searchTerms}"; +const char DefaultSearchPolicyHandlerTest::kIconURL[] = + "http://test.com/icon.jpg"; +const char DefaultSearchPolicyHandlerTest::kName[] = + "MyName"; +const char DefaultSearchPolicyHandlerTest::kKeyword[] = + "MyKeyword"; +const char DefaultSearchPolicyHandlerTest::kReplacementKey[] = + "espv"; +const char DefaultSearchPolicyHandlerTest::kImageURL[] = + "http://test.com/searchbyimage/upload"; +const char DefaultSearchPolicyHandlerTest::kImageParams[] = + "image_content=content,image_url=http://test.com/test.png"; +const char DefaultSearchPolicyHandlerTest::kNewTabURL[] = + "http://test.com/newtab"; + +void DefaultSearchPolicyHandlerTest:: + BuildDefaultSearchPolicy(PolicyMap* policy) { + base::ListValue* encodings = new base::ListValue(); + encodings->AppendString("UTF-16"); + encodings->AppendString("UTF-8"); + policy->Set(key::kDefaultSearchProviderEnabled, POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_USER, base::Value::CreateBooleanValue(true), NULL); + policy->Set(key::kDefaultSearchProviderSearchURL, POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_USER, base::Value::CreateStringValue(kSearchURL), + NULL); + policy->Set(key::kDefaultSearchProviderName, POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_USER, base::Value::CreateStringValue(kName), NULL); + policy->Set(key::kDefaultSearchProviderKeyword, POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_USER, base::Value::CreateStringValue(kKeyword), + NULL); + policy->Set(key::kDefaultSearchProviderSuggestURL, POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_USER, base::Value::CreateStringValue(kSuggestURL), + NULL); + policy->Set(key::kDefaultSearchProviderIconURL, POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_USER, base::Value::CreateStringValue(kIconURL), + NULL); + policy->Set(key::kDefaultSearchProviderEncodings, POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_USER, encodings, NULL); + policy->Set(key::kDefaultSearchProviderAlternateURLs, POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_USER, default_alternate_urls_.DeepCopy(), NULL); + policy->Set(key::kDefaultSearchProviderSearchTermsReplacementKey, + POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateStringValue(kReplacementKey), NULL); + policy->Set(key::kDefaultSearchProviderImageURL, + POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateStringValue(kImageURL), NULL); + policy->Set(key::kDefaultSearchProviderImageURLPostParams, + POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateStringValue(kImageParams), NULL); + policy->Set(key::kDefaultSearchProviderNewTabURL, + POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateStringValue(kNewTabURL), NULL); +} + +// Checks that if the policy for default search is valid, i.e. there's a +// search URL, that all the elements have been given proper defaults. +TEST_F(DefaultSearchPolicyHandlerTest, MinimallyDefined) { + PolicyMap policy; + policy.Set(key::kDefaultSearchProviderEnabled, POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_USER, base::Value::CreateBooleanValue(true), NULL); + policy.Set(key::kDefaultSearchProviderSearchURL, POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_USER, base::Value::CreateStringValue(kSearchURL), + NULL); + UpdateProviderPolicy(policy); + + const base::Value* value = NULL; + EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderSearchURL, &value)); + EXPECT_TRUE(base::StringValue(kSearchURL).Equals(value)); + + EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderName, &value)); + EXPECT_TRUE(base::StringValue("test.com").Equals(value)); + + EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderKeyword, &value)); + EXPECT_TRUE(base::StringValue("test.com").Equals(value)); + + EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderSuggestURL, + &value)); + EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); + + EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderIconURL, &value)); + EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); + + EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderEncodings, &value)); + EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); + + EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderInstantURL, + &value)); + EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); + + EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderAlternateURLs, + &value)); + EXPECT_TRUE(base::ListValue().Equals(value)); + + EXPECT_TRUE( + store_->GetValue(prefs::kDefaultSearchProviderSearchTermsReplacementKey, + &value)); + EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); + + EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderImageURL, &value)); + EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); + + EXPECT_TRUE(store_->GetValue( + prefs::kDefaultSearchProviderSearchURLPostParams, &value)); + EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); + + EXPECT_TRUE(store_->GetValue( + prefs::kDefaultSearchProviderSuggestURLPostParams, &value)); + EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); + + EXPECT_TRUE(store_->GetValue( + prefs::kDefaultSearchProviderInstantURLPostParams, &value)); + EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); + + EXPECT_TRUE(store_->GetValue( + prefs::kDefaultSearchProviderImageURLPostParams, &value)); + EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); + + EXPECT_TRUE(store_->GetValue( + prefs::kDefaultSearchProviderNewTabURL, &value)); + EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); +} + +// Checks that for a fully defined search policy, all elements have been +// read properly. +TEST_F(DefaultSearchPolicyHandlerTest, FullyDefined) { + PolicyMap policy; + BuildDefaultSearchPolicy(&policy); + UpdateProviderPolicy(policy); + + const base::Value* value = NULL; + EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderSearchURL, &value)); + EXPECT_TRUE(base::StringValue(kSearchURL).Equals(value)); + + EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderName, &value)); + EXPECT_TRUE(base::StringValue(kName).Equals(value)); + + EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderKeyword, &value)); + EXPECT_TRUE(base::StringValue(kKeyword).Equals(value)); + + EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderSuggestURL, + &value)); + EXPECT_TRUE(base::StringValue(kSuggestURL).Equals(value)); + + EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderIconURL, &value)); + EXPECT_TRUE(base::StringValue(kIconURL).Equals(value)); + + EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderEncodings, &value)); + EXPECT_TRUE(base::StringValue("UTF-16;UTF-8").Equals(value)); + + EXPECT_TRUE(store_->GetValue( + prefs::kDefaultSearchProviderAlternateURLs, &value)); + EXPECT_TRUE(default_alternate_urls_.Equals(value)); + + EXPECT_TRUE( + store_->GetValue(prefs::kDefaultSearchProviderSearchTermsReplacementKey, + &value)); + EXPECT_TRUE(base::StringValue(kReplacementKey).Equals(value)); + + EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderImageURL, &value)); + EXPECT_TRUE(base::StringValue(std::string(kImageURL)).Equals(value)); + + EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderImageURLPostParams, + &value)); + EXPECT_TRUE(base::StringValue(std::string(kImageParams)).Equals(value)); + + EXPECT_TRUE(store_->GetValue( + prefs::kDefaultSearchProviderSearchURLPostParams, &value)); + EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); + + EXPECT_TRUE(store_->GetValue( + prefs::kDefaultSearchProviderSuggestURLPostParams, &value)); + EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); + + EXPECT_TRUE(store_->GetValue( + prefs::kDefaultSearchProviderInstantURLPostParams, &value)); + EXPECT_TRUE(base::StringValue(std::string()).Equals(value)); +} + +// Checks that if the default search policy is missing, that no elements of the +// default search policy will be present. +TEST_F(DefaultSearchPolicyHandlerTest, MissingUrl) { + PolicyMap policy; + BuildDefaultSearchPolicy(&policy); + policy.Erase(key::kDefaultSearchProviderSearchURL); + UpdateProviderPolicy(policy); + + EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderSearchURL, NULL)); + EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderName, NULL)); + EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderKeyword, NULL)); + EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderSuggestURL, NULL)); + EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderIconURL, NULL)); + EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderEncodings, NULL)); + EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderAlternateURLs, + NULL)); + EXPECT_FALSE(store_->GetValue( + prefs::kDefaultSearchProviderSearchTermsReplacementKey, NULL)); + EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderImageURL, NULL)); + EXPECT_FALSE(store_->GetValue( + prefs::kDefaultSearchProviderImageURLPostParams, NULL)); + EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderInstantURL, NULL)); + EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderNewTabURL, NULL)); +} + +// Checks that if the default search policy is invalid, that no elements of the +// default search policy will be present. +TEST_F(DefaultSearchPolicyHandlerTest, Invalid) { + PolicyMap policy; + BuildDefaultSearchPolicy(&policy); + const char bad_search_url[] = "http://test.com/noSearchTerms"; + policy.Set(key::kDefaultSearchProviderSearchURL, POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_USER, + base::Value::CreateStringValue(bad_search_url), NULL); + UpdateProviderPolicy(policy); + + EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderSearchURL, NULL)); + EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderName, NULL)); + EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderKeyword, NULL)); + EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderSuggestURL, NULL)); + EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderIconURL, NULL)); + EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderEncodings, NULL)); + EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderAlternateURLs, + NULL)); + EXPECT_FALSE(store_->GetValue( + prefs::kDefaultSearchProviderSearchTermsReplacementKey, NULL)); + EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderImageURL, NULL)); + EXPECT_FALSE(store_->GetValue( + prefs::kDefaultSearchProviderImageURLPostParams, NULL)); + EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderInstantURL, NULL)); + EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderNewTabURL, NULL)); +} + +// Checks that if the default search policy is invalid, that no elements of the +// default search policy will be present. +TEST_F(DefaultSearchPolicyHandlerTest, Disabled) { + PolicyMap policy; + policy.Set(key::kDefaultSearchProviderEnabled, POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_USER, base::Value::CreateBooleanValue(false), NULL); + UpdateProviderPolicy(policy); + + const base::Value* value = NULL; + EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderEnabled, &value)); + base::FundamentalValue expected_enabled(false); + EXPECT_TRUE(base::Value::Equals(&expected_enabled, value)); + EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderSearchURL, &value)); + base::StringValue expected_search_url((std::string())); + EXPECT_TRUE(base::Value::Equals(&expected_search_url, value)); +} + +} // namespace policy diff --git a/chrome/browser/sessions/better_session_restore_browsertest.cc b/chrome/browser/sessions/better_session_restore_browsertest.cc index 11ea512655..ad68c95002 100644 --- a/chrome/browser/sessions/better_session_restore_browsertest.cc +++ b/chrome/browser/sessions/better_session_restore_browsertest.cc @@ -9,16 +9,21 @@ #include "base/path_service.h" #include "base/prefs/pref_service.h" #include "base/strings/utf_string_conversions.h" +#include "chrome/browser/background/background_mode_manager.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/content_settings/cookie_settings.h" +#include "chrome/browser/defaults.h" #include "chrome/browser/infobars/confirm_infobar_delegate.h" #include "chrome/browser/infobars/infobar_service.h" +#include "chrome/browser/lifetime/application_lifetime.h" #include "chrome/browser/prefs/session_startup_pref.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_impl.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/sessions/session_backend.h" #include "chrome/browser/sessions/session_service_factory.h" +#include "chrome/browser/sessions/session_service_test_helper.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/browser_iterator.h" @@ -41,6 +46,10 @@ #include "net/url_request/url_request_filter.h" #include "net/url_request/url_request_test_job.h" +#if defined(OS_MACOSX) +#include "base/mac/scoped_nsautorelease_pool.h" +#endif + namespace { Browser* FindOneOtherBrowserForProfile(Profile* profile, @@ -97,6 +106,27 @@ net::URLRequestJob* URLRequestFakerForPostRequests( true); } +class FakeBackgroundModeManager : public BackgroundModeManager { + public: + FakeBackgroundModeManager() + : BackgroundModeManager( + CommandLine::ForCurrentProcess(), + &g_browser_process->profile_manager()->GetProfileInfoCache()), + background_mode_active_(false) {} + + void SetBackgroundModeActive(bool active) { + background_mode_active_ = active; + } + + virtual bool IsBackgroundModeActive() OVERRIDE { + return background_mode_active_; + } + + private: + bool background_mode_active_; + +}; + } // namespace class BetterSessionRestoreTest : public InProcessBrowserTest { @@ -138,6 +168,15 @@ class BetterSessionRestoreTest : public InProcessBrowserTest { } protected: + virtual void SetUpOnMainThread() OVERRIDE { + SessionServiceTestHelper helper( + SessionServiceFactory::GetForProfile(browser()->profile())); + helper.SetForceBrowserNotAliveWithNoWindows(true); + helper.ReleaseService(); + g_browser_process->set_background_mode_manager_for_test( + scoped_ptr<BackgroundModeManager>(new FakeBackgroundModeManager)); + } + void StoreDataWithPage(const std::string& filename) { StoreDataWithPage(browser(), filename); } @@ -184,7 +223,11 @@ class BetterSessionRestoreTest : public InProcessBrowserTest { } void CheckReloadedPageNotRestored() { - CheckTitle(browser(), title_storing_); + CheckReloadedPageNotRestored(browser()); + } + + void CheckReloadedPageNotRestored(Browser* browser) { + CheckTitle(browser, title_storing_); } void CheckTitle(Browser* browser, const string16& expected_title) { @@ -230,7 +273,12 @@ class BetterSessionRestoreTest : public InProcessBrowserTest { } void CheckFormRestored(bool text_present, bool password_present) { - CheckReloadedPageRestored(); + CheckFormRestored(browser(), text_present, password_present); + } + + void CheckFormRestored( + Browser* browser, bool text_present, bool password_present) { + CheckReloadedPageRestored(browser); if (text_present) { EXPECT_TRUE(g_last_upload_bytes.Get().find("posted-text") != std::string::npos); @@ -255,6 +303,36 @@ class BetterSessionRestoreTest : public InProcessBrowserTest { } } + void CloseBrowserSynchronously(Browser* browser) { + content::WindowedNotificationObserver observer( + chrome::NOTIFICATION_BROWSER_CLOSED, + content::NotificationService::AllSources()); + browser->window()->Close(); +#if defined(OS_MACOSX) + // BrowserWindowController depends on the auto release pool being recycled + // in the message loop to delete itself, which frees the Browser object + // which fires this event. + AutoreleasePool()->Recycle(); +#endif + observer.Wait(); + } + + virtual Browser* QuitBrowserAndRestore(Browser* browser) { + Profile* profile = browser->profile(); + + // Close the browser. + chrome::StartKeepAlive(); + CloseBrowserSynchronously(browser); + + // Create a new window, which should trigger session restore. + ui_test_utils::BrowserAddedObserver window_observer; + chrome::NewEmptyWindow(profile, chrome::HOST_DESKTOP_TYPE_NATIVE); + Browser* new_browser = window_observer.WaitForSingleNewBrowser(); + chrome::EndKeepAlive(); + + return new_browser; + } + std::string fake_server_address() { return fake_server_address_; } @@ -263,6 +341,18 @@ class BetterSessionRestoreTest : public InProcessBrowserTest { return test_path_; } + void EnableBackgroundMode() { + static_cast<FakeBackgroundModeManager*>( + g_browser_process->background_mode_manager())-> + SetBackgroundModeActive(true); + } + + void DisableBackgroundMode() { + static_cast<FakeBackgroundModeManager*>( + g_browser_process->background_mode_manager())-> + SetBackgroundModeActive(false); + } + private: const std::string fake_server_address_; const std::string test_path_; @@ -284,6 +374,17 @@ class ContinueWhereILeftOffTest : public BetterSessionRestoreTest { browser()->profile(), SessionStartupPref(SessionStartupPref::LAST)); } + protected: + virtual Browser* QuitBrowserAndRestore(Browser* browser) OVERRIDE { + content::WindowedNotificationObserver session_restore_observer( + chrome::NOTIFICATION_SESSION_RESTORE_DONE, + content::NotificationService::AllSources()); + Browser* new_browser = + BetterSessionRestoreTest::QuitBrowserAndRestore(browser); + session_restore_observer.Wait(); + return new_browser; + } + DISALLOW_COPY_AND_ASSIGN(ContinueWhereILeftOffTest); }; @@ -360,6 +461,53 @@ IN_PROC_BROWSER_TEST_F(ContinueWhereILeftOffTest, PostWithPassword) { CheckFormRestored(false, false); } +IN_PROC_BROWSER_TEST_F(ContinueWhereILeftOffTest, SessionCookiesBrowserClose) { + // Set the startup preference to "continue where I left off" and visit a page + // which stores a session cookie. + StoreDataWithPage("session_cookies.html"); + Browser* new_browser = QuitBrowserAndRestore(browser()); + // The browsing session will be continued; just wait for the page to reload + // and check the stored data. + CheckReloadedPageRestored(new_browser); +} + +IN_PROC_BROWSER_TEST_F(ContinueWhereILeftOffTest, + CookiesClearedOnBrowserClose) { + StoreDataWithPage("cookies.html"); + // Normally cookies are restored. + Browser* new_browser = QuitBrowserAndRestore(browser()); + CheckReloadedPageRestored(new_browser); + // ... but not if the content setting is set to clear on exit. + CookieSettings::Factory::GetForProfile(new_browser->profile())-> + SetDefaultCookieSetting(CONTENT_SETTING_SESSION_ONLY); + // ... unless background mode is active. + EnableBackgroundMode(); + new_browser = QuitBrowserAndRestore(new_browser); + CheckReloadedPageRestored(new_browser); + + DisableBackgroundMode(); + new_browser = QuitBrowserAndRestore(new_browser); + if (browser_defaults::kBrowserAliveWithNoWindows) + CheckReloadedPageRestored(new_browser); + else + CheckReloadedPageNotRestored(new_browser); +} + +IN_PROC_BROWSER_TEST_F(ContinueWhereILeftOffTest, PostBrowserClose) { + PostFormWithPage("post.html", false); + Browser* new_browser = QuitBrowserAndRestore(browser()); + CheckFormRestored(new_browser, true, false); +} + +IN_PROC_BROWSER_TEST_F(ContinueWhereILeftOffTest, + PostWithPasswordBrowserClose) { + PostFormWithPage("post_with_password.html", true); + Browser* new_browser = QuitBrowserAndRestore(browser()); + CheckReloadedPageRestored(new_browser); + // The form data contained passwords, so it's removed completely. + CheckFormRestored(new_browser, false, false); +} + class RestartTest : public BetterSessionRestoreTest { public: RestartTest() { } @@ -530,3 +678,38 @@ IN_PROC_BROWSER_TEST_F(NoSessionRestoreTest, CookiesClearedOnExit) { web_contents->GetURL().spec()); StoreDataWithPage("local_storage.html"); } + +IN_PROC_BROWSER_TEST_F(NoSessionRestoreTest, SessionCookiesBrowserClose) { + StoreDataWithPage("session_cookies.html"); + EnableBackgroundMode(); + Browser* new_browser = QuitBrowserAndRestore(browser()); + NavigateAndCheckStoredData(new_browser, "session_cookies.html"); + DisableBackgroundMode(); + new_browser = QuitBrowserAndRestore(new_browser); + if (browser_defaults::kBrowserAliveWithNoWindows) + NavigateAndCheckStoredData(new_browser, "session_cookies.html"); + else + StoreDataWithPage(new_browser, "session_cookies.html"); +} + +IN_PROC_BROWSER_TEST_F(NoSessionRestoreTest, CookiesClearedOnBrowserClose) { + StoreDataWithPage("cookies.html"); + + // Normally cookies are restored. + Browser* new_browser = QuitBrowserAndRestore(browser()); + NavigateAndCheckStoredData(new_browser, "cookies.html"); + + // ... but not if the content setting is set to clear on exit. + CookieSettings::Factory::GetForProfile(new_browser->profile())-> + SetDefaultCookieSetting(CONTENT_SETTING_SESSION_ONLY); + // ... unless background mode is active. + EnableBackgroundMode(); + new_browser = QuitBrowserAndRestore(new_browser); + NavigateAndCheckStoredData(new_browser, "cookies.html"); + DisableBackgroundMode(); + new_browser = QuitBrowserAndRestore(new_browser); + if (browser_defaults::kBrowserAliveWithNoWindows) + NavigateAndCheckStoredData(new_browser, "cookies.html"); + else + StoreDataWithPage(new_browser, "cookies.html"); +} diff --git a/chrome/browser/sessions/session_data_deleter.cc b/chrome/browser/sessions/session_data_deleter.cc new file mode 100644 index 0000000000..2378731718 --- /dev/null +++ b/chrome/browser/sessions/session_data_deleter.cc @@ -0,0 +1,169 @@ +// Copyright 2013 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/bind.h" +#include "base/command_line.h" +#include "chrome/browser/browser_shutdown.h" +#include "chrome/browser/net/chrome_url_request_context.h" +#include "chrome/browser/prefs/session_startup_pref.h" +#include "chrome/browser/profiles/profile_io_data.h" +#include "chrome/browser/ui/startup/startup_browser_creator.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/dom_storage_context.h" +#include "content/public/browser/local_storage_usage_info.h" +#include "content/public/browser/storage_partition.h" +#include "net/cookies/cookie_monster.h" +#include "net/cookies/cookie_store.h" +#include "net/cookies/cookie_util.h" +#include "webkit/browser/quota/special_storage_policy.h" + +namespace { + +void CookieDeleted(bool success) { + DCHECK(success); +} + +class SessionDataDeleter + : public base::RefCountedThreadSafe<SessionDataDeleter> { + public: + SessionDataDeleter(quota::SpecialStoragePolicy* storage_policy, + bool delete_only_by_session_only_policy); + + void Run(content::StoragePartition* storage_partition, + ProfileIOData* profile_io_data); + + private: + friend class base::RefCountedThreadSafe<SessionDataDeleter>; + ~SessionDataDeleter(); + + // Deletes the local storage described by |usages| for origins which are + // session-only. + void ClearSessionOnlyLocalStorage( + content::StoragePartition* storage_partition, + const std::vector<content::LocalStorageUsageInfo>& usages); + + // Deletes all cookies that are session only if + // |delete_only_by_session_only_policy_| is false. Once completed or skipped, + // this arranges for DeleteSessionOnlyOriginCookies to be called with a list + // of all remaining cookies. + void DeleteSessionCookiesOnIOThread(ProfileIOData* profile_io_data); + + // Called when all session-only cookies have been deleted. + void DeleteSessionCookiesDone(int num_deleted); + + // Deletes the cookies in |cookies| that are for origins which are + // session-only. + void DeleteSessionOnlyOriginCookies(const net::CookieList& cookies); + + base::WeakPtr<ChromeURLRequestContext> request_context_; + scoped_refptr<quota::SpecialStoragePolicy> storage_policy_; + const bool delete_only_by_session_only_policy_; + + DISALLOW_COPY_AND_ASSIGN(SessionDataDeleter); +}; + +SessionDataDeleter::SessionDataDeleter( + quota::SpecialStoragePolicy* storage_policy, + bool delete_only_by_session_only_policy) + : storage_policy_(storage_policy), + delete_only_by_session_only_policy_(delete_only_by_session_only_policy) {} + +void SessionDataDeleter::Run(content::StoragePartition* storage_partition, + ProfileIOData* profile_io_data) { + storage_partition->GetDOMStorageContext()->GetLocalStorageUsage( + base::Bind(&SessionDataDeleter::ClearSessionOnlyLocalStorage, + this, + storage_partition)); + content::BrowserThread::PostTask( + content::BrowserThread::IO, + FROM_HERE, + base::Bind(&SessionDataDeleter::DeleteSessionCookiesOnIOThread, + this, + profile_io_data)); +} + +SessionDataDeleter::~SessionDataDeleter() {} + +void SessionDataDeleter::ClearSessionOnlyLocalStorage( + content::StoragePartition* storage_partition, + const std::vector<content::LocalStorageUsageInfo>& usages) { + for (std::vector<content::LocalStorageUsageInfo>::const_iterator it = + usages.begin(); + it != usages.end(); + ++it) { + if (storage_policy_->IsStorageSessionOnly(it->origin)) + storage_partition->GetDOMStorageContext()->DeleteLocalStorage(it->origin); + } +} + +void SessionDataDeleter::DeleteSessionCookiesOnIOThread( + ProfileIOData* profile_io_data) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + ChromeURLRequestContext* request_context = + profile_io_data->GetMainRequestContext(); + request_context_ = request_context->GetWeakPtr(); + net::CookieMonster* cookie_monster = + request_context_->cookie_store()->GetCookieMonster(); + if (delete_only_by_session_only_policy_) { + cookie_monster->GetAllCookiesAsync( + base::Bind(&SessionDataDeleter::DeleteSessionOnlyOriginCookies, this)); + } else { + cookie_monster->DeleteSessionCookiesAsync( + base::Bind(&SessionDataDeleter::DeleteSessionCookiesDone, this)); + } +} + +void SessionDataDeleter::DeleteSessionCookiesDone(int num_deleted) { + ChromeURLRequestContext* request_context = request_context_.get(); + if (!request_context) + return; + + request_context->cookie_store()->GetCookieMonster()->GetAllCookiesAsync( + base::Bind(&SessionDataDeleter::DeleteSessionOnlyOriginCookies, this)); +} + +void SessionDataDeleter::DeleteSessionOnlyOriginCookies( + const net::CookieList& cookies) { + ChromeURLRequestContext* request_context = request_context_.get(); + if (!request_context) + return; + + net::CookieMonster* cookie_monster = + request_context->cookie_store()->GetCookieMonster(); + for (net::CookieList::const_iterator it = cookies.begin(); + it != cookies.end(); + ++it) { + if (storage_policy_->IsStorageSessionOnly( + net::cookie_util::CookieOriginToURL(it->Domain(), + it->IsSecure()))) { + cookie_monster->DeleteCanonicalCookieAsync(*it, + base::Bind(CookieDeleted)); + } + } +} + +} // namespace + +void DeleteSessionOnlyData(Profile* profile) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + if (browser_shutdown::IsTryingToQuit()) + return; + +#if defined(OS_ANDROID) + SessionStartupPref::Type startup_pref_type = + SessionStartupPref::GetDefaultStartupType(); +#else + SessionStartupPref::Type startup_pref_type = + StartupBrowserCreator::GetSessionStartupPref( + *CommandLine::ForCurrentProcess(), profile).type; +#endif + + scoped_refptr<SessionDataDeleter> deleter( + new SessionDataDeleter(profile->GetSpecialStoragePolicy(), + startup_pref_type == SessionStartupPref::LAST)); + deleter->Run( + Profile::GetDefaultStoragePartition(profile), + ProfileIOData::FromResourceContext(profile->GetResourceContext())); +} diff --git a/chrome/browser/sessions/session_data_deleter.h b/chrome/browser/sessions/session_data_deleter.h new file mode 100644 index 0000000000..710cd7e41e --- /dev/null +++ b/chrome/browser/sessions/session_data_deleter.h @@ -0,0 +1,15 @@ +// Copyright 2013 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_SESSIONS_SESSION_DATA_DELETER_H_ +#define CHROME_BROWSER_SESSIONS_SESSION_DATA_DELETER_H_ + +class Profile; + +// Clears cookies and local storage for origins that are session-only and clears +// session cookies unless the startup preference is to continue the previous +// session. +void DeleteSessionOnlyData(Profile* profile); + +#endif // CHROME_BROWSER_SESSIONS_SESSION_DATA_DELETER_H_ diff --git a/chrome/browser/sessions/session_service.cc b/chrome/browser/sessions/session_service.cc index 8af2138194..0b7aca805c 100644 --- a/chrome/browser/sessions/session_service.cc +++ b/chrome/browser/sessions/session_service.cc @@ -16,12 +16,16 @@ #include "base/metrics/histogram.h" #include "base/pickle.h" #include "base/threading/thread.h" +#include "chrome/browser/background/background_mode_manager.h" +#include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/defaults.h" #include "chrome/browser/extensions/tab_helper.h" #include "chrome/browser/prefs/session_startup_pref.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/sessions/session_backend.h" #include "chrome/browser/sessions/session_command.h" +#include "chrome/browser/sessions/session_data_deleter.h" #include "chrome/browser/sessions/session_restore.h" #include "chrome/browser/sessions/session_tab_helper.h" #include "chrome/browser/sessions/session_types.h" @@ -338,6 +342,14 @@ void SessionService::WindowClosed(const SessionID& window_id) { else ScheduleCommand(CreateWindowClosedCommand(window_id.id())); } + // Clear session data if the last window for a profile has been closed and + // closing the last window would normally close Chrome, unless background mode + // is active. + if (!has_open_trackable_browsers_ && + !browser_defaults::kBrowserAliveWithNoWindows && + !g_browser_process->background_mode_manager()->IsBackgroundModeActive()) { + DeleteSessionOnlyData(profile()); + } } void SessionService::SetWindowType(const SessionID& window_id, diff --git a/chrome/browser/sessions/session_service_factory.cc b/chrome/browser/sessions/session_service_factory.cc index 05cf198989..7e928f988d 100644 --- a/chrome/browser/sessions/session_service_factory.cc +++ b/chrome/browser/sessions/session_service_factory.cc @@ -5,6 +5,7 @@ #include "chrome/browser/sessions/session_service_factory.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/sessions/session_data_deleter.h" #include "chrome/browser/sessions/session_service.h" #include "components/browser_context_keyed_service/browser_context_dependency_manager.h" @@ -33,6 +34,8 @@ SessionService* SessionServiceFactory::GetForProfileIfExisting( // static void SessionServiceFactory::ShutdownForProfile(Profile* profile) { + DeleteSessionOnlyData(profile); + // We're about to exit, force creation of the session service if it hasn't // been created yet. We do this to ensure session state matches the point in // time the user exited. diff --git a/chrome/browser/sessions/tab_restore_browsertest.cc b/chrome/browser/sessions/tab_restore_browsertest.cc index 31f831061d..4d6e849f82 100644 --- a/chrome/browser/sessions/tab_restore_browsertest.cc +++ b/chrome/browser/sessions/tab_restore_browsertest.cc @@ -26,6 +26,7 @@ #include "content/public/browser/page_navigator.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" +#include "content/public/test/browser_test_utils.h" #include "net/base/net_util.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "third_party/WebKit/public/web/WebFindOptions.h" @@ -70,12 +71,11 @@ class TabRestoreTest : public InProcessBrowserTest { } void CloseTab(int index) { - content::WindowedNotificationObserver tab_close_observer( - content::NOTIFICATION_WEB_CONTENTS_DESTROYED, - content::NotificationService::AllSources()); + content::WebContentsDestroyedWatcher destroyed_watcher( + browser()->tab_strip_model()->GetWebContentsAt(index)); browser()->tab_strip_model()->CloseWebContentsAt( index, TabStripModel::CLOSE_CREATE_HISTORICAL_TAB); - tab_close_observer.Wait(); + destroyed_watcher.Wait(); } // Uses the undo-close-tab accelerator to undo a close-tab or close-window diff --git a/chrome/browser/signin/account_reconcilor.cc b/chrome/browser/signin/account_reconcilor.cc new file mode 100644 index 0000000000..7f27ec5950 --- /dev/null +++ b/chrome/browser/signin/account_reconcilor.cc @@ -0,0 +1,15 @@ +// Copyright 2013 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/signin/account_reconcilor.h" + +#include "chrome/browser/profiles/profile.h" + +AccountReconcilor::AccountReconcilor(Profile* profile) : profile_(profile) { +} + +AccountReconcilor::~AccountReconcilor() {} + +void AccountReconcilor::Shutdown() {} + diff --git a/chrome/browser/signin/account_reconcilor.h b/chrome/browser/signin/account_reconcilor.h new file mode 100644 index 0000000000..b57fa9c585 --- /dev/null +++ b/chrome/browser/signin/account_reconcilor.h @@ -0,0 +1,30 @@ +// Copyright 2013 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_SIGNIN_ACCOUNT_RECONCILOR_H_ +#define CHROME_BROWSER_SIGNIN_ACCOUNT_RECONCILOR_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "components/browser_context_keyed_service/browser_context_keyed_service.h" + +class Profile; + +class AccountReconcilor : public BrowserContextKeyedService { + public: + AccountReconcilor(Profile* profile); + virtual ~AccountReconcilor(); + + // BrowserContextKeyedService implementation. + virtual void Shutdown() OVERRIDE; + + Profile* profile() { return profile_; } + + private: + // The profile that this reconcilor belongs to. + Profile* profile_; + + DISALLOW_COPY_AND_ASSIGN(AccountReconcilor); +}; + +#endif // CHROME_BROWSER_SIGNIN_ACCOUNT_RECONCILOR_H_ diff --git a/chrome/browser/signin/account_reconcilor_factory.cc b/chrome/browser/signin/account_reconcilor_factory.cc new file mode 100644 index 0000000000..ef250e8d12 --- /dev/null +++ b/chrome/browser/signin/account_reconcilor_factory.cc @@ -0,0 +1,41 @@ +// Copyright 2013 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/signin/account_reconcilor_factory.h" + +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" +#include "chrome/browser/signin/signin_manager_factory.h" +#include "components/browser_context_keyed_service/browser_context_dependency_manager.h" + +AccountReconcilorFactory::AccountReconcilorFactory() + : BrowserContextKeyedServiceFactory( + "AccountReconcilor", + BrowserContextDependencyManager::GetInstance()) { + DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance()); + DependsOn(SigninManagerFactory::GetInstance()); +} + +AccountReconcilorFactory::~AccountReconcilorFactory() {} + +// static +AccountReconcilor* AccountReconcilorFactory::GetForProfile( + Profile* profile) { + return static_cast<AccountReconcilor*>( + GetInstance()->GetServiceForBrowserContext(profile, true)); +} + +// static +AccountReconcilorFactory* AccountReconcilorFactory::GetInstance() { + return Singleton<AccountReconcilorFactory>::get(); +} + +BrowserContextKeyedService* AccountReconcilorFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + return new AccountReconcilor(static_cast<Profile*>(context)); +} + +void AccountReconcilorFactory::RegisterProfilePrefs( + user_prefs::PrefRegistrySyncable* registry) { +} diff --git a/chrome/browser/signin/account_reconcilor_factory.h b/chrome/browser/signin/account_reconcilor_factory.h new file mode 100644 index 0000000000..f17fdf1e80 --- /dev/null +++ b/chrome/browser/signin/account_reconcilor_factory.h @@ -0,0 +1,40 @@ +// Copyright 2013 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_SIGNIN_ACCOUNT_RECONCILOR_FACTORY_H_ +#define CHROME_BROWSER_SIGNIN_ACCOUNT_RECONCILOR_FACTORY_H_ + +#include "base/memory/singleton.h" +#include "chrome/browser/signin/account_reconcilor.h" +#include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h" + +class AccountReconcilor; +class Profile; + +// Singleton that owns all AccountReconcilors and associates them with +// Profiles. Listens for the Profile's destruction notification and cleans up. +class AccountReconcilorFactory : public BrowserContextKeyedServiceFactory { + public: + // Returns the instance of AccountReconcilor associated with this profile + // (creating one if none exists). Returns NULL if this profile cannot have an + // AccountReconcilor (for example, if |profile| is incognito). + static AccountReconcilor* GetForProfile(Profile* profile); + + // Returns an instance of the factory singleton. + static AccountReconcilorFactory* GetInstance(); + + private: + friend struct DefaultSingletonTraits<AccountReconcilorFactory>; + + AccountReconcilorFactory(); + virtual ~AccountReconcilorFactory(); + + // BrowserContextKeyedServiceFactory: + virtual BrowserContextKeyedService* BuildServiceInstanceFor( + content::BrowserContext* profile) const OVERRIDE; + virtual void RegisterProfilePrefs( + user_prefs::PrefRegistrySyncable* registry) OVERRIDE; +}; + +#endif // CHROME_BROWSER_SIGNIN_ACCOUNT_RECONCILOR_FACTORY_H_ diff --git a/chrome/browser/signin/account_reconcilor_unittest.cc b/chrome/browser/signin/account_reconcilor_unittest.cc new file mode 100644 index 0000000000..c21acc88c9 --- /dev/null +++ b/chrome/browser/signin/account_reconcilor_unittest.cc @@ -0,0 +1,52 @@ +// Copyright 2013 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/memory/scoped_ptr.h" +#include "chrome/browser/signin/account_reconcilor.h" +#include "chrome/browser/signin/account_reconcilor_factory.h" +#include "chrome/browser/signin/fake_profile_oauth2_token_service.h" +#include "chrome/browser/signin/fake_signin_manager.h" +#include "chrome/browser/signin/profile_oauth2_token_service.h" +#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" +#include "chrome/browser/signin/signin_manager.h" +#include "chrome/browser/signin/signin_manager_factory.h" +#include "chrome/test/base/testing_profile.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class AccountReconcilorTest : public testing::Test { + public: + virtual void SetUp() OVERRIDE; + virtual void TearDown() OVERRIDE; + + TestingProfile* profile() { return profile_.get(); } + +private: + scoped_ptr<TestingProfile> profile_; +}; + +void AccountReconcilorTest::SetUp() { + TestingProfile::Builder builder; + builder.AddTestingFactory(ProfileOAuth2TokenServiceFactory::GetInstance(), + FakeProfileOAuth2TokenService::Build); + builder.AddTestingFactory(SigninManagerFactory::GetInstance(), + FakeSigninManagerBase::Build); + profile_ = builder.Build(); +} + +void AccountReconcilorTest::TearDown() { + // Destroy the profile before all threads are torn down. + profile_.reset(); +} + +} // namespace + +TEST_F(AccountReconcilorTest, Basic) { + AccountReconcilor* reconcilor = + AccountReconcilorFactory::GetForProfile(profile()); + ASSERT_TRUE(NULL != reconcilor); + ASSERT_EQ(profile(), reconcilor->profile()); +} diff --git a/chrome/browser/signin/fake_profile_oauth2_token_service.cc b/chrome/browser/signin/fake_profile_oauth2_token_service.cc index 6c864b1557..6f783bf5f5 100644 --- a/chrome/browser/signin/fake_profile_oauth2_token_service.cc +++ b/chrome/browser/signin/fake_profile_oauth2_token_service.cc @@ -31,11 +31,17 @@ bool FakeProfileOAuth2TokenService::RefreshTokenIsAvailable( void FakeProfileOAuth2TokenService::IssueRefreshToken( const std::string& token) { + IssueRefreshTokenForUser("account_id", token); +} + +void FakeProfileOAuth2TokenService::IssueRefreshTokenForUser( + const std::string& account_id, + const std::string& token) { refresh_token_ = token; if (refresh_token_.empty()) - FireRefreshTokenRevoked("account_id"); + FireRefreshTokenRevoked(account_id); else - FireRefreshTokenAvailable("account_id"); + FireRefreshTokenAvailable(account_id); // TODO(atwilson): Maybe we should also call FireRefreshTokensLoaded() here? } @@ -79,6 +85,7 @@ void FakeProfileOAuth2TokenService::CompleteRequests( const base::Time& expiration) { std::vector<FakeProfileOAuth2TokenService::PendingRequest> requests = GetPendingRequests(); + // Walk the requests and notify the callbacks. for (std::vector<PendingRequest>::iterator it = pending_requests_.begin(); it != pending_requests_.end(); ++it) { diff --git a/chrome/browser/signin/fake_profile_oauth2_token_service.h b/chrome/browser/signin/fake_profile_oauth2_token_service.h index e8e7dc85e6..d7e4923162 100644 --- a/chrome/browser/signin/fake_profile_oauth2_token_service.h +++ b/chrome/browser/signin/fake_profile_oauth2_token_service.h @@ -71,6 +71,11 @@ class FakeProfileOAuth2TokenService // OnRefreshTokenRevoked(). void IssueRefreshToken(const std::string& token); + // TODO(fgorski,rogerta): Merge with UpdateCredentials when this class fully + // supports multiple accounts. + void IssueRefreshTokenForUser(const std::string& account_id, + const std::string& token); + // Gets a list of active requests (can be used by tests to validate that the // correct request has been issued). std::vector<PendingRequest> GetPendingRequests(); diff --git a/chrome/browser/signin/signin_browsertest.cc b/chrome/browser/signin/signin_browsertest.cc index d3031c0dbe..f85b8bdd47 100644 --- a/chrome/browser/signin/signin_browsertest.cc +++ b/chrome/browser/signin/signin_browsertest.cc @@ -171,6 +171,7 @@ class BackOnNTPCommitObserver : public content::WebContentsObserver { virtual void DidCommitProvisionalLoadForFrame( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& url, content::PageTransition transition_type, diff --git a/chrome/browser/signin/signin_promo.cc b/chrome/browser/signin/signin_promo.cc index fd64bddb1d..be3c5d625f 100644 --- a/chrome/browser/signin/signin_promo.cc +++ b/chrome/browser/signin/signin_promo.cc @@ -176,6 +176,17 @@ GURL GetLandingURL(const char* option, int value) { GURL GetPromoURL(Source source, bool auto_close) { DCHECK_NE(SOURCE_UNKNOWN, source); + bool enable_inline = CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableInlineSignin); + if (enable_inline) { + std::string url(chrome::kChromeUIInlineLoginURL); + base::StringAppendF(&url, "?%s=%d", kSignInPromoQueryKeySource, source); + if (auto_close) + base::StringAppendF( + &url, "&%s=1", kSignInPromoQueryKeyAutoClose); + return GURL(url); + } + // Build a Gaia-based URL that can be used to sign the user into chrome. // There are required request parameters: // diff --git a/chrome/browser/ssl/ssl_blocking_page.cc b/chrome/browser/ssl/ssl_blocking_page.cc index 056321f5ff..1c380a26bd 100644 --- a/chrome/browser/ssl/ssl_blocking_page.cc +++ b/chrome/browser/ssl/ssl_blocking_page.cc @@ -7,7 +7,9 @@ #include "base/i18n/rtl.h" #include "base/metrics/field_trial.h" #include "base/metrics/histogram.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/string_piece.h" +#include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/history/history_service_factory.h" @@ -29,6 +31,7 @@ #include "grit/app_locale_settings.h" #include "grit/browser_resources.h" #include "grit/generated_resources.h" +#include "net/base/hash_value.h" #include "net/base/net_errors.h" #include "net/base/net_util.h" #include "ui/base/l10n/l10n_util.h" @@ -51,7 +54,8 @@ enum SSLBlockingPageCommands { CMD_DONT_PROCEED, CMD_PROCEED, CMD_FOCUS, - CMD_MORE + CMD_MORE, + CMD_RELOAD, }; // Events for UMA. @@ -198,51 +202,143 @@ SSLBlockingPage::~SSLBlockingPage() { } std::string SSLBlockingPage::GetHTMLContents() { - // Let's build the html error page. DictionaryValue strings; - SSLErrorInfo error_info = - SSLErrorInfo::CreateError(SSLErrorInfo::NetErrorToErrorType(cert_error_), - ssl_info_.cert.get(), - request_url_); - - int resource_id = IDR_SSL_ROAD_BLOCK_HTML; - strings.SetString("headLine", error_info.title()); - strings.SetString("description", error_info.details()); - strings.SetString("moreInfoTitle", - l10n_util::GetStringUTF16(IDS_CERT_ERROR_EXTRA_INFO_TITLE)); - SetExtraInfo(&strings, error_info.extra_information()); - - strings.SetString("exit", - l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_EXIT)); - + int resource_id; if (overridable_ && !strict_enforcement_) { - strings.SetString("title", - l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_TITLE)); - strings.SetString("proceed", - l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_PROCEED)); - strings.SetString("reasonForNotProceeding", - l10n_util::GetStringUTF16( - IDS_SSL_BLOCKING_PAGE_SHOULD_NOT_PROCEED)); + // Let's build the overridable error page. + SSLErrorInfo error_info = + SSLErrorInfo::CreateError( + SSLErrorInfo::NetErrorToErrorType(cert_error_), + ssl_info_.cert.get(), + request_url_); + + resource_id = IDR_SSL_ROAD_BLOCK_HTML; + strings.SetString("headLine", error_info.title()); + strings.SetString("description", error_info.details()); + strings.SetString("moreInfoTitle", + l10n_util::GetStringUTF16(IDS_CERT_ERROR_EXTRA_INFO_TITLE)); + SetExtraInfo(&strings, error_info.extra_information()); + + strings.SetString( + "exit", l10n_util::GetStringUTF16(IDS_SSL_OVERRIDABLE_PAGE_EXIT)); + strings.SetString( + "title", l10n_util::GetStringUTF16(IDS_SSL_OVERRIDABLE_PAGE_TITLE)); + strings.SetString( + "proceed", l10n_util::GetStringUTF16(IDS_SSL_OVERRIDABLE_PAGE_PROCEED)); + strings.SetString( + "reasonForNotProceeding", l10n_util::GetStringUTF16( + IDS_SSL_OVERRIDABLE_PAGE_SHOULD_NOT_PROCEED)); strings.SetString("errorType", "overridable"); + strings.SetString("textdirection", base::i18n::IsRTL() ? "rtl" : "ltr"); } else { - strings.SetString("title", - l10n_util::GetStringUTF16(IDS_SSL_ERROR_PAGE_TITLE)); - if (strict_enforcement_) { - strings.SetString("reasonForNotProceeding", - l10n_util::GetStringUTF16( - IDS_SSL_ERROR_PAGE_CANNOT_PROCEED)); + // Let's build the blocking error page. + resource_id = IDR_SSL_BLOCKING_HTML; + + // Strings that are not dependent on the URL. + strings.SetString( + "title", l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_TITLE)); + strings.SetString( + "reloadMsg", l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_RELOAD)); + strings.SetString( + "more", l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_MORE)); + strings.SetString( + "less", l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_LESS)); + strings.SetString( + "moreTitle", + l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_MORE_TITLE)); + strings.SetString( + "techTitle", + l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_TECH_TITLE)); + + // Strings that are dependent on the URL. + string16 url(ASCIIToUTF16(request_url_.host())); + bool rtl = base::i18n::IsRTL(); + strings.SetString("textDirection", rtl ? "rtl" : "ltr"); + if (rtl) + base::i18n::WrapStringWithLTRFormatting(&url); + strings.SetString( + "headline", l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_HEADLINE, + url.c_str())); + strings.SetString( + "message", l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_BODY_TEXT, + url.c_str())); + strings.SetString( + "moreMessage", + l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_MORE_TEXT, + url.c_str())); + strings.SetString("reloadUrl", request_url_.spec()); + + // Strings that are dependent on the error type. + SSLErrorInfo::ErrorType type = + SSLErrorInfo::NetErrorToErrorType(cert_error_); + string16 errorType; + if (type == SSLErrorInfo::CERT_REVOKED) { + errorType = string16(ASCIIToUTF16("Key revocation")); + strings.SetString( + "failure", + l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_REVOKED)); + } else if (type == SSLErrorInfo::CERT_INVALID) { + errorType = string16(ASCIIToUTF16("Malformed certificate")); + strings.SetString( + "failure", + l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_FORMATTED)); + } else if (type == SSLErrorInfo::CERT_PINNED_KEY_MISSING) { + errorType = string16(ASCIIToUTF16("Certificate pinning failure")); + strings.SetString( + "failure", + l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_PINNING, + url.c_str())); + } else if (type == SSLErrorInfo::CERT_WEAK_KEY_DH) { + errorType = string16(ASCIIToUTF16("Weak DH public key")); + strings.SetString( + "failure", + l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_WEAK_DH, + url.c_str())); } else { - strings.SetString("reasonForNotProceeding", std::string()); + // HSTS failure. + errorType = string16(ASCIIToUTF16("HSTS failure")); + strings.SetString( + "failure", + l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_HSTS, url.c_str())); + } + if (rtl) + base::i18n::WrapStringWithLTRFormatting(&errorType); + strings.SetString( + "errorType", l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_ERROR, + errorType.c_str())); + + // Strings that display the invalid cert. + string16 subject(ASCIIToUTF16(ssl_info_.cert->subject().GetDisplayName())); + string16 issuer(ASCIIToUTF16(ssl_info_.cert->issuer().GetDisplayName())); + std::string hashes; + for (std::vector<net::HashValue>::iterator it = + ssl_info_.public_key_hashes.begin(); + it != ssl_info_.public_key_hashes.end(); + ++it) { + base::StringAppendF(&hashes, "%s ", it->ToString().c_str()); } - strings.SetString("errorType", "notoverridable"); + string16 fingerprint(ASCIIToUTF16(hashes)); + if (rtl) { + // These are always going to be LTR. + base::i18n::WrapStringWithLTRFormatting(&subject); + base::i18n::WrapStringWithLTRFormatting(&issuer); + base::i18n::WrapStringWithLTRFormatting(&fingerprint); + } + strings.SetString( + "subject", l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_SUBJECT, + subject.c_str())); + strings.SetString( + "issuer", l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_ISSUER, + issuer.c_str())); + strings.SetString( + "fingerprint", + l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_HASHES, + fingerprint.c_str())); } - strings.SetString("textdirection", base::i18n::IsRTL() ? "rtl" : "ltr"); - base::StringPiece html( ResourceBundle::GetSharedInstance().GetRawDataResource( resource_id)); - return webui::GetI18nTemplateHtml(html, &strings); } @@ -274,6 +370,10 @@ void SSLBlockingPage::CommandReceived(const std::string& command) { display_start_time_ = base::TimeTicks::Now(); } else if (cmd == CMD_MORE) { RecordSSLBlockingPageEventStats(MORE); + } else if (cmd == CMD_RELOAD) { + // The interstitial can't refresh itself. + content::NavigationController* controller = &web_contents_->GetController(); + controller->Reload(true); } } diff --git a/chrome/browser/ssl/ssl_browser_tests.cc b/chrome/browser/ssl/ssl_browser_tests.cc index 991d5b9241..cba27ee6a0 100644 --- a/chrome/browser/ssl/ssl_browser_tests.cc +++ b/chrome/browser/ssl/ssl_browser_tests.cc @@ -75,6 +75,7 @@ class ProvisionalLoadWaiter : public content::WebContentsObserver { virtual void DidFailProvisionalLoad( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& validated_url, int error_code, diff --git a/chrome/browser/ssl/ssl_error_info.cc b/chrome/browser/ssl/ssl_error_info.cc index d11beb80f3..ac30ed084b 100644 --- a/chrome/browser/ssl/ssl_error_info.cc +++ b/chrome/browser/ssl/ssl_error_info.cc @@ -198,6 +198,23 @@ SSLErrorInfo SSLErrorInfo::CreateError(ErrorType error_type, l10n_util::GetStringUTF16( IDS_CERT_ERROR_WEAK_KEY_EXTRA_INFO_2)); break; + case CERT_WEAK_KEY_DH: + title = l10n_util::GetStringUTF16( + IDS_ERRORPAGES_HEADING_WEAK_SERVER_EPHEMERAL_DH_KEY); + details = l10n_util::GetStringFUTF16( + IDS_CERT_ERROR_WEAK_KEY_DETAILS, UTF8ToUTF16(request_url.host())); + short_description = l10n_util::GetStringUTF16( + IDS_CERT_ERROR_WEAK_KEY_DESCRIPTION); + extra_info.push_back( + l10n_util::GetStringUTF16( + IDS_ERRORPAGES_SUMMARY_WEAK_SERVER_EPHEMERAL_DH_KEY)); + case CERT_PINNED_KEY_MISSING: + title = l10n_util::GetStringUTF16( + IDS_ERRORPAGES_HEADING_PINNING_FAILURE); + details = l10n_util::GetStringUTF16( + IDS_ERRORPAGES_SUMMARY_PINNING_FAILURE); + short_description = l10n_util::GetStringUTF16( + IDS_ERRORPAGES_DETAILS_PINNING_FAILURE); case UNKNOWN: title = l10n_util::GetStringUTF16(IDS_CERT_ERROR_UNKNOWN_ERROR_TITLE); details = l10n_util::GetStringUTF16(IDS_CERT_ERROR_UNKNOWN_ERROR_DETAILS); @@ -236,6 +253,10 @@ SSLErrorInfo::ErrorType SSLErrorInfo::NetErrorToErrorType(int net_error) { return CERT_WEAK_SIGNATURE_ALGORITHM; case net::ERR_CERT_WEAK_KEY: return CERT_WEAK_KEY; + case net::ERR_SSL_WEAK_SERVER_EPHEMERAL_DH_KEY: + return CERT_WEAK_KEY_DH; + case net::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN: + return CERT_PINNED_KEY_MISSING; default: NOTREACHED(); return UNKNOWN; diff --git a/chrome/browser/ssl/ssl_error_info.h b/chrome/browser/ssl/ssl_error_info.h index 5e29523edb..b50d225913 100644 --- a/chrome/browser/ssl/ssl_error_info.h +++ b/chrome/browser/ssl/ssl_error_info.h @@ -33,6 +33,8 @@ class SSLErrorInfo { CERT_WEAK_SIGNATURE_ALGORITHM, CERT_WEAK_KEY, UNKNOWN, + CERT_WEAK_KEY_DH, + CERT_PINNED_KEY_MISSING, END_OF_ENUM }; diff --git a/chrome/browser/storage_monitor/image_capture_device.mm b/chrome/browser/storage_monitor/image_capture_device.mm index e70be83f8d..dfbb60646f 100644 --- a/chrome/browser/storage_monitor/image_capture_device.mm +++ b/chrome/browser/storage_monitor/image_capture_device.mm @@ -9,23 +9,20 @@ namespace { -void RenameFile(const base::FilePath& downloaded_filename, - const base::FilePath& desired_filename, - base::PlatformFileError* result) { +base::PlatformFileError RenameFile(const base::FilePath& downloaded_filename, + const base::FilePath& desired_filename) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); bool success = base::ReplaceFile(downloaded_filename, desired_filename, NULL); - *result = success ? base::PLATFORM_FILE_OK - : base::PLATFORM_FILE_ERROR_NOT_FOUND; + return success ? base::PLATFORM_FILE_OK : base::PLATFORM_FILE_ERROR_NOT_FOUND; } void ReturnRenameResultToListener( base::WeakPtr<ImageCaptureDeviceListener> listener, const std::string& name, - base::PlatformFileError* result) { + const base::PlatformFileError& result) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - scoped_ptr<base::PlatformFileError> result_deleter(result); if (listener) - listener->DownloadedFile(name, *result); + listener->DownloadedFile(name, result); } base::Time NSDateToBaseTime(NSDate* date) { @@ -214,12 +211,11 @@ base::FilePath PathForCameraItem(ICCameraItem* item) { base::FilePath saveAsPath = saveDir.Append(saveAsFilename); base::FilePath savedPath = saveDir.Append(savedFilename); // Shared result value from file-copy closure to tell-listener closure. - base::PlatformFileError* copyResult = new base::PlatformFileError(); - content::BrowserThread::PostTaskAndReply( + content::BrowserThread::PostTaskAndReplyWithResult( content::BrowserThread::FILE, FROM_HERE, - base::Bind(&RenameFile, savedPath, saveAsPath, copyResult), - base::Bind(&ReturnRenameResultToListener, listener_, name, copyResult)); + base::Bind(&RenameFile, savedPath, saveAsPath), + base::Bind(&ReturnRenameResultToListener, listener_, name)); } @end // ImageCaptureDevice diff --git a/chrome/browser/storage_monitor/storage_monitor_chromeos_unittest.cc b/chrome/browser/storage_monitor/storage_monitor_chromeos_unittest.cc index 2ab67a8a92..880b6f93c8 100644 --- a/chrome/browser/storage_monitor/storage_monitor_chromeos_unittest.cc +++ b/chrome/browser/storage_monitor/storage_monitor_chromeos_unittest.cc @@ -30,13 +30,10 @@ using content::BrowserThread; using disks::DiskMountManager; using testing::_; -const char kDeviceNameWithManufacturerDetails[] = "110 KB (CompanyA, Z101)"; const char kDevice1[] = "/dev/d1"; const char kDevice1Name[] = "d1"; -const char kDevice1NameWithSizeInfo[] = "110 KB d1"; const char kDevice2[] = "/dev/disk/d2"; const char kDevice2Name[] = "d2"; -const char kDevice2NameWithSizeInfo[] = "207 KB d2"; const char kEmptyDeviceLabel[] = ""; const char kMountPointA[] = "mnt_a"; const char kMountPointB[] = "mnt_b"; diff --git a/chrome/browser/sync/profile_sync_service_android.cc b/chrome/browser/sync/profile_sync_service_android.cc index 19f94ec92f..ec7d507513 100644 --- a/chrome/browser/sync/profile_sync_service_android.cc +++ b/chrome/browser/sync/profile_sync_service_android.cc @@ -46,7 +46,6 @@ using base::android::ScopedJavaLocalRef; using content::BrowserThread; namespace { -const char kSyncDisabledStatus[] = "OFFLINE_DISABLED"; enum { #define DEFINE_MODEL_TYPE_SELECTION(name,value) name = value, diff --git a/chrome/browser/sync/profile_sync_service_preference_unittest.cc b/chrome/browser/sync/profile_sync_service_preference_unittest.cc index 3c5dbe6749..b3683bfada 100644 --- a/chrome/browser/sync/profile_sync_service_preference_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_preference_unittest.cc @@ -10,6 +10,7 @@ #include "base/callback.h" #include "base/json/json_reader.h" #include "base/json/json_string_value_serializer.h" +#include "base/json/json_writer.h" #include "base/location.h" #include "base/stl_util.h" #include "base/strings/string_piece.h" @@ -364,6 +365,7 @@ TEST_F(ProfileSyncServicePreferenceTest, ModelAssociationCloudHasData) { } PreferenceValues cloud_data; + STLValueDeleter<PreferenceValues> cloud_data_deleter(&cloud_data); cloud_data[prefs::kHomePage] = Value::CreateStringValue(example_url1_); ListValue* urls_to_restore = new ListValue; urls_to_restore->Append(Value::CreateStringValue(example_url1_)); @@ -399,7 +401,165 @@ TEST_F(ProfileSyncServicePreferenceTest, ModelAssociationCloudHasData) { EXPECT_EQ(non_default_charset_value_, string_value); EXPECT_EQ(non_default_charset_value_, prefs_->GetString(prefs::kDefaultCharset)); - STLDeleteValues(&cloud_data); +} + +TEST_F(ProfileSyncServicePreferenceTest, + ModelAssociationCloudHasOldMigratedData) { + ASSERT_TRUE(PrefModelAssociator::IsMigratedPreference( + prefs::kURLsToRestoreOnStartup)); + ASSERT_TRUE(PrefModelAssociator::IsOldMigratedPreference( + prefs::kURLsToRestoreOnStartupOld)); + prefs_->SetString(prefs::kHomePage, example_url0_); + { + ListPrefUpdate update(prefs_, prefs::kURLsToRestoreOnStartup); + ListValue* url_list = update.Get(); + url_list->Append(Value::CreateStringValue(example_url0_)); + url_list->Append(Value::CreateStringValue(example_url1_)); + } + + PreferenceValues cloud_data; + STLValueDeleter<PreferenceValues> cloud_data_deleter(&cloud_data); + cloud_data[prefs::kHomePage] = Value::CreateStringValue(example_url1_); + ListValue* urls_to_restore = new ListValue; + urls_to_restore->Append(Value::CreateStringValue(example_url1_)); + urls_to_restore->Append(Value::CreateStringValue(example_url2_)); + cloud_data[prefs::kURLsToRestoreOnStartupOld] = urls_to_restore; + + AddPreferenceEntriesHelper helper(this, cloud_data); + ASSERT_TRUE(StartSyncService(helper.callback(), false)); + ASSERT_TRUE(helper.success()); + + scoped_ptr<const Value> value(GetSyncedValue(prefs::kHomePage)); + ASSERT_TRUE(value.get()); + std::string string_value; + EXPECT_TRUE(value->GetAsString(&string_value)); + EXPECT_EQ(example_url1_, string_value); + EXPECT_EQ(example_url1_, prefs_->GetString(prefs::kHomePage)); + + // Expect that the new preference data contains the merged old prefs values. + scoped_ptr<ListValue> expected_urls(new ListValue); + expected_urls->Append(Value::CreateStringValue(example_url1_)); + expected_urls->Append(Value::CreateStringValue(example_url2_)); + expected_urls->Append(Value::CreateStringValue(example_url0_)); + + value.reset(GetSyncedValue(prefs::kURLsToRestoreOnStartup)); + ASSERT_TRUE(value.get()); + EXPECT_TRUE(value->Equals(expected_urls.get())); + EXPECT_TRUE(GetPreferenceValue(prefs::kURLsToRestoreOnStartup). + Equals(expected_urls.get())); + + // The old preference name should also contain the merged sync data. + expected_urls.reset(new ListValue); + value.reset(GetSyncedValue(prefs::kURLsToRestoreOnStartupOld)); + ASSERT_TRUE(value.get()); + EXPECT_TRUE(GetPreferenceValue(prefs::kURLsToRestoreOnStartupOld). + Equals(expected_urls.get())); +} + +TEST_F(ProfileSyncServicePreferenceTest, + ModelAssociationCloudHasNewMigratedData) { + ASSERT_TRUE(PrefModelAssociator::IsMigratedPreference( + prefs::kURLsToRestoreOnStartup)); + ASSERT_TRUE(PrefModelAssociator::IsOldMigratedPreference( + prefs::kURLsToRestoreOnStartupOld)); + prefs_->SetString(prefs::kHomePage, example_url0_); + { + ListPrefUpdate update(prefs_, prefs::kURLsToRestoreOnStartupOld); + ListValue* url_list = update.Get(); + url_list->Append(Value::CreateStringValue(example_url0_)); + url_list->Append(Value::CreateStringValue(example_url1_)); + } + + PreferenceValues cloud_data; + STLValueDeleter<PreferenceValues> cloud_data_deleter(&cloud_data); + cloud_data[prefs::kHomePage] = Value::CreateStringValue(example_url1_); + ListValue* urls_to_restore = new ListValue; + urls_to_restore->Append(Value::CreateStringValue(example_url1_)); + urls_to_restore->Append(Value::CreateStringValue(example_url2_)); + cloud_data[prefs::kURLsToRestoreOnStartup] = urls_to_restore; + + AddPreferenceEntriesHelper helper(this, cloud_data); + ASSERT_TRUE(StartSyncService(helper.callback(), false)); + ASSERT_TRUE(helper.success()); + + scoped_ptr<const Value> value(GetSyncedValue(prefs::kHomePage)); + ASSERT_TRUE(value.get()); + std::string string_value; + EXPECT_TRUE(value->GetAsString(&string_value)); + EXPECT_EQ(example_url1_, string_value); + EXPECT_EQ(example_url1_, prefs_->GetString(prefs::kHomePage)); + + // Expect that the cloud data under the new migrated preference name sticks. + scoped_ptr<ListValue> expected_urls(new ListValue); + expected_urls->Append(Value::CreateStringValue(example_url1_)); + expected_urls->Append(Value::CreateStringValue(example_url2_)); + + value.reset(GetSyncedValue(prefs::kURLsToRestoreOnStartup)); + ASSERT_TRUE(value.get()); + EXPECT_TRUE(value->Equals(expected_urls.get())); + EXPECT_TRUE(GetPreferenceValue(prefs::kURLsToRestoreOnStartup). + Equals(expected_urls.get())); + + // The old preference data should still be here, though not synced. + expected_urls.reset(new ListValue); + expected_urls->Append(Value::CreateStringValue(example_url0_)); + expected_urls->Append(Value::CreateStringValue(example_url1_)); + + value.reset(GetSyncedValue(prefs::kURLsToRestoreOnStartupOld)); + ASSERT_FALSE(value.get()); + EXPECT_TRUE(GetPreferenceValue(prefs::kURLsToRestoreOnStartupOld). + Equals(expected_urls.get())); +} + +TEST_F(ProfileSyncServicePreferenceTest, + ModelAssociationCloudAddsOldAndNewMigratedData) { + ASSERT_TRUE(PrefModelAssociator::IsMigratedPreference( + prefs::kURLsToRestoreOnStartup)); + ASSERT_TRUE(PrefModelAssociator::IsOldMigratedPreference( + prefs::kURLsToRestoreOnStartupOld)); + prefs_->SetString(prefs::kHomePage, example_url0_); + { + ListPrefUpdate update_old(prefs_, prefs::kURLsToRestoreOnStartupOld); + ListValue* url_list_old = update_old.Get(); + url_list_old->Append(Value::CreateStringValue(example_url0_)); + url_list_old->Append(Value::CreateStringValue(example_url1_)); + ListPrefUpdate update(prefs_, prefs::kURLsToRestoreOnStartup); + ListValue* url_list = update.Get(); + url_list->Append(Value::CreateStringValue(example_url1_)); + url_list->Append(Value::CreateStringValue(example_url2_)); + } + + PreferenceValues cloud_data; + STLValueDeleter<PreferenceValues> cloud_data_deleter(&cloud_data); + cloud_data[prefs::kHomePage] = Value::CreateStringValue(example_url1_); + + AddPreferenceEntriesHelper helper(this, cloud_data); + ASSERT_TRUE(StartSyncService(helper.callback(), false)); + ASSERT_TRUE(helper.success()); + + scoped_ptr<const Value> value(GetSyncedValue(prefs::kHomePage)); + ASSERT_TRUE(value.get()); + std::string string_value; + EXPECT_TRUE(value->GetAsString(&string_value)); + EXPECT_EQ(example_url1_, string_value); + EXPECT_EQ(example_url1_, prefs_->GetString(prefs::kHomePage)); + + // Expect that the cloud data under the new migrated preference name sticks. + scoped_ptr<ListValue> expected_urls(new ListValue); + expected_urls->Append(Value::CreateStringValue(example_url1_)); + expected_urls->Append(Value::CreateStringValue(example_url2_)); + + value.reset(GetSyncedValue(prefs::kURLsToRestoreOnStartup)); + ASSERT_TRUE(value.get()); + EXPECT_TRUE(value->Equals(expected_urls.get())); + EXPECT_TRUE(GetPreferenceValue(prefs::kURLsToRestoreOnStartup). + Equals(expected_urls.get())); + + // Should not have synced in the old startup url values. + value.reset(GetSyncedValue(prefs::kURLsToRestoreOnStartupOld)); + ASSERT_FALSE(value.get()); + EXPECT_FALSE(GetPreferenceValue(prefs::kURLsToRestoreOnStartupOld). + Equals(expected_urls.get())); } TEST_F(ProfileSyncServicePreferenceTest, FailModelAssociation) { @@ -552,10 +712,11 @@ TEST_F(ProfileSyncServicePreferenceTest, ManagedListPreferences) { // Set a cloud version. PreferenceValues cloud_data; - scoped_ptr<ListValue> urls_to_restore(new ListValue); + STLValueDeleter<PreferenceValues> cloud_data_deleter(&cloud_data); + ListValue* urls_to_restore = new ListValue; urls_to_restore->Append(Value::CreateStringValue(example_url1_)); urls_to_restore->Append(Value::CreateStringValue(example_url2_)); - cloud_data[prefs::kURLsToRestoreOnStartup] = urls_to_restore.get(); + cloud_data[prefs::kURLsToRestoreOnStartup] = urls_to_restore; // Start sync and verify the synced value didn't get merged. AddPreferenceEntriesHelper helper(this, cloud_data); diff --git a/chrome/browser/sync/test/integration/single_client_managed_user_settings_sync_test.cc b/chrome/browser/sync/test/integration/single_client_managed_user_settings_sync_test.cc index 51b25fd7bc..55b2e08495 100644 --- a/chrome/browser/sync/test/integration/single_client_managed_user_settings_sync_test.cc +++ b/chrome/browser/sync/test/integration/single_client_managed_user_settings_sync_test.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/command_line.h" #include "base/prefs/pref_service.h" #include "base/values.h" #include "chrome/browser/managed_mode/managed_user_constants.h" @@ -12,12 +13,19 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/sync/profile_sync_service_harness.h" #include "chrome/browser/sync/test/integration/sync_test.h" +#include "chrome/common/chrome_switches.h" class SingleClientManagedUserSettingsSyncTest : public SyncTest { public: SingleClientManagedUserSettingsSyncTest() : SyncTest(SINGLE_CLIENT) {} virtual ~SingleClientManagedUserSettingsSyncTest() {} + + // SyncTest overrides: + virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { + SyncTest::SetUpCommandLine(command_line); + command_line->AppendSwitch(switches::kNewProfileIsSupervised); + } }; // TODO(pavely): Fix this test. See also: http://crbug.com/279307 @@ -26,7 +34,6 @@ IN_PROC_BROWSER_TEST_F(SingleClientManagedUserSettingsSyncTest, ASSERT_TRUE(SetupClients()); for (int i = 0; i < num_clients(); ++i) { Profile* profile = GetProfile(i); - ManagedUserServiceFactory::GetForProfile(profile)->InitForTesting(); // Managed users are prohibited from signing into the browser. Currently // that means they're also unable to sync anything, so override that for // this test. diff --git a/chrome/browser/sync/test/integration/sync_extension_helper.cc b/chrome/browser/sync/test/integration/sync_extension_helper.cc index 3e1c0d4cd1..75a69e60fb 100644 --- a/chrome/browser/sync/test/integration/sync_extension_helper.cc +++ b/chrome/browser/sync/test/integration/sync_extension_helper.cc @@ -10,6 +10,7 @@ #include "base/values.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/pending_extension_info.h" #include "chrome/browser/extensions/pending_extension_manager.h" #include "chrome/browser/profiles/profile.h" @@ -116,20 +117,20 @@ bool SyncExtensionHelper::IsExtensionEnabled( void SyncExtensionHelper::IncognitoEnableExtension( Profile* profile, const std::string& name) { - profile->GetExtensionService()->SetIsIncognitoEnabled( - extensions::id_util::GenerateId(name), true); + extension_util::SetIsIncognitoEnabled(extensions::id_util::GenerateId(name), + profile->GetExtensionService(), true); } void SyncExtensionHelper::IncognitoDisableExtension( Profile* profile, const std::string& name) { - profile->GetExtensionService()->SetIsIncognitoEnabled( - extensions::id_util::GenerateId(name), false); + extension_util::SetIsIncognitoEnabled(extensions::id_util::GenerateId(name), + profile->GetExtensionService(), false); } bool SyncExtensionHelper::IsIncognitoEnabled( Profile* profile, const std::string& name) const { - return profile->GetExtensionService()->IsIncognitoEnabled( - extensions::id_util::GenerateId(name)); + return extension_util::IsIncognitoEnabled( + extensions::id_util::GenerateId(name), profile->GetExtensionService()); } @@ -197,7 +198,7 @@ SyncExtensionHelper::ExtensionStateMap ExtensionState::ENABLED : ExtensionState::DISABLED; extension_state_map[id].incognito_enabled = - extension_service->IsIncognitoEnabled(id); + extension_util::IsIncognitoEnabled(id, extension_service); DVLOG(2) << "Extension " << (*it)->id() << " in profile " << profile_debug_name << " is " @@ -215,7 +216,7 @@ SyncExtensionHelper::ExtensionStateMap for (id = pending_crx_ids.begin(); id != pending_crx_ids.end(); ++id) { extension_state_map[*id].enabled_state = ExtensionState::PENDING; extension_state_map[*id].incognito_enabled = - extension_service->IsIncognitoEnabled(*id); + extension_util::IsIncognitoEnabled(*id, extension_service); DVLOG(2) << "Extension " << *id << " in profile " << profile_debug_name << " is pending"; } diff --git a/chrome/browser/sync_file_system/drive_backend/drive_backend_constants.cc b/chrome/browser/sync_file_system/drive_backend/drive_backend_constants.cc index c373fd944b..8b76a0c698 100644 --- a/chrome/browser/sync_file_system/drive_backend/drive_backend_constants.cc +++ b/chrome/browser/sync_file_system/drive_backend/drive_backend_constants.cc @@ -11,6 +11,12 @@ const char kSyncRootFolderTitle[] = "Chrome Syncable FileSystem"; const base::FilePath::CharType kDatabaseName[] = FILE_PATH_LITERAL("DriveMetadata"); +const char kDatabaseVersionKey[] = "VERSION"; +const int64 kCurrentDatabaseVersion = 3; +const char kServiceMetadataKey[] = "SERVICE"; +const char kFileMetadataKeyPrefix[] = "FILE: "; +const char kFileTrackerKeyPrefix[] = "TRACKER: "; + const int kMaxRetry = 5; } // namespace drive_backend diff --git a/chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h b/chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h index a2a85718e5..de9866d8c5 100644 --- a/chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h +++ b/chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h @@ -13,6 +13,12 @@ namespace drive_backend { extern const char kSyncRootFolderTitle[]; extern const base::FilePath::CharType kDatabaseName[]; +extern const char kDatabaseVersionKey[]; +extern const int64 kCurrentDatabaseVersion; +extern const char kServiceMetadataKey[]; +extern const char kFileMetadataKeyPrefix[]; +extern const char kFileTrackerKeyPrefix[]; + extern const int kMaxRetry; } // namespace drive_backend diff --git a/chrome/browser/sync_file_system/drive_backend/drive_backend_util.cc b/chrome/browser/sync_file_system/drive_backend/drive_backend_util.cc new file mode 100644 index 0000000000..b8086da16f --- /dev/null +++ b/chrome/browser/sync_file_system/drive_backend/drive_backend_util.cc @@ -0,0 +1,109 @@ +// Copyright 2013 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/sync_file_system/drive_backend/drive_backend_util.h" + +#include <string> + +#include "base/logging.h" +#include "base/memory/scoped_vector.h" +#include "base/strings/string_number_conversions.h" +#include "chrome/browser/drive/drive_api_util.h" +#include "chrome/browser/google_apis/drive_api_parser.h" +#include "chrome/browser/google_apis/gdata_wapi_parser.h" +#include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h" +#include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h" +#include "third_party/leveldatabase/src/include/leveldb/write_batch.h" + +namespace sync_file_system { +namespace drive_backend { + +void PutServiceMetadataToBatch(const ServiceMetadata& service_metadata, + leveldb::WriteBatch* batch) { + std::string value; + bool success = service_metadata.SerializeToString(&value); + DCHECK(success); + batch->Put(kServiceMetadataKey, value); +} + +void PutFileToBatch(const FileMetadata& file, leveldb::WriteBatch* batch) { + std::string value; + bool success = file.SerializeToString(&value); + DCHECK(success); + batch->Put(kFileMetadataKeyPrefix + file.file_id(), value); +} + +void PutTrackerToBatch(const FileTracker& tracker, leveldb::WriteBatch* batch) { + std::string value; + bool success = tracker.SerializeToString(&value); + DCHECK(success); + batch->Put(kFileTrackerKeyPrefix + base::Int64ToString(tracker.tracker_id()), + value); +} + +void PopulateFileDetailsByFileResource( + const google_apis::FileResource& file_resource, + FileDetails* details) { + details->clear_parent_folder_ids(); + for (ScopedVector<google_apis::ParentReference>::const_iterator itr = + file_resource.parents().begin(); + itr != file_resource.parents().end(); + ++itr) { + details->add_parent_folder_ids((*itr)->file_id()); + } + details->set_title(file_resource.title()); + + google_apis::DriveEntryKind kind = drive::util::GetKind(file_resource); + if (kind == google_apis::ENTRY_KIND_FILE) + details->set_file_kind(FILE_KIND_FILE); + else if (kind == google_apis::ENTRY_KIND_FOLDER) + details->set_file_kind(FILE_KIND_FOLDER); + else + details->set_file_kind(FILE_KIND_UNSUPPORTED); + + details->set_md5(file_resource.md5_checksum()); + details->set_etag(file_resource.etag()); + details->set_creation_time(file_resource.created_date().ToInternalValue()); + details->set_modification_time( + file_resource.modified_date().ToInternalValue()); + details->set_deleted(false); +} + +scoped_ptr<FileMetadata> CreateFileMetadataFromFileResource( + int64 change_id, + const google_apis::FileResource& resource) { + scoped_ptr<FileMetadata> file(new FileMetadata); + file->set_file_id(resource.file_id()); + + FileDetails* details = file->mutable_details(); + details->set_change_id(change_id); + + if (resource.labels().is_trashed()) { + details->set_deleted(true); + return file.Pass(); + } + + PopulateFileDetailsByFileResource(resource, details); + return file.Pass(); +} + +scoped_ptr<FileMetadata> CreateFileMetadataFromChangeResource( + const google_apis::ChangeResource& change) { + scoped_ptr<FileMetadata> file(new FileMetadata); + file->set_file_id(change.file_id()); + + FileDetails* details = file->mutable_details(); + details->set_change_id(change.change_id()); + + if (change.is_deleted()) { + details->set_deleted(true); + return file.Pass(); + } + + PopulateFileDetailsByFileResource(*change.file(), details); + return file.Pass(); +} + +} // namespace drive_backend +} // namespace sync_file_system diff --git a/chrome/browser/sync_file_system/drive_backend/drive_backend_util.h b/chrome/browser/sync_file_system/drive_backend/drive_backend_util.h new file mode 100644 index 0000000000..03ada0672e --- /dev/null +++ b/chrome/browser/sync_file_system/drive_backend/drive_backend_util.h @@ -0,0 +1,44 @@ +// Copyright 2013 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_SYNC_FILE_SYSTEM_DRIVE_BACKEND_DRIVE_BACKEND_UTIL_H_ +#define CHROME_BROWSER_SYNC_FILE_SYSTEM_DRIVE_BACKEND_DRIVE_BACKEND_UTIL_H_ + +#include "base/memory/scoped_ptr.h" + +namespace google_apis { +class ChangeResource; +class FileResource; +} + +namespace leveldb { +class WriteBatch; +} + +namespace sync_file_system { +namespace drive_backend { + +class FileDetails; +class FileMetadata; +class FileTracker; +class ServiceMetadata; + +void PutServiceMetadataToBatch(const ServiceMetadata& service_metadata, + leveldb::WriteBatch* batch); +void PutFileToBatch(const FileMetadata& file, leveldb::WriteBatch* batch); +void PutTrackerToBatch(const FileTracker& tracker, leveldb::WriteBatch* batch); + +void PopulateFileDetailsByFileResource( + const google_apis::FileResource& file_resource, + FileDetails* details); +scoped_ptr<FileMetadata> CreateFileMetadataFromFileResource( + int64 change_id, + const google_apis::FileResource& resource); +scoped_ptr<FileMetadata> CreateFileMetadataFromChangeResource( + const google_apis::ChangeResource& change); + +} // namespace drive_backend +} // namespace sync_file_system + +#endif // CHROME_BROWSER_SYNC_FILE_SYSTEM_DRIVE_BACKEND_DRIVE_BACKEND_UTIL_H_ diff --git a/chrome/browser/sync_file_system/drive_backend/metadata_database.cc b/chrome/browser/sync_file_system/drive_backend/metadata_database.cc index 0ba2872f87..964b651e82 100644 --- a/chrome/browser/sync_file_system/drive_backend/metadata_database.cc +++ b/chrome/browser/sync_file_system/drive_backend/metadata_database.cc @@ -23,6 +23,8 @@ #include "chrome/browser/drive/drive_api_util.h" #include "chrome/browser/google_apis/drive_api_parser.h" #include "chrome/browser/google_apis/drive_entry_kinds.h" +#include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h" +#include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h" #include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h" #include "chrome/browser/sync_file_system/drive_backend/metadata_db_migration_util.h" #include "chrome/browser/sync_file_system/logger.h" @@ -34,12 +36,6 @@ namespace sync_file_system { namespace drive_backend { -const char kDatabaseVersionKey[] = "VERSION"; -const int64 kCurrentDatabaseVersion = 3; -const char kServiceMetadataKey[] = "SERVICE"; -const char kFileMetadataKeyPrefix[] = "FILE: "; -const char kFileTrackerKeyPrefix[] = "TRACKER: "; - struct DatabaseContents { scoped_ptr<ServiceMetadata> service_metadata; ScopedVector<FileMetadata> file_metadata; @@ -86,69 +82,6 @@ base::FilePath ReverseConcatPathComponents( return base::FilePath(result).NormalizePathSeparators(); } -void PopulateFileDetailsByFileResource( - const google_apis::FileResource& file_resource, - FileDetails* details) { - details->clear_parent_folder_ids(); - for (ScopedVector<google_apis::ParentReference>::const_iterator itr = - file_resource.parents().begin(); - itr != file_resource.parents().end(); - ++itr) { - details->add_parent_folder_ids((*itr)->file_id()); - } - details->set_title(file_resource.title()); - - google_apis::DriveEntryKind kind = drive::util::GetKind(file_resource); - if (kind == google_apis::ENTRY_KIND_FILE) - details->set_file_kind(FILE_KIND_FILE); - else if (kind == google_apis::ENTRY_KIND_FOLDER) - details->set_file_kind(FILE_KIND_FOLDER); - else - details->set_file_kind(FILE_KIND_UNSUPPORTED); - - details->set_md5(file_resource.md5_checksum()); - details->set_etag(file_resource.etag()); - details->set_creation_time(file_resource.created_date().ToInternalValue()); - details->set_modification_time( - file_resource.modified_date().ToInternalValue()); - details->set_deleted(false); -} - -scoped_ptr<FileMetadata> CreateFileMetadataFromFileResource( - int64 change_id, - const google_apis::FileResource& resource) { - scoped_ptr<FileMetadata> file(new FileMetadata); - file->set_file_id(resource.file_id()); - - FileDetails* details = file->mutable_details(); - details->set_change_id(change_id); - - if (resource.labels().is_trashed()) { - details->set_deleted(true); - return file.Pass(); - } - - PopulateFileDetailsByFileResource(resource, details); - return file.Pass(); -} - -scoped_ptr<FileMetadata> CreateFileMetadataFromChangeResource( - const google_apis::ChangeResource& change) { - scoped_ptr<FileMetadata> file(new FileMetadata); - file->set_file_id(change.file_id()); - - FileDetails* details = file->mutable_details(); - details->set_change_id(change.change_id()); - - if (change.is_deleted()) { - details->set_deleted(true); - return file.Pass(); - } - - PopulateFileDetailsByFileResource(*change.file(), details); - return file.Pass(); -} - void CreateInitialSyncRootTracker( int64 tracker_id, const google_apis::FileResource& file_resource, @@ -207,29 +140,6 @@ void AdaptLevelDBStatusToSyncStatusCode(const SyncStatusCallback& callback, callback.Run(LevelDBStatusToSyncStatusCode(status)); } -void PutServiceMetadataToBatch(const ServiceMetadata& service_metadata, - leveldb::WriteBatch* batch) { - std::string value; - bool success = service_metadata.SerializeToString(&value); - DCHECK(success); - batch->Put(kServiceMetadataKey, value); -} - -void PutFileToBatch(const FileMetadata& file, leveldb::WriteBatch* batch) { - std::string value; - bool success = file.SerializeToString(&value); - DCHECK(success); - batch->Put(kFileMetadataKeyPrefix + file.file_id(), value); -} - -void PutTrackerToBatch(const FileTracker& tracker, leveldb::WriteBatch* batch) { - std::string value; - bool success = tracker.SerializeToString(&value); - DCHECK(success); - batch->Put(kFileTrackerKeyPrefix + base::Int64ToString(tracker.tracker_id()), - value); -} - void PutFileDeletionToBatch(const std::string& file_id, leveldb::WriteBatch* batch) { batch->Delete(kFileMetadataKeyPrefix + file_id); diff --git a/chrome/browser/sync_file_system/drive_backend/metadata_database.h b/chrome/browser/sync_file_system/drive_backend/metadata_database.h index b4f616b441..82b7e70e27 100644 --- a/chrome/browser/sync_file_system/drive_backend/metadata_database.h +++ b/chrome/browser/sync_file_system/drive_backend/metadata_database.h @@ -224,6 +224,7 @@ class MetadataDatabase { const SyncStatusCallback& callback); private: + friend class RegisterAppTaskTest; friend class SyncEngineInitializerTest; struct DirtyTrackerComparator { diff --git a/chrome/browser/sync_file_system/drive_backend/metadata_database_unittest.cc b/chrome/browser/sync_file_system/drive_backend/metadata_database_unittest.cc index 44cc7fdadd..429ad7cafe 100644 --- a/chrome/browser/sync_file_system/drive_backend/metadata_database_unittest.cc +++ b/chrome/browser/sync_file_system/drive_backend/metadata_database_unittest.cc @@ -139,7 +139,7 @@ void ExpectEquivalentSets(const Container& left, const Container& right) { class MetadataDatabaseTest : public testing::Test { public: MetadataDatabaseTest() - : next_change_id_(kInitialChangeID + 1), + : current_change_id_(kInitialChangeID), next_tracker_id_(kSyncRootTrackerID + 1), next_file_id_number_(1), next_md5_sequence_number_(1) {} @@ -392,7 +392,7 @@ class MetadataDatabaseTest : public testing::Test { FileMetadata* file) { FileDetails* details = file->mutable_details(); details->set_title(new_title); - details->set_change_id(next_change_id_++); + details->set_change_id(++current_change_id_); } void ApplyReorganizeChangeToMetadata(const std::string& new_parent, @@ -400,14 +400,14 @@ class MetadataDatabaseTest : public testing::Test { FileDetails* details = file->mutable_details(); details->clear_parent_folder_ids(); details->add_parent_folder_ids(new_parent); - details->set_change_id(next_change_id_++); + details->set_change_id(++current_change_id_); } void ApplyContentChangeToMetadata(FileMetadata* file) { FileDetails* details = file->mutable_details(); details->set_md5( "md5_value_" + base::Int64ToString(next_md5_sequence_number_++)); - details->set_change_id(next_change_id_++); + details->set_change_id(++current_change_id_); } void PushToChangeList(scoped_ptr<google_apis::ChangeResource> change, @@ -586,7 +586,7 @@ class MetadataDatabaseTest : public testing::Test { scoped_ptr<MetadataDatabase> metadata_database_; - int64 next_change_id_; + int64 current_change_id_; int64 next_tracker_id_; int64 next_file_id_number_; int64 next_md5_sequence_number_; diff --git a/chrome/browser/sync_file_system/drive_backend/register_app_task.cc b/chrome/browser/sync_file_system/drive_backend/register_app_task.cc index 3a7eec19ea..fc19be33eb 100644 --- a/chrome/browser/sync_file_system/drive_backend/register_app_task.cc +++ b/chrome/browser/sync_file_system/drive_backend/register_app_task.cc @@ -105,7 +105,7 @@ void RegisterAppTask::DidCreateAppRootFolder( DCHECK(entry); scoped_ptr<google_apis::FileResource> resource( drive::util::ConvertResourceEntryToFileResource(*entry)); - sync_context_->GetMetadataDatabase()->UpdateByFileResource( + metadata_database()->UpdateByFileResource( change_id, *resource, base::Bind(&RegisterAppTask::DidUpdateDatabase, diff --git a/chrome/browser/sync_file_system/drive_backend/register_app_task_unittest.cc b/chrome/browser/sync_file_system/drive_backend/register_app_task_unittest.cc new file mode 100644 index 0000000000..327e48fb53 --- /dev/null +++ b/chrome/browser/sync_file_system/drive_backend/register_app_task_unittest.cc @@ -0,0 +1,319 @@ +// Copyright 2013 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/sync_file_system/drive_backend/register_app_task.h" + +#include "base/files/scoped_temp_dir.h" +#include "base/format_macros.h" +#include "base/run_loop.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" +#include "chrome/browser/drive/fake_drive_service.h" +#include "chrome/browser/google_apis/gdata_wapi_parser.h" +#include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h" +#include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h" +#include "chrome/browser/sync_file_system/drive_backend/metadata_database.h" +#include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h" +#include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h" +#include "chrome/browser/sync_file_system/drive_backend_v1/fake_drive_service_helper.h" +#include "chrome/browser/sync_file_system/sync_file_system_test_util.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/leveldatabase/src/include/leveldb/db.h" +#include "third_party/leveldatabase/src/include/leveldb/write_batch.h" + +namespace sync_file_system { +namespace drive_backend { + +namespace { +const int64 kSyncRootTrackerID = 100; +} // namespace + +class RegisterAppTaskTest : public testing::Test, + public SyncEngineContext { + public: + RegisterAppTaskTest() + : next_file_id_(1000), + next_tracker_id_(10000) {} + virtual ~RegisterAppTaskTest() {} + + virtual void SetUp() OVERRIDE { + ASSERT_TRUE(database_dir_.CreateUniqueTempDir()); + + fake_drive_service_.reset(new drive::FakeDriveService); + ASSERT_TRUE(fake_drive_service_->LoadAccountMetadataForWapi( + "sync_file_system/account_metadata.json")); + ASSERT_TRUE(fake_drive_service_->LoadResourceListForWapi( + "gdata/empty_feed.json")); + + drive_uploader_.reset(new drive::DriveUploader( + fake_drive_service_.get(), base::MessageLoopProxy::current())); + + fake_drive_service_helper_.reset(new FakeDriveServiceHelper( + fake_drive_service_.get(), drive_uploader_.get())); + + ASSERT_EQ(google_apis::HTTP_CREATED, + fake_drive_service_helper_->AddOrphanedFolder( + kSyncRootFolderTitle, &sync_root_folder_id_)); + } + + virtual void TearDown() OVERRIDE { + metadata_database_.reset(); + base::RunLoop().RunUntilIdle(); + } + + virtual drive::DriveServiceInterface* GetDriveService() OVERRIDE { + return fake_drive_service_.get(); + } + + virtual MetadataDatabase* GetMetadataDatabase() OVERRIDE { + return metadata_database_.get(); + } + + protected: + scoped_ptr<leveldb::DB> OpenLevelDB() { + leveldb::DB* db = NULL; + leveldb::Options options; + options.create_if_missing = true; + leveldb::Status status = + leveldb::DB::Open(options, database_dir_.path().AsUTF8Unsafe(), &db); + EXPECT_TRUE(status.ok()); + return make_scoped_ptr<leveldb::DB>(db); + } + + void SetUpInitialData(leveldb::DB* db) { + ServiceMetadata service_metadata; + service_metadata.set_largest_change_id(100); + service_metadata.set_sync_root_tracker_id(kSyncRootTrackerID); + service_metadata.set_next_tracker_id(next_tracker_id_); + + FileDetails sync_root_details; + sync_root_details.set_title(kSyncRootFolderTitle); + sync_root_details.set_file_kind(FILE_KIND_FOLDER); + sync_root_details.set_change_id(1); + + FileMetadata sync_root_metadata; + sync_root_metadata.set_file_id(sync_root_folder_id_); + *sync_root_metadata.mutable_details() = sync_root_details; + + FileTracker sync_root_tracker; + sync_root_tracker.set_tracker_id(service_metadata.sync_root_tracker_id()); + sync_root_tracker.set_parent_tracker_id(0); + sync_root_tracker.set_file_id(sync_root_metadata.file_id()); + sync_root_tracker.set_tracker_kind(TRACKER_KIND_REGULAR); + *sync_root_tracker.mutable_synced_details() = sync_root_details; + sync_root_tracker.set_active(true); + + leveldb::WriteBatch batch; + batch.Put(kDatabaseVersionKey, + base::Int64ToString(kCurrentDatabaseVersion)); + PutServiceMetadataToBatch(service_metadata, &batch); + PutFileToBatch(sync_root_metadata, &batch); + PutTrackerToBatch(sync_root_tracker, &batch); + EXPECT_TRUE(db->Write(leveldb::WriteOptions(), &batch).ok()); + } + + void CreateMetadataDatabase(scoped_ptr<leveldb::DB> db) { + ASSERT_TRUE(db); + ASSERT_FALSE(metadata_database_); + ASSERT_EQ(SYNC_STATUS_OK, + MetadataDatabase::CreateForTesting( + db.Pass(), &metadata_database_)); + } + + SyncStatusCode RunRegisterAppTask(const std::string& app_id) { + RegisterAppTask task(this, app_id); + SyncStatusCode status = SYNC_STATUS_UNKNOWN; + task.Run(CreateResultReceiver(&status)); + base::RunLoop().RunUntilIdle(); + return status; + } + + void SetUpRegisteredAppRoot( + const std::string& app_id, + leveldb::DB* db) { + FileDetails details; + details.set_title(app_id); + details.set_file_kind(FILE_KIND_FOLDER); + details.add_parent_folder_ids(sync_root_folder_id_); + + FileMetadata metadata; + metadata.set_file_id(GenerateFileID()); + *metadata.mutable_details() = details; + + FileTracker tracker; + tracker.set_parent_tracker_id(kSyncRootTrackerID); + tracker.set_tracker_id(next_tracker_id_++); + tracker.set_file_id(metadata.file_id()); + tracker.set_tracker_kind(TRACKER_KIND_APP_ROOT); + tracker.set_app_id(app_id); + *tracker.mutable_synced_details() = details; + tracker.set_active(true); + + leveldb::WriteBatch batch; + PutFileToBatch(metadata, &batch); + PutTrackerToBatch(tracker, &batch); + EXPECT_TRUE(db->Write(leveldb::WriteOptions(), &batch).ok()); + } + + void SetUpUnregisteredAppRoot(const std::string& app_id, + leveldb::DB* db) { + FileDetails details; + details.set_title(app_id); + details.set_file_kind(FILE_KIND_FOLDER); + details.add_parent_folder_ids(sync_root_folder_id_); + + FileMetadata metadata; + metadata.set_file_id(GenerateFileID()); + *metadata.mutable_details() = details; + + FileTracker tracker; + tracker.set_parent_tracker_id(kSyncRootTrackerID); + tracker.set_tracker_id(next_tracker_id_++); + tracker.set_file_id(metadata.file_id()); + tracker.set_tracker_kind(TRACKER_KIND_REGULAR); + *tracker.mutable_synced_details() = details; + tracker.set_active(false); + + leveldb::WriteBatch batch; + PutFileToBatch(metadata, &batch); + PutTrackerToBatch(tracker, &batch); + EXPECT_TRUE(db->Write(leveldb::WriteOptions(), &batch).ok()); + } + + size_t CountRegisteredAppRoot() { + // TODO(tzik): Add function to MetadataDatabase to list trackers by parent. + typedef MetadataDatabase::TrackersByTitle TrackersByTitle; + const TrackersByTitle& trackers_by_title = + metadata_database_->trackers_by_parent_and_title_[kSyncRootTrackerID]; + + size_t count = 0; + for (TrackersByTitle::const_iterator itr = trackers_by_title.begin(); + itr != trackers_by_title.end(); ++itr) { + if (itr->second.has_active()) + ++count; + } + + return count; + } + + bool IsAppRegistered(const std::string& app_id) { + TrackerSet trackers; + if (!metadata_database_->FindTrackersByParentAndTitle( + kSyncRootTrackerID, app_id, &trackers)) + return false; + return trackers.has_active(); + } + + size_t CountRemoteFileInSyncRoot() { + ScopedVector<google_apis::ResourceEntry> files; + EXPECT_EQ(google_apis::HTTP_SUCCESS, + fake_drive_service_helper_->ListFilesInFolder( + sync_root_folder_id_, &files)); + return files.size(); + } + + bool HasRemoteAppRoot(const std::string& app_id) { + TrackerSet files; + if (!metadata_database_->FindTrackersByParentAndTitle( + kSyncRootTrackerID, app_id, &files) || + !files.has_active()) + return false; + + std::string app_root_folder_id = files.active_tracker()->file_id(); + scoped_ptr<google_apis::ResourceEntry> entry; + if (google_apis::HTTP_SUCCESS != + fake_drive_service_helper_->GetResourceEntry( + app_root_folder_id, &entry)) + return false; + + return !entry->deleted(); + } + + private: + std::string GenerateFileID() { + return base::StringPrintf("file_id_%" PRId64, next_file_id_++); + } + + std::string sync_root_folder_id_; + + int64 next_file_id_; + int64 next_tracker_id_; + + content::TestBrowserThreadBundle browser_threads_; + base::ScopedTempDir database_dir_; + + scoped_ptr<drive::FakeDriveService> fake_drive_service_; + scoped_ptr<drive::DriveUploader> drive_uploader_; + scoped_ptr<FakeDriveServiceHelper> fake_drive_service_helper_; + + scoped_ptr<MetadataDatabase> metadata_database_; + + DISALLOW_COPY_AND_ASSIGN(RegisterAppTaskTest); +}; + +TEST_F(RegisterAppTaskTest, AlreadyRegistered) { + scoped_ptr<leveldb::DB> db(OpenLevelDB()); + ASSERT_TRUE(db); + SetUpInitialData(db.get()); + + const std::string kAppID = "app_id"; + SetUpRegisteredAppRoot(kAppID, db.get()); + + CreateMetadataDatabase(db.Pass()); + EXPECT_EQ(SYNC_STATUS_OK, RunRegisterAppTask(kAppID)); + + EXPECT_EQ(1u, CountRegisteredAppRoot()); + EXPECT_TRUE(IsAppRegistered(kAppID)); +} + +TEST_F(RegisterAppTaskTest, CreateAppFolder) { + scoped_ptr<leveldb::DB> db(OpenLevelDB()); + ASSERT_TRUE(db); + SetUpInitialData(db.get()); + + const std::string kAppID = "app_id"; + CreateMetadataDatabase(db.Pass()); + RunRegisterAppTask(kAppID); + + EXPECT_EQ(1u, CountRegisteredAppRoot()); + EXPECT_TRUE(IsAppRegistered(kAppID)); + + EXPECT_EQ(1u, CountRemoteFileInSyncRoot()); + EXPECT_TRUE(HasRemoteAppRoot(kAppID)); +} + +TEST_F(RegisterAppTaskTest, RegisterExistingFolder) { + scoped_ptr<leveldb::DB> db(OpenLevelDB()); + ASSERT_TRUE(db); + SetUpInitialData(db.get()); + + const std::string kAppID = "app_id"; + SetUpUnregisteredAppRoot(kAppID, db.get()); + + CreateMetadataDatabase(db.Pass()); + RunRegisterAppTask(kAppID); + + EXPECT_EQ(1u, CountRegisteredAppRoot()); + EXPECT_TRUE(IsAppRegistered(kAppID)); +} + +TEST_F(RegisterAppTaskTest, RegisterExistingFolder_MultipleCandidate) { + scoped_ptr<leveldb::DB> db(OpenLevelDB()); + ASSERT_TRUE(db); + SetUpInitialData(db.get()); + + const std::string kAppID = "app_id"; + SetUpUnregisteredAppRoot(kAppID, db.get()); + SetUpUnregisteredAppRoot(kAppID, db.get()); + + CreateMetadataDatabase(db.Pass()); + RunRegisterAppTask(kAppID); + + EXPECT_EQ(1u, CountRegisteredAppRoot()); + EXPECT_TRUE(IsAppRegistered(kAppID)); +} + +} // namespace drive_backend +} // namespace sync_file_system diff --git a/chrome/browser/sync_file_system/drive_backend/sync_engine_initializer_unittest.cc b/chrome/browser/sync_file_system/drive_backend/sync_engine_initializer_unittest.cc index b4ba1403e1..4dd8a71ad6 100644 --- a/chrome/browser/sync_file_system/drive_backend/sync_engine_initializer_unittest.cc +++ b/chrome/browser/sync_file_system/drive_backend/sync_engine_initializer_unittest.cc @@ -6,7 +6,7 @@ #include "base/bind.h" #include "base/files/scoped_temp_dir.h" -#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" #include "chrome/browser/drive/drive_api_util.h" #include "chrome/browser/drive/fake_drive_service.h" #include "chrome/browser/google_apis/drive_api_parser.h" @@ -50,7 +50,7 @@ class SyncEngineInitializerTest : public testing::Test { virtual void TearDown() OVERRIDE { initializer_.reset(); metadata_database_.reset(); - base::MessageLoop::current()->RunUntilIdle(); + base::RunLoop().RunUntilIdle(); } base::FilePath database_path() { @@ -65,7 +65,7 @@ class SyncEngineInitializerTest : public testing::Test { SyncStatusCode status = SYNC_STATUS_UNKNOWN; initializer_->Run(CreateResultReceiver(&status)); - base::MessageLoop::current()->RunUntilIdle(); + base::RunLoop().RunUntilIdle(); metadata_database_ = initializer_->PassMetadataDatabase(); return status; @@ -86,7 +86,7 @@ class SyncEngineInitializerTest : public testing::Test { scoped_ptr<google_apis::ResourceEntry> entry; fake_drive_service_.GetResourceEntry(file->metadata.file_id(), CreateResultReceiver(&error, &entry)); - base::MessageLoop::current()->RunUntilIdle(); + base::RunLoop().RunUntilIdle(); if (entry) { file->resource = @@ -108,7 +108,7 @@ class SyncEngineInitializerTest : public testing::Test { base::MessageLoopProxy::current(), database_path(), CreateResultReceiver(&status, &database)); - base::MessageLoop::current()->RunUntilIdle(); + base::RunLoop().RunUntilIdle(); if (status != SYNC_STATUS_OK) return status; @@ -125,7 +125,7 @@ class SyncEngineInitializerTest : public testing::Test { sync_root, app_root_list, CreateResultReceiver(&status)); - base::MessageLoop::current()->RunUntilIdle(); + base::RunLoop().RunUntilIdle(); app_root_list.weak_clear(); @@ -140,7 +140,7 @@ class SyncEngineInitializerTest : public testing::Test { fake_drive_service_.AddNewDirectory( parent_folder_id, title, CreateResultReceiver(&error, &entry)); - base::MessageLoop::current()->RunUntilIdle(); + base::RunLoop().RunUntilIdle(); EXPECT_EQ(google_apis::HTTP_CREATED, error); if (!entry) @@ -158,7 +158,7 @@ class SyncEngineInitializerTest : public testing::Test { sync_root->parents()[i]->file_id(), sync_root->file_id(), CreateResultReceiver(&error)); - base::MessageLoop::current()->RunUntilIdle(); + base::RunLoop().RunUntilIdle(); EXPECT_EQ(google_apis::HTTP_SUCCESS, error); } @@ -191,7 +191,7 @@ class SyncEngineInitializerTest : public testing::Test { fake_drive_service_.GetResourceEntry( file_id, CreateResultReceiver(&error, &entry)); - base::MessageLoop::current()->RunUntilIdle(); + base::RunLoop().RunUntilIdle(); EXPECT_EQ(google_apis::HTTP_SUCCESS, error); return !entry->GetLinkByType(google_apis::Link::LINK_PARENT); } @@ -211,7 +211,7 @@ class SyncEngineInitializerTest : public testing::Test { fake_drive_service_.AddResourceToDirectory( new_parent_folder_id, file_id, CreateResultReceiver(&error)); - base::MessageLoop::current()->RunUntilIdle(); + base::RunLoop().RunUntilIdle(); return error; } diff --git a/chrome/browser/sync_file_system/drive_backend_v1/drive_file_sync_service_fake_unittest.cc b/chrome/browser/sync_file_system/drive_backend_v1/drive_file_sync_service_fake_unittest.cc index 6b7df0baf8..912bbb9478 100644 --- a/chrome/browser/sync_file_system/drive_backend_v1/drive_file_sync_service_fake_unittest.cc +++ b/chrome/browser/sync_file_system/drive_backend_v1/drive_file_sync_service_fake_unittest.cc @@ -66,8 +66,10 @@ using drive_backend::FakeDriveServiceHelper; namespace { +#if !defined(OS_ANDROID) const char kExtensionName1[] = "example1"; const char kExtensionName2[] = "example2"; +#endif void DidInitialize(bool* done, SyncStatusCode status, bool created) { EXPECT_FALSE(*done); diff --git a/chrome/browser/sync_file_system/drive_backend_v1/fake_drive_service_helper.cc b/chrome/browser/sync_file_system/drive_backend_v1/fake_drive_service_helper.cc index 93987ec950..7871f70bf4 100644 --- a/chrome/browser/sync_file_system/drive_backend_v1/fake_drive_service_helper.cc +++ b/chrome/browser/sync_file_system/drive_backend_v1/fake_drive_service_helper.cc @@ -7,8 +7,12 @@ #include "base/bind.h" #include "base/file_util.h" #include "base/message_loop/message_loop.h" +#include "base/run_loop.h" #include "base/threading/sequenced_worker_pool.h" +#include "chrome/browser/google_apis/drive_api_parser.h" +#include "chrome/browser/google_apis/gdata_wapi_parser.h" #include "chrome/browser/sync_file_system/drive_backend_v1/api_util.h" +#include "chrome/browser/sync_file_system/sync_file_system_test_util.h" #include "chrome/browser/sync_file_system/sync_status_code.h" #include "content/public/test/test_browser_thread.h" #include "testing/gtest/include/gtest/gtest.h" @@ -16,6 +20,7 @@ #define FPL(path) FILE_PATH_LITERAL(path) +using google_apis::AboutResource; using google_apis::GDataErrorCode; using google_apis::ResourceEntry; using google_apis::ResourceList; @@ -25,16 +30,6 @@ namespace drive_backend { namespace { -void ResourceEntryResultCallback(GDataErrorCode* error_out, - scoped_ptr<ResourceEntry>* entry_out, - GDataErrorCode error, - scoped_ptr<ResourceEntry> entry) { - ASSERT_TRUE(error_out); - ASSERT_TRUE(entry_out); - *error_out = error; - *entry_out = entry.Pass(); -} - void UploadResultCallback(GDataErrorCode* error_out, scoped_ptr<ResourceEntry>* entry_out, GDataErrorCode error, @@ -46,22 +41,6 @@ void UploadResultCallback(GDataErrorCode* error_out, *entry_out = entry.Pass(); } -void ResourceListResultCallback(GDataErrorCode* error_out, - scoped_ptr<ResourceList>* list_out, - GDataErrorCode error, - scoped_ptr<ResourceList> list) { - ASSERT_TRUE(error_out); - ASSERT_TRUE(list_out); - *error_out = error; - *list_out = list.Pass(); -} - -void GDataErrorResultCallback(GDataErrorCode* error_out, - GDataErrorCode error) { - ASSERT_TRUE(error_out); - *error_out = error; -} - void DownloadResultCallback(GDataErrorCode* error_out, GDataErrorCode error, const base::FilePath& local_file) { @@ -95,8 +74,8 @@ GDataErrorCode FakeDriveServiceHelper::AddOrphanedFolder( error = google_apis::GDATA_OTHER_ERROR; fake_drive_service_->RemoveResourceFromDirectory( root_folder_id, *folder_id, - base::Bind(&GDataErrorResultCallback, &error)); - FlushMessageLoop(); + CreateResultReceiver(&error)); + base::RunLoop().RunUntilIdle(); if (error != google_apis::HTTP_SUCCESS) return error; @@ -113,8 +92,8 @@ GDataErrorCode FakeDriveServiceHelper::AddFolder( scoped_ptr<ResourceEntry> folder; fake_drive_service_->AddNewDirectory( parent_folder_id, title, - base::Bind(&ResourceEntryResultCallback, &error, &folder)); - FlushMessageLoop(); + CreateResultReceiver(&error, &folder)); + base::RunLoop().RunUntilIdle(); if (error == google_apis::HTTP_CREATED) *folder_id = folder->resource_id(); @@ -136,7 +115,7 @@ GDataErrorCode FakeDriveServiceHelper::AddFile( "application/octet-stream", base::Bind(&UploadResultCallback, &error, &file), google_apis::ProgressCallback()); - FlushMessageLoop(); + base::RunLoop().RunUntilIdle(); if (error == google_apis::HTTP_SUCCESS) *file_id = file->resource_id(); @@ -155,7 +134,7 @@ GDataErrorCode FakeDriveServiceHelper::UpdateFile( std::string(), // etag base::Bind(&UploadResultCallback, &error, &file), google_apis::ProgressCallback()); - FlushMessageLoop(); + base::RunLoop().RunUntilIdle(); return error; } @@ -165,8 +144,8 @@ GDataErrorCode FakeDriveServiceHelper::RemoveResource( fake_drive_service_->DeleteResource( file_id, std::string(), // etag - base::Bind(&GDataErrorResultCallback, &error)); - FlushMessageLoop(); + CreateResultReceiver(&error)); + base::RunLoop().RunUntilIdle(); return error; } @@ -176,8 +155,8 @@ GDataErrorCode FakeDriveServiceHelper::GetSyncRootFolderID( scoped_ptr<ResourceList> resource_list; fake_drive_service_->SearchByTitle( APIUtil::GetSyncRootDirectoryName(), std::string(), - base::Bind(&ResourceListResultCallback, &error, &resource_list)); - FlushMessageLoop(); + CreateResultReceiver(&error, &resource_list)); + base::RunLoop().RunUntilIdle(); if (error != google_apis::HTTP_SUCCESS) return error; @@ -200,8 +179,8 @@ GDataErrorCode FakeDriveServiceHelper::ListFilesInFolder( scoped_ptr<ResourceList> list; fake_drive_service_->GetResourceListInDirectory( folder_id, - base::Bind(&ResourceListResultCallback, &error, &list)); - FlushMessageLoop(); + CreateResultReceiver(&error, &list)); + base::RunLoop().RunUntilIdle(); if (error != google_apis::HTTP_SUCCESS) return error; @@ -216,8 +195,8 @@ GDataErrorCode FakeDriveServiceHelper::SearchByTitle( scoped_ptr<ResourceList> list; fake_drive_service_->SearchByTitle( title, folder_id, - base::Bind(&ResourceListResultCallback, &error, &list)); - FlushMessageLoop(); + CreateResultReceiver(&error, &list)); + base::RunLoop().RunUntilIdle(); if (error != google_apis::HTTP_SUCCESS) return error; @@ -230,8 +209,8 @@ GDataErrorCode FakeDriveServiceHelper::GetResourceEntry( GDataErrorCode error = google_apis::GDATA_OTHER_ERROR; fake_drive_service_->GetResourceEntry( file_id, - base::Bind(&ResourceEntryResultCallback, &error, entry)); - FlushMessageLoop(); + CreateResultReceiver(&error, entry)); + base::RunLoop().RunUntilIdle(); return error; } @@ -253,7 +232,7 @@ GDataErrorCode FakeDriveServiceHelper::ReadFile( base::Bind(&DownloadResultCallback, &error), google_apis::GetContentCallback(), google_apis::ProgressCallback()); - FlushMessageLoop(); + base::RunLoop().RunUntilIdle(); if (error != google_apis::HTTP_SUCCESS) return error; @@ -261,6 +240,15 @@ GDataErrorCode FakeDriveServiceHelper::ReadFile( ? google_apis::HTTP_SUCCESS : google_apis::GDATA_FILE_ERROR; } +GDataErrorCode FakeDriveServiceHelper::GetAboutResource( + scoped_ptr<AboutResource>* about_resource) { + GDataErrorCode error = google_apis::GDATA_OTHER_ERROR; + fake_drive_service_->GetAboutResource( + CreateResultReceiver(&error, about_resource)); + base::RunLoop().RunUntilIdle(); + return error; +} + GDataErrorCode FakeDriveServiceHelper::CompleteListing( scoped_ptr<ResourceList> list, ScopedVector<ResourceEntry>* entries) { @@ -281,8 +269,8 @@ GDataErrorCode FakeDriveServiceHelper::CompleteListing( list.reset(); fake_drive_service_->GetRemainingFileList( next_feed, - base::Bind(&ResourceListResultCallback, &error, &list)); - FlushMessageLoop(); + CreateResultReceiver(&error, &list)); + base::RunLoop().RunUntilIdle(); if (error != google_apis::HTTP_SUCCESS) return error; } @@ -303,11 +291,5 @@ base::FilePath FakeDriveServiceHelper::WriteToTempFile( return temp_file; } -void FakeDriveServiceHelper::FlushMessageLoop() { - base::MessageLoop::current()->RunUntilIdle(); - content::BrowserThread::GetBlockingPool()->FlushForTesting(); - base::MessageLoop::current()->RunUntilIdle(); -} - } // namespace drive_backend } // namespace sync_file_system diff --git a/chrome/browser/sync_file_system/drive_backend_v1/fake_drive_service_helper.h b/chrome/browser/sync_file_system/drive_backend_v1/fake_drive_service_helper.h index 05cd927eca..7aa2af68b2 100644 --- a/chrome/browser/sync_file_system/drive_backend_v1/fake_drive_service_helper.h +++ b/chrome/browser/sync_file_system/drive_backend_v1/fake_drive_service_helper.h @@ -57,6 +57,8 @@ class FakeDriveServiceHelper { google_apis::GDataErrorCode ReadFile( const std::string& file_id, std::string* file_content); + google_apis::GDataErrorCode GetAboutResource( + scoped_ptr<google_apis::AboutResource>* about_resource); base::FilePath base_dir_path() { return base_dir_.path(); } @@ -68,7 +70,6 @@ class FakeDriveServiceHelper { void Initialize(); base::FilePath WriteToTempFile(const std::string& content); - void FlushMessageLoop(); base::ScopedTempDir base_dir_; base::FilePath temp_dir_; diff --git a/chrome/browser/sync_file_system/local/sync_file_system_backend.cc b/chrome/browser/sync_file_system/local/sync_file_system_backend.cc index 199c23398a..f85aaa8c3b 100644 --- a/chrome/browser/sync_file_system/local/sync_file_system_backend.cc +++ b/chrome/browser/sync_file_system/local/sync_file_system_backend.cc @@ -18,7 +18,6 @@ #include "webkit/browser/fileapi/file_stream_writer.h" #include "webkit/browser/fileapi/file_system_context.h" #include "webkit/browser/fileapi/file_system_operation.h" -#include "webkit/browser/fileapi/sandbox_quota_observer.h" #include "webkit/common/fileapi/file_system_util.h" using content::BrowserThread; @@ -106,14 +105,9 @@ void SyncFileSystemBackend::Initialize(fileapi::FileSystemContext* context) { context_ = context; fileapi::SandboxFileSystemBackendDelegate* delegate = GetDelegate(); - delegate->AddFileUpdateObserver( - fileapi::kFileSystemTypeSyncable, - delegate->quota_observer(), - delegate->file_task_runner()); - delegate->AddFileUpdateObserver( - fileapi::kFileSystemTypeSyncableForInternalSync, - delegate->quota_observer(), - delegate->file_task_runner()); + delegate->RegisterQuotaUpdateObserver(fileapi::kFileSystemTypeSyncable); + delegate->RegisterQuotaUpdateObserver( + fileapi::kFileSystemTypeSyncableForInternalSync); } void SyncFileSystemBackend::OpenFileSystem( diff --git a/chrome/browser/sync_file_system/local/syncable_file_system_unittest.cc b/chrome/browser/sync_file_system/local/syncable_file_system_unittest.cc index b921319ddf..a7bff60bf0 100644 --- a/chrome/browser/sync_file_system/local/syncable_file_system_unittest.cc +++ b/chrome/browser/sync_file_system/local/syncable_file_system_unittest.cc @@ -11,8 +11,8 @@ #include "content/public/test/sandbox_file_system_test_helper.h" #include "content/public/test/test_browser_thread_bundle.h" #include "testing/gtest/include/gtest/gtest.h" +#include "webkit/browser/fileapi/async_file_test_helper.h" #include "webkit/browser/fileapi/file_system_context.h" -#include "webkit/browser/fileapi/file_system_file_util.h" #include "webkit/browser/fileapi/file_system_operation_context.h" #include "webkit/browser/fileapi/isolated_context.h" #include "webkit/browser/quota/quota_manager.h" @@ -265,22 +265,12 @@ TEST_F(SyncableFileSystemTest, DisableDirectoryOperations) { const FileSystemURL kSrcDir = other_file_system_.CreateURLFromUTF8("/a"); const FileSystemURL kSrcChild = other_file_system_.CreateURLFromUTF8("/a/b"); - bool created = false; - scoped_ptr<FileSystemOperationContext> operation_context; - - operation_context.reset(other_file_system_.NewOperationContext()); - operation_context->set_allowed_bytes_growth(1024); EXPECT_EQ(base::PLATFORM_FILE_OK, - other_file_system_.file_util()->CreateDirectory( - operation_context.get(), - kSrcDir, false /* exclusive */, false /* recursive */)); - - operation_context.reset(other_file_system_.NewOperationContext()); - operation_context->set_allowed_bytes_growth(1024); + fileapi::AsyncFileTestHelper::CreateDirectory( + other_file_system_.file_system_context(), kSrcDir)); EXPECT_EQ(base::PLATFORM_FILE_OK, - other_file_system_.file_util()->EnsureFileExists( - operation_context.get(), kSrcChild, &created)); - EXPECT_TRUE(created); + fileapi::AsyncFileTestHelper::CreateFile( + other_file_system_.file_system_context(), kSrcChild)); // Now try copying the directory into the syncable file system, which should // fail if directory operation is disabled. (http://crbug.com/161442) diff --git a/chrome/browser/sync_file_system/sync_file_system_test_util.cc b/chrome/browser/sync_file_system/sync_file_system_test_util.cc index 407b36298a..0b31c31470 100644 --- a/chrome/browser/sync_file_system/sync_file_system_test_util.cc +++ b/chrome/browser/sync_file_system/sync_file_system_test_util.cc @@ -4,18 +4,10 @@ #include "chrome/browser/sync_file_system/sync_file_system_test_util.h" -#include "base/bind.h" -#include "base/message_loop/message_loop_proxy.h" #include "base/run_loop.h" -#include "base/single_thread_task_runner.h" -#include "base/threading/thread.h" #include "chrome/browser/google_apis/gdata_errorcode.h" -#include "chrome/browser/google_apis/gdata_wapi_parser.h" -#include "chrome/browser/sync_file_system/drive_backend/metadata_database.h" #include "chrome/browser/sync_file_system/sync_status_code.h" -#include "content/public/browser/browser_thread.h" #include "content/public/test/test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" using content::BrowserThread; @@ -45,32 +37,12 @@ void ReceiveResult1(bool* done, Arg* arg_out, Arg arg) { *arg_out = base::internal::CallbackForward(arg); } -template <typename Arg1, typename Arg2> -void ReceiveResult2(bool* done, - Arg1* arg1_out, - Arg2* arg2_out, - Arg1 arg1, - Arg2 arg2) { - EXPECT_FALSE(*done); - *done = true; - *arg1_out = base::internal::CallbackForward(arg1); - *arg2_out = base::internal::CallbackForward(arg2); -} - template <typename Arg> base::Callback<void(Arg)> CreateResultReceiver(Arg* arg_out) { return base::Bind(&ReceiveResult1<Arg>, base::Owned(new bool(false)), arg_out); } -template <typename Arg1, typename Arg2> -base::Callback<void(Arg1, Arg2)> CreateResultReceiver(Arg1* arg1_out, - Arg2* arg2_out) { - return base::Bind(&ReceiveResult2<Arg1, Arg2>, - base::Owned(new bool(false)), - arg1_out, arg2_out); -} - // Instantiate versions we know callers will need. template base::Callback<void(SyncStatusCode)> AssignAndQuitCallback(base::RunLoop*, SyncStatusCode*); @@ -81,11 +53,4 @@ INSTANTIATE_RECEIVER(SyncStatusCode); INSTANTIATE_RECEIVER(google_apis::GDataErrorCode); #undef INSTANTIATE_RECEIVER -#define INSTANTIATE_RECEIVER(type1, type2) \ - template base::Callback<void(type1, scoped_ptr<type2>)> \ - CreateResultReceiver(type1*, scoped_ptr<type2>*) -INSTANTIATE_RECEIVER(SyncStatusCode, drive_backend::MetadataDatabase); -INSTANTIATE_RECEIVER(google_apis::GDataErrorCode, google_apis::ResourceEntry); -#undef INSTANTIATE_RECEIVER - } // namespace sync_file_system diff --git a/chrome/browser/sync_file_system/sync_file_system_test_util.h b/chrome/browser/sync_file_system/sync_file_system_test_util.h index 66c3b8a6cf..572cc32539 100644 --- a/chrome/browser/sync_file_system/sync_file_system_test_util.h +++ b/chrome/browser/sync_file_system/sync_file_system_test_util.h @@ -5,7 +5,9 @@ #ifndef CHROME_BROWSER_SYNC_FILE_SYSTEM_SYNC_FILE_SYSTEM_TEST_UTIL_H_ #define CHROME_BROWSER_SYNC_FILE_SYSTEM_SYNC_FILE_SYSTEM_TEST_UTIL_H_ -#include "base/callback_forward.h" +#include "base/bind.h" +#include "base/callback.h" +#include "testing/gtest/include/gtest/gtest.h" namespace base { class RunLoop; @@ -13,6 +15,18 @@ class RunLoop; namespace sync_file_system { +template <typename Arg1, typename Arg2> +void ReceiveResult2(bool* done, + Arg1* arg1_out, + Arg2* arg2_out, + Arg1 arg1, + Arg2 arg2) { + EXPECT_FALSE(*done); + *done = true; + *arg1_out = base::internal::CallbackForward(arg1); + *arg2_out = base::internal::CallbackForward(arg2); +} + template <typename R> void AssignAndQuit(base::RunLoop* run_loop, R* result_out, R result); @@ -24,7 +38,11 @@ base::Callback<void(Arg)> CreateResultReceiver(Arg* arg_out); template <typename Arg1, typename Arg2> base::Callback<void(Arg1, Arg2)> CreateResultReceiver(Arg1* arg1_out, - Arg2* arg2_out); + Arg2* arg2_out) { + return base::Bind(&ReceiveResult2<Arg1, Arg2>, + base::Owned(new bool(false)), + arg1_out, arg2_out); +} } // namespace sync_file_system diff --git a/chrome/browser/tab_contents/background_contents.cc b/chrome/browser/tab_contents/background_contents.cc index 8d3dceca20..a6c9a58891 100644 --- a/chrome/browser/tab_contents/background_contents.cc +++ b/chrome/browser/tab_contents/background_contents.cc @@ -6,6 +6,7 @@ #include "chrome/browser/background/background_contents_service.h" #include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/extensions/extension_web_contents_observer.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/renderer_preferences_util.h" #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h" @@ -47,6 +48,8 @@ BackgroundContents::BackgroundContents( web_contents_.get(), extensions::VIEW_TYPE_BACKGROUND_CONTENTS); web_contents_->SetDelegate(this); content::WebContentsObserver::Observe(web_contents_.get()); + extensions::ExtensionWebContentsObserver::CreateForWebContents( + web_contents_.get()); // Close ourselves when the application is shutting down. registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, diff --git a/chrome/browser/tab_contents/navigation_metrics_recorder.cc b/chrome/browser/tab_contents/navigation_metrics_recorder.cc index e1f98e458d..f1475d1edf 100644 --- a/chrome/browser/tab_contents/navigation_metrics_recorder.cc +++ b/chrome/browser/tab_contents/navigation_metrics_recorder.cc @@ -7,6 +7,13 @@ #include "base/metrics/histogram.h" #include "content/public/browser/navigation_details.h" #include "content/public/browser/navigation_entry.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/render_widget_host_view.h" + +#if defined(OS_WIN) +#include "base/win/windows_version.h" +#include "chrome/browser/metro_utils/metro_chrome_win.h" +#endif DEFINE_WEB_CONTENTS_USER_DATA_KEY(NavigationMetricsRecorder); @@ -70,3 +77,20 @@ void NavigationMetricsRecorder::DidNavigateMainFrame( RecordMainFrameNavigation(details); } +void NavigationMetricsRecorder::DidStartLoading( + content::RenderViewHost* render_view_host) { +#if defined(OS_WIN) && defined(USE_ASH) + if (base::win::GetVersion() >= base::win::VERSION_WIN8) { + gfx::NativeView view = render_view_host->GetView()->GetNativeView(); + if (view) { + chrome::HostDesktopType desktop = + chrome::GetHostDesktopTypeForNativeView(view); + UMA_HISTOGRAM_ENUMERATION("Win8.PageLoad", + chrome::GetWin8Environment(desktop), + chrome::WIN_8_ENVIRONMENT_MAX); + } + } +#endif +} + + diff --git a/chrome/browser/tab_contents/navigation_metrics_recorder.h b/chrome/browser/tab_contents/navigation_metrics_recorder.h index 6c339bf539..ca3301e83b 100644 --- a/chrome/browser/tab_contents/navigation_metrics_recorder.h +++ b/chrome/browser/tab_contents/navigation_metrics_recorder.h @@ -23,6 +23,9 @@ class NavigationMetricsRecorder const content::LoadCommittedDetails& details, const content::FrameNavigateParams& params) OVERRIDE; + virtual void DidStartLoading( + content::RenderViewHost* render_view_host) OVERRIDE; + DISALLOW_COPY_AND_ASSIGN(NavigationMetricsRecorder); }; diff --git a/chrome/browser/task_manager/background_resource_provider.cc b/chrome/browser/task_manager/background_resource_provider.cc index 6bbf757da4..9ffaece5e7 100644 --- a/chrome/browser/task_manager/background_resource_provider.cc +++ b/chrome/browser/task_manager/background_resource_provider.cc @@ -196,6 +196,8 @@ void BackgroundContentsResourceProvider::StartUpdating() { content::NotificationService::AllBrowserContextsAndSources()); registrar_.Add(this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED, content::NotificationService::AllBrowserContextsAndSources()); + registrar_.Add(this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED, + content::NotificationService::AllBrowserContextsAndSources()); } void BackgroundContentsResourceProvider::StopUpdating() { @@ -212,6 +214,9 @@ void BackgroundContentsResourceProvider::StopUpdating() { registrar_.Remove( this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED, content::NotificationService::AllBrowserContextsAndSources()); + registrar_.Remove( + this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED, + content::NotificationService::AllBrowserContextsAndSources()); // Delete all the resources. STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end()); @@ -223,8 +228,7 @@ void BackgroundContentsResourceProvider::AddToTaskManager( BackgroundContents* background_contents, const string16& application_name) { BackgroundContentsResource* resource = - new BackgroundContentsResource(background_contents, - application_name); + new BackgroundContentsResource(background_contents, application_name); resources_[background_contents] = resource; task_manager_->AddResource(resource); } @@ -307,6 +311,20 @@ void BackgroundContentsResourceProvider::Observe( // (applications may now be considered "foreground" that weren't before). task_manager_->ModelChanged(); break; + case content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED: { + WebContents* web_contents = content::Source<WebContents>(source).ptr(); + for (Resources::iterator i = resources_.begin(); i != resources_.end(); + i++) { + if (i->first->web_contents() == web_contents) { + string16 application_name = i->second->application_name(); + BackgroundContents* contents = i->first; + Remove(contents); + Add(contents, application_name); + return; + } + } + break; + } default: NOTREACHED() << "Unexpected notification."; return; diff --git a/chrome/browser/task_manager/renderer_resource.cc b/chrome/browser/task_manager/renderer_resource.cc index cc66efb720..51d37653bc 100644 --- a/chrome/browser/task_manager/renderer_resource.cc +++ b/chrome/browser/task_manager/renderer_resource.cc @@ -15,8 +15,7 @@ namespace task_manager { RendererResource::RendererResource(base::ProcessHandle process, content::RenderViewHost* render_view_host) - : content::RenderViewHostObserver(render_view_host), - process_(process), + : process_(process), render_view_host_(render_view_host), pending_stats_update_(false), fps_(0.0f), @@ -125,11 +124,4 @@ bool RendererResource::SupportNetworkUsage() const { return true; } -void RendererResource::RenderViewHostDestroyed( - content::RenderViewHost* render_view_host) { - // We should never get here. We should get deleted first. - // Use this CHECK to help diagnose http://crbug.com/165138. - CHECK(false); -} - } // namespace task_manager diff --git a/chrome/browser/task_manager/renderer_resource.h b/chrome/browser/task_manager/renderer_resource.h index 3ca4699ea4..c588ff6c74 100644 --- a/chrome/browser/task_manager/renderer_resource.h +++ b/chrome/browser/task_manager/renderer_resource.h @@ -7,7 +7,6 @@ #include "base/basictypes.h" #include "chrome/browser/task_manager/resource_provider.h" -#include "content/public/browser/render_view_host_observer.h" namespace content { class RenderViewHost; @@ -17,8 +16,7 @@ namespace task_manager { // Base class for various types of render process resources that provides common // functionality like stats tracking. -class RendererResource : public Resource, - public content::RenderViewHostObserver { +class RendererResource : public Resource { public: RendererResource(base::ProcessHandle process, content::RenderViewHost* render_view_host); @@ -57,10 +55,6 @@ class RendererResource : public Resource, virtual void NotifyV8HeapStats(size_t v8_memory_allocated, size_t v8_memory_used) OVERRIDE; - // content::RenderViewHostObserver implementation. - virtual void RenderViewHostDestroyed( - content::RenderViewHost* render_view_host) OVERRIDE; - content::RenderViewHost* render_view_host() const { return render_view_host_; } diff --git a/chrome/browser/task_manager/task_manager_browsertest.cc b/chrome/browser/task_manager/task_manager_browsertest.cc index 12686b476f..0093f38288 100644 --- a/chrome/browser/task_manager/task_manager_browsertest.cc +++ b/chrome/browser/task_manager/task_manager_browsertest.cc @@ -179,7 +179,7 @@ IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, MAYBE_NoticePanelChanges) { "chrome-extension://behllobkkfkfnphdnhnkndlbkcpglgmj/french_sentence.html"); Panel* panel = PanelManager::GetInstance()->CreatePanel( web_app::GenerateApplicationNameFromExtensionId( - last_loaded_extension_id_), + last_loaded_extension_id()), browser()->profile(), url, gfx::Rect(300, 400), @@ -198,7 +198,7 @@ IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, MAYBE_NoticePanelChanges) { TaskManagerBrowserTestUtil::WaitForWebResourceChange(2); // Unload extension to avoid crash on Windows. - UnloadExtension(last_loaded_extension_id_); + UnloadExtension(last_loaded_extension_id()); TaskManagerBrowserTestUtil::WaitForWebResourceChange(1); } @@ -226,7 +226,7 @@ IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, MAYBE_KillPanelExtension) { "chrome-extension://behllobkkfkfnphdnhnkndlbkcpglgmj/french_sentence.html"); PanelManager::GetInstance()->CreatePanel( web_app::GenerateApplicationNameFromExtensionId( - last_loaded_extension_id_), + last_loaded_extension_id()), browser()->profile(), url, gfx::Rect(300, 400), @@ -276,7 +276,7 @@ IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NoticeExtensionTabs) { prefix, true)); // Unload extension to avoid crash on Windows. - UnloadExtension(last_loaded_extension_id_); + UnloadExtension(last_loaded_extension_id()); TaskManagerBrowserTestUtil::WaitForWebResourceChange(1); } @@ -287,7 +287,7 @@ IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NoticeAppTabs) { ExtensionService* service = extensions::ExtensionSystem::Get( browser()->profile())->extension_service(); const extensions::Extension* extension = - service->GetExtensionById(last_loaded_extension_id_, false); + service->GetExtensionById(last_loaded_extension_id(), false); // New Tab Page. TaskManagerBrowserTestUtil::WaitForWebResourceChange(1); @@ -309,7 +309,7 @@ IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NoticeAppTabs) { prefix, true)); // Unload extension to avoid crash on Windows. - UnloadExtension(last_loaded_extension_id_); + UnloadExtension(last_loaded_extension_id()); TaskManagerBrowserTestUtil::WaitForWebResourceChange(1); } @@ -363,7 +363,7 @@ IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NoticeHostedAppTabs) { app_prefix, true)); // Disable extension and reload page. - DisableExtension(last_loaded_extension_id_); + DisableExtension(last_loaded_extension_id()); ui_test_utils::NavigateToURL(browser(), url); // Force the TaskManager to query the title. diff --git a/chrome/browser/themes/theme_service.cc b/chrome/browser/themes/theme_service.cc index bf6b9ccdd1..238824bb69 100644 --- a/chrome/browser/themes/theme_service.cc +++ b/chrome/browser/themes/theme_service.cc @@ -14,8 +14,6 @@ #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" -#include "chrome/browser/managed_mode/managed_user_service.h" -#include "chrome/browser/managed_mode/managed_user_service_factory.h" #include "chrome/browser/managed_mode/managed_user_theme.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/themes/browser_theme_pack.h" @@ -98,9 +96,6 @@ void ThemeService::Init(Profile* profile) { DCHECK(CalledOnValidThread()); profile_ = profile; - ManagedUserServiceFactory::GetForProfile(profile)->AddInitCallback(base::Bind( - &ThemeService::OnManagedUserInitialized, weak_ptr_factory_.GetWeakPtr())); - LoadThemePrefs(); registrar_.Add(this, @@ -578,25 +573,6 @@ void ThemeService::SetManagedUserTheme() { SetCustomDefaultTheme(new ManagedUserTheme); } -void ThemeService::OnManagedUserInitialized() { - // Currently when creating a supervised user, the ThemeService is initialized - // before the boolean flag indicating the profile belongs to a supervised - // user gets set. In order to get the custom managed user theme, we get a - // callback when ManagedUserService is initialized, which happens some time - // after the boolean flag has been set in - // ProfileManager::InitProfileUserPrefs() and after the - // NOTIFICATION_EXTENSIONS_READY notification is sent. - if ((theme_supplier_.get() && - (theme_supplier_->get_theme_type() == CustomThemeSupplier::EXTENSION || - theme_supplier_->get_theme_type() == - CustomThemeSupplier::MANAGED_USER_THEME)) || - !IsManagedUser()) { - return; - } - - SetManagedUserTheme(); -} - void ThemeService::OnInfobarDisplayed() { number_of_infobars_++; } diff --git a/chrome/browser/themes/theme_service_unittest.cc b/chrome/browser/themes/theme_service_unittest.cc index 4b03205756..19b78f940a 100644 --- a/chrome/browser/themes/theme_service_unittest.cc +++ b/chrome/browser/themes/theme_service_unittest.cc @@ -26,10 +26,8 @@ namespace theme_service_internal { class ThemeServiceTest : public ExtensionServiceTestBase { public: - ThemeServiceTest() { - manager_.reset( - new TestingProfileManager(TestingBrowserProcess::GetGlobal())); - } + ThemeServiceTest() : is_managed_(false), + manager_(TestingBrowserProcess::GetGlobal()) {} virtual ~ThemeServiceTest() {} // Moves a minimal theme to |temp_dir_path| and unpacks it from that @@ -81,10 +79,12 @@ class ThemeServiceTest : public ExtensionServiceTestBase { virtual void SetUp() { ExtensionServiceTestBase::SetUp(); - InitializeEmptyExtensionService(); + ExtensionServiceTestBase::ExtensionServiceInitParams params = + CreateDefaultInitParams(); + params.profile_is_managed = is_managed_; + InitializeExtensionService(params); service_->Init(); - bool success = manager_->SetUp(); - ASSERT_TRUE(success); + ASSERT_TRUE(manager_.SetUp()); } const CustomThemeSupplier* get_theme_supplier(ThemeService* theme_service) { @@ -92,11 +92,14 @@ class ThemeServiceTest : public ExtensionServiceTestBase { } TestingProfileManager* manager() { - return manager_.get(); + return &manager_; } + protected: + bool is_managed_; + private: - scoped_ptr<TestingProfileManager> manager_; + TestingProfileManager manager_; }; // Installs then uninstalls a theme and makes sure that the ThemeService @@ -226,9 +229,19 @@ TEST_F(ThemeServiceTest, ThemeUpgrade) { ExtensionService::INCLUDE_DISABLED)); } +class ThemeServiceManagedUserTest : public ThemeServiceTest { + public: + ThemeServiceManagedUserTest() {} + virtual ~ThemeServiceManagedUserTest() {} + + virtual void SetUp() OVERRIDE { + is_managed_ = true; + ThemeServiceTest::SetUp(); + } +}; + // Checks that managed users have their own default theme. -TEST_F(ThemeServiceTest, ManagedUserThemeReplacesDefaultTheme) { - ManagedUserServiceFactory::GetForProfile(profile_.get())->InitForTesting(); +TEST_F(ThemeServiceManagedUserTest, ManagedUserThemeReplacesDefaultTheme) { ThemeService* theme_service = ThemeServiceFactory::GetForProfile(profile_.get()); theme_service->UseDefaultTheme(); @@ -238,23 +251,10 @@ TEST_F(ThemeServiceTest, ManagedUserThemeReplacesDefaultTheme) { CustomThemeSupplier::MANAGED_USER_THEME); } -TEST_F(ThemeServiceTest, ManagedUserThemeNewUser) { - TestingProfile* profile = manager()->CreateTestingProfile("mu"); - // Simulate the current initialization behavior: first the ThemeService is - // created, then the supervised user profile is initialized. - ThemeService* theme_service = - ThemeServiceFactory::GetForProfile(profile); - ManagedUserServiceFactory::GetForProfile(profile)->InitForTesting(); - EXPECT_EQ(get_theme_supplier(theme_service)->get_theme_type(), - CustomThemeSupplier::MANAGED_USER_THEME); - manager()->DeleteTestingProfile("mu"); -} - #if defined(OS_LINUX) && !defined(OS_CHROMEOS) // Checks that managed users don't use the system theme even if it is the // default. The system theme is only available on Linux. -TEST_F(ThemeServiceTest, ManagedUserThemeReplacesNativeTheme) { - ManagedUserServiceFactory::GetForProfile(profile_.get())->InitForTesting(); +TEST_F(ThemeServiceManagedUserTest, ManagedUserThemeReplacesNativeTheme) { profile_->GetPrefs()->SetBoolean(prefs::kUsesSystemTheme, true); ThemeService* theme_service = ThemeServiceFactory::GetForProfile(profile_.get()); diff --git a/chrome/browser/ui/android/infobars/infobar_android.cc b/chrome/browser/ui/android/infobars/infobar_android.cc index f52384e00f..2f08d849f3 100644 --- a/chrome/browser/ui/android/infobars/infobar_android.cc +++ b/chrome/browser/ui/android/infobars/infobar_android.cc @@ -67,12 +67,7 @@ void InfoBarAndroid::OnButtonClicked(JNIEnv* env, void InfoBarAndroid::OnCloseButtonClicked(JNIEnv* env, jobject obj) { delegate_->InfoBarDismissed(); -} - -void InfoBarAndroid::OnInfoBarClosed(JNIEnv* env, jobject obj) { - java_info_bar_.Reset(); // So we don't notify Java. - if (owner()) - RemoveSelf(); + RemoveSelf(); } void InfoBarAndroid::CloseJavaInfoBar() { diff --git a/chrome/browser/ui/android/infobars/infobar_android.h b/chrome/browser/ui/android/infobars/infobar_android.h index 0fe85fc1cc..654f480f38 100644 --- a/chrome/browser/ui/android/infobars/infobar_android.h +++ b/chrome/browser/ui/android/infobars/infobar_android.h @@ -52,8 +52,6 @@ class InfoBarAndroid : public InfoBar { jstring action_value); void OnCloseButtonClicked(JNIEnv* env, jobject obj); - void OnInfoBarClosed(JNIEnv* env, jobject obj); - void CloseJavaInfoBar(); // Maps from a Chromium ID (IDR_TRANSLATE) to a enum value that Java code can diff --git a/chrome/browser/ui/android/infobars/translate_infobar.cc b/chrome/browser/ui/android/infobars/translate_infobar.cc index 516da32f56..a8cd9596b9 100644 --- a/chrome/browser/ui/android/infobars/translate_infobar.cc +++ b/chrome/browser/ui/android/infobars/translate_infobar.cc @@ -59,8 +59,6 @@ void TranslateInfoBar::ProcessButton( return; // We're closing; don't call anything, it might access the owner. if (action == InfoBarAndroid::ACTION_TRANSLATE) { - // Do not close the infobar upon translate since it will be replaced by a - // different one which will close this current infobar. delegate_->Translate(); return; } diff --git a/chrome/browser/ui/android/tab_model/tab_model.h b/chrome/browser/ui/android/tab_model/tab_model.h index e8a5e6c8eb..129164afdb 100644 --- a/chrome/browser/ui/android/tab_model/tab_model.h +++ b/chrome/browser/ui/android/tab_model/tab_model.h @@ -47,6 +47,9 @@ class TabModel : public content::NotificationObserver, virtual content::WebContents* GetWebContentsAt(int index) const = 0; virtual TabAndroid* GetTabAt(int index) const = 0; + virtual void SetActiveIndex(int index) = 0; + virtual void CloseTabAt(int index) = 0; + // Used for restoring tabs from synced foreign sessions. virtual void CreateTab(content::WebContents* web_contents) = 0; diff --git a/chrome/browser/ui/android/tab_model/tab_model_unittest.cc b/chrome/browser/ui/android/tab_model/tab_model_unittest.cc index 3feaf0fc66..bcddf79731 100644 --- a/chrome/browser/ui/android/tab_model/tab_model_unittest.cc +++ b/chrome/browser/ui/android/tab_model/tab_model_unittest.cc @@ -43,7 +43,8 @@ class TestTabModel : public TabModel { virtual TabAndroid* GetTabAt(int index) const OVERRIDE { return NULL; } - + virtual void SetActiveIndex(int index) OVERRIDE {} + virtual void CloseTabAt(int index) OVERRIDE {} }; TEST_F(TabModelTest, TestProfileHandling) { diff --git a/chrome/browser/ui/app_list/app_context_menu.cc b/chrome/browser/ui/app_list/app_context_menu.cc index 8d9c9c2bbb..ac5ce9e368 100644 --- a/chrome/browser/ui/app_list/app_context_menu.cc +++ b/chrome/browser/ui/app_list/app_context_menu.cc @@ -66,7 +66,7 @@ class ExtensionUninstaller : public ExtensionUninstallDialog::Delegate { void Run() { const Extension* extension = extensions::ExtensionSystem::Get(profile_)->extension_service()-> - GetExtensionById(app_id_, true); + GetInstalledExtension(app_id_); if (!extension) { CleanUp(); return; @@ -189,7 +189,7 @@ ui::MenuModel* AppContextMenu::GetMenuModel() { IDS_APP_LIST_CONTEXT_MENU_PIN); } - if (controller_->CanDoCreateShortcutsFlow(is_platform_app_)) { + if (controller_->CanDoCreateShortcutsFlow()) { menu_model_->AddItemWithStringId(CREATE_SHORTCUTS, IDS_NEW_TAB_APP_CREATE_SHORTCUT); } diff --git a/chrome/browser/ui/app_list/app_list_controller_delegate.cc b/chrome/browser/ui/app_list/app_list_controller_delegate.cc index ec1332d184..9daf2f8c68 100644 --- a/chrome/browser/ui/app_list/app_list_controller_delegate.cc +++ b/chrome/browser/ui/app_list/app_list_controller_delegate.cc @@ -6,7 +6,10 @@ #include "base/logging.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/ui/browser_dialogs.h" #include "chrome/common/extensions/extension_constants.h" #include "ui/gfx/image/image_skia.h" @@ -26,9 +29,30 @@ void AppListControllerDelegate::PinApp(const std::string& extension_id) {} void AppListControllerDelegate::UnpinApp(const std::string& extension_id) {} +bool AppListControllerDelegate::CanDoCreateShortcutsFlow() { + return false; +} + void AppListControllerDelegate::DoCreateShortcutsFlow( Profile* profile, - const std::string& extension_id) {} + const std::string& extension_id) { + DCHECK(CanDoCreateShortcutsFlow()); + ExtensionService* service = + extensions::ExtensionSystem::Get(profile)->extension_service(); + DCHECK(service); + const extensions::Extension* extension = service->GetInstalledExtension( + extension_id); + DCHECK(extension); + + gfx::NativeWindow parent_window = GetAppListWindow(); + if (!parent_window) + return; + OnShowExtensionPrompt(); + chrome::ShowCreateChromeAppShortcutsDialog( + parent_window, profile, extension, + base::Bind(&AppListControllerDelegate::OnCloseExtensionPrompt, + base::Unretained(this))); +} void AppListControllerDelegate::CreateNewWindow(Profile* profile, bool incognito) { diff --git a/chrome/browser/ui/app_list/app_list_controller_delegate.h b/chrome/browser/ui/app_list/app_list_controller_delegate.h index 4b8ed1dc14..04d04a85b3 100644 --- a/chrome/browser/ui/app_list/app_list_controller_delegate.h +++ b/chrome/browser/ui/app_list/app_list_controller_delegate.h @@ -67,9 +67,11 @@ class AppListControllerDelegate { virtual void OnCloseExtensionPrompt() {} // Whether the controller supports a Create Shortcuts flow. - virtual bool CanDoCreateShortcutsFlow(bool is_platform_app) = 0; - virtual void DoCreateShortcutsFlow(Profile* profile, - const std::string& extension_id); + virtual bool CanDoCreateShortcutsFlow(); + + // Show the dialog to create shortcuts. Call only if + // CanDoCreateShortcutsFlow() returns true. + void DoCreateShortcutsFlow(Profile* profile, const std::string& extension_id); // Handle the "create window" context menu items of Chrome App. // |incognito| is true to create an incognito window. diff --git a/chrome/browser/ui/app_list/app_list_service_impl.cc b/chrome/browser/ui/app_list/app_list_service_impl.cc index 006138fd5a..90fc03b306 100644 --- a/chrome/browser/ui/app_list/app_list_service_impl.cc +++ b/chrome/browser/ui/app_list/app_list_service_impl.cc @@ -281,7 +281,7 @@ void AppListServiceImpl::HandleCommandLineFlags(Profile* initial_profile) { if (command_line_.HasSwitch(switches::kEnableAppList)) EnableAppList(initial_profile); - if (command_line_.HasSwitch(switches::kDisableAppList)) + if (command_line_.HasSwitch(switches::kResetAppListInstallState)) local_state_->SetBoolean(prefs::kAppLauncherHasBeenEnabled, false); } diff --git a/chrome/browser/ui/app_list/app_list_service_mac.mm b/chrome/browser/ui/app_list/app_list_service_mac.mm index 4dcaf16961..193a93d124 100644 --- a/chrome/browser/ui/app_list/app_list_service_mac.mm +++ b/chrome/browser/ui/app_list/app_list_service_mac.mm @@ -89,10 +89,7 @@ class AppListControllerDelegateCocoa : public AppListControllerDelegate { virtual void DismissView() OVERRIDE; virtual gfx::NativeWindow GetAppListWindow() OVERRIDE; virtual Pinnable GetPinnable() OVERRIDE; - virtual bool CanDoCreateShortcutsFlow(bool is_platform_app) OVERRIDE; virtual void CreateNewWindow(Profile* profile, bool incognito) OVERRIDE; - virtual void DoCreateShortcutsFlow(Profile* profile, - const std::string& extension_id) OVERRIDE; virtual void ActivateApp(Profile* profile, const extensions::Extension* extension, AppListSource source, @@ -216,24 +213,6 @@ AppListControllerDelegate::Pinnable return NO_PIN; } -bool AppListControllerDelegateCocoa::CanDoCreateShortcutsFlow( - bool is_platform_app) { - return false; -} - -void AppListControllerDelegateCocoa::DoCreateShortcutsFlow( - Profile* profile, const std::string& extension_id) { - ExtensionService* service = - extensions::ExtensionSystem::Get(profile)->extension_service(); - DCHECK(service); - const extensions::Extension* extension = - service->GetInstalledExtension(extension_id); - DCHECK(extension); - - web_app::UpdateShortcutInfoAndIconForApp( - *extension, profile, base::Bind(&CreateShortcutsInDefaultLocation)); -} - void AppListControllerDelegateCocoa::CreateNewWindow( Profile* profile, bool incognito) { Profile* window_profile = incognito ? @@ -454,7 +433,9 @@ void AppListServiceMac::CreateForProfile(Profile* requested_profile) { } scoped_ptr<app_list::AppListViewDelegate> delegate( - new AppListViewDelegate(new AppListControllerDelegateCocoa(), profile())); + new AppListViewDelegate( + scoped_ptr<AppListControllerDelegate>( + new AppListControllerDelegateCocoa()), profile())); [[window_controller_ appListViewController] setDelegate:delegate.Pass()]; } diff --git a/chrome/browser/ui/app_list/app_list_service_unittest.cc b/chrome/browser/ui/app_list/app_list_service_unittest.cc index 570b1d5712..9ad7aeec0c 100644 --- a/chrome/browser/ui/app_list/app_list_service_unittest.cc +++ b/chrome/browser/ui/app_list/app_list_service_unittest.cc @@ -168,7 +168,7 @@ TEST_F(AppListServiceUnitTest, EnableViaCommandLineFlag) { TEST_F(AppListServiceUnitTest, DisableViaCommandLineFlag) { CommandLine command_line(CommandLine::NO_PROGRAM); - command_line.AppendSwitch(switches::kDisableAppList); + command_line.AppendSwitch(switches::kResetAppListInstallState); SetupWithCommandLine(command_line); service_->HandleCommandLineFlags(profile1_.get()); EXPECT_FALSE(local_state_->GetBoolean(prefs::kAppLauncherHasBeenEnabled)); diff --git a/chrome/browser/ui/app_list/app_list_view_delegate.cc b/chrome/browser/ui/app_list/app_list_view_delegate.cc index 0a5202d0cf..89d54fe7b2 100644 --- a/chrome/browser/ui/app_list/app_list_view_delegate.cc +++ b/chrome/browser/ui/app_list/app_list_view_delegate.cc @@ -76,9 +76,10 @@ void PopulateUsers(const ProfileInfoCache& profile_info, } // namespace -AppListViewDelegate::AppListViewDelegate(AppListControllerDelegate* controller, - Profile* profile) - : controller_(controller), +AppListViewDelegate::AppListViewDelegate( + scoped_ptr<AppListControllerDelegate> controller, + Profile* profile) + : controller_(controller.Pass()), profile_(profile), model_(NULL) { CHECK(controller_); diff --git a/chrome/browser/ui/app_list/app_list_view_delegate.h b/chrome/browser/ui/app_list/app_list_view_delegate.h index d7ab3cf928..0a2584fff1 100644 --- a/chrome/browser/ui/app_list/app_list_view_delegate.h +++ b/chrome/browser/ui/app_list/app_list_view_delegate.h @@ -47,7 +47,8 @@ class AppListViewDelegate : public app_list::AppListViewDelegate, public ProfileInfoCacheObserver { public: // The delegate will take ownership of the controller. - AppListViewDelegate(AppListControllerDelegate* controller, Profile* profile); + AppListViewDelegate(scoped_ptr<AppListControllerDelegate> controller, + Profile* profile); virtual ~AppListViewDelegate(); private: diff --git a/chrome/browser/ui/app_list/extension_app_item.cc b/chrome/browser/ui/app_list/extension_app_item.cc index 07b0c60708..6d1f9aa9e6 100644 --- a/chrome/browser/ui/app_list/extension_app_item.cc +++ b/chrome/browser/ui/app_list/extension_app_item.cc @@ -144,6 +144,35 @@ void ExtensionAppItem::UpdateIcon() { SetIcon(icon, !HasOverlay()); } +void ExtensionAppItem::Move(const ExtensionAppItem* prev, + const ExtensionAppItem* next) { + if (!prev && !next) + return; // No reordering necessary + + ExtensionService* service = + extensions::ExtensionSystem::Get(profile_)->extension_service(); + ExtensionSorting* sorting = service->extension_prefs()->extension_sorting(); + + syncer::StringOrdinal page; + std::string prev_id, next_id; + if (!prev) { + next_id = next->extension_id(); + page = sorting->GetPageOrdinal(next_id); + } else if (!next) { + prev_id = prev->extension_id(); + page = sorting->GetPageOrdinal(prev_id); + } else { + prev_id = prev->extension_id(); + page = sorting->GetPageOrdinal(prev_id); + // Only set |next_id| if on the same page, otherwise just insert after prev. + if (page.Equals(sorting->GetPageOrdinal(next->extension_id()))) + next_id = next->extension_id(); + } + service->extension_prefs()->SetAppDraggedByUser(extension_id_); + sorting->SetPageOrdinal(extension_id_, page); + service->OnExtensionMoved(extension_id_, prev_id, next_id); +} + const Extension* ExtensionAppItem::GetExtension() const { const ExtensionService* service = extensions::ExtensionSystem::Get(profile_)->extension_service(); diff --git a/chrome/browser/ui/app_list/extension_app_item.h b/chrome/browser/ui/app_list/extension_app_item.h index 3d4224f1a1..eb56f42b32 100644 --- a/chrome/browser/ui/app_list/extension_app_item.h +++ b/chrome/browser/ui/app_list/extension_app_item.h @@ -48,6 +48,11 @@ class ExtensionAppItem : public app_list::AppListItemModel, // it gray. void UpdateIcon(); + // Update page and app launcher ordinals to put the app in between |prev| and + // |next|. Note that |prev| and |next| could be NULL when the app is put at + // the beginning or at the end. + void Move(const ExtensionAppItem* prev, const ExtensionAppItem* next); + const std::string& extension_id() const { return extension_id_; } static const char kAppType[]; diff --git a/chrome/browser/ui/app_list/extension_app_model_builder.cc b/chrome/browser/ui/app_list/extension_app_model_builder.cc index 767bb7b7f2..1f17f6d6d6 100644 --- a/chrome/browser/ui/app_list/extension_app_model_builder.cc +++ b/chrome/browser/ui/app_list/extension_app_model_builder.cc @@ -48,11 +48,13 @@ ExtensionAppModelBuilder::ExtensionAppModelBuilder( model_(model), highlighted_app_pending_(false), tracker_(NULL) { + model_->apps()->AddObserver(this); SwitchProfile(profile); // Builds the model. } ExtensionAppModelBuilder::~ExtensionAppModelBuilder() { OnShutdown(); + model_->apps()->RemoveObserver(this); } void ExtensionAppModelBuilder::OnBeginExtensionInstall( @@ -239,3 +241,40 @@ void ExtensionAppModelBuilder::UpdateHighlight() { item->SetHighlighted(true); highlighted_app_pending_ = false; } + +void ExtensionAppModelBuilder::ListItemsAdded(size_t start, size_t count) { +} + +void ExtensionAppModelBuilder::ListItemsRemoved(size_t start, size_t count) { +} + +void ExtensionAppModelBuilder::ListItemMoved(size_t index, + size_t target_index) { + app_list::AppListModel::Apps* app_list = model_->apps(); + app_list::AppListItemModel* item = app_list->GetItemAt(target_index); + if (item->GetAppType() != ExtensionAppItem::kAppType) + return; + + ExtensionAppItem* prev = NULL; + for (size_t idx = target_index; idx > 1; --idx) { + app_list::AppListItemModel* item = app_list->GetItemAt(idx - 1); + if (item->GetAppType() == ExtensionAppItem::kAppType) { + prev = static_cast<ExtensionAppItem*>(item); + break; + } + } + ExtensionAppItem* next = NULL; + for (size_t idx = target_index; idx < app_list->item_count() - 1; ++idx) { + app_list::AppListItemModel* item = app_list->GetItemAt(idx + 1); + if (item->GetAppType() == ExtensionAppItem::kAppType) { + next = static_cast<ExtensionAppItem*>(item); + break; + } + } + if (prev || next) + static_cast<ExtensionAppItem*>(item)->Move(prev, next); +} + +void ExtensionAppModelBuilder::ListItemsChanged(size_t start, size_t count) { + NOTREACHED(); +} diff --git a/chrome/browser/ui/app_list/extension_app_model_builder.h b/chrome/browser/ui/app_list/extension_app_model_builder.h index 9278d05299..50cfb9e414 100644 --- a/chrome/browser/ui/app_list/extension_app_model_builder.h +++ b/chrome/browser/ui/app_list/extension_app_model_builder.h @@ -29,7 +29,8 @@ class ImageSkia; // This class populates and maintains the given |model| with information from // |profile|. -class ExtensionAppModelBuilder : public extensions::InstallObserver { +class ExtensionAppModelBuilder : public extensions::InstallObserver, + public ui::ListModelObserver { public: ExtensionAppModelBuilder(Profile* profile, app_list::AppListModel* model, @@ -42,16 +43,14 @@ class ExtensionAppModelBuilder : public extensions::InstallObserver { private: typedef std::vector<ExtensionAppItem*> ExtensionAppList; - // Overridden from extensions::InstallObserver: + // extensions::InstallObserver virtual void OnBeginExtensionInstall(const std::string& extension_id, const std::string& extension_name, const gfx::ImageSkia& installing_icon, bool is_app, bool is_platform_app) OVERRIDE; - virtual void OnDownloadProgress(const std::string& extension_id, int percent_downloaded) OVERRIDE; - virtual void OnInstallFailure(const std::string& extension_id) OVERRIDE; virtual void OnExtensionInstalled( const extensions::Extension* extension) OVERRIDE {} @@ -66,6 +65,12 @@ class ExtensionAppModelBuilder : public extensions::InstallObserver { const std::string& extension_id) OVERRIDE; virtual void OnShutdown() OVERRIDE; + // ui::ListModelObserver + virtual void ListItemsAdded(size_t start, size_t count) OVERRIDE; + virtual void ListItemsRemoved(size_t start, size_t count) OVERRIDE; + virtual void ListItemMoved(size_t index, size_t target_index) OVERRIDE; + virtual void ListItemsChanged(size_t start, size_t count) OVERRIDE; + // Adds apps in |extensions| to |apps|. void AddApps(const ExtensionSet* extensions, ExtensionAppList* apps); diff --git a/chrome/browser/ui/app_list/search/app_search_provider.cc b/chrome/browser/ui/app_list/search/app_search_provider.cc index ceba03f9c4..c7fc5f0ddf 100644 --- a/chrome/browser/ui/app_list/search/app_search_provider.cc +++ b/chrome/browser/ui/app_list/search/app_search_provider.cc @@ -10,6 +10,7 @@ #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system_factory.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/app_list/search/app_result.h" #include "chrome/browser/ui/app_list/search/tokenized_string.h" @@ -82,7 +83,7 @@ void AppSearchProvider::AddApps(const ExtensionSet* extensions, continue; if (profile_->IsOffTheRecord() && - !service->CanLoadInIncognito(app)) + !extension_util::CanLoadInIncognito(app, service)) continue; apps_.push_back(new App(app)); } diff --git a/chrome/browser/ui/app_list/search/app_search_provider_unittest.cc b/chrome/browser/ui/app_list/search/app_search_provider_unittest.cc index fe8f22c154..ab44c2d9fc 100644 --- a/chrome/browser/ui/app_list/search/app_search_provider_unittest.cc +++ b/chrome/browser/ui/app_list/search/app_search_provider_unittest.cc @@ -19,7 +19,6 @@ namespace test { const char kHostedAppId[] = "dceacbkfkmllgmjmbhgkpjegnodmildf"; const char kPackagedApp1Id[] = "emfkafnhnpcmabnnkckkchdilgeoekbo"; -const char kPackagedApp2Id[] = "jlklkagmeajbjiobondfhiekepofmljl"; class AppSearchProviderTest : public ExtensionServiceTestBase { public: diff --git a/chrome/browser/ui/app_list/search/common/dictionary_data_store.cc b/chrome/browser/ui/app_list/search/common/dictionary_data_store.cc new file mode 100644 index 0000000000..fd50e8d51d --- /dev/null +++ b/chrome/browser/ui/app_list/search/common/dictionary_data_store.cc @@ -0,0 +1,92 @@ +// Copyright 2013 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/ui/app_list/search/common/dictionary_data_store.h" + +#include "base/callback.h" +#include "base/json/json_file_value_serializer.h" +#include "base/json/json_string_value_serializer.h" +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "base/task_runner_util.h" +#include "base/threading/sequenced_worker_pool.h" +#include "base/values.h" +#include "content/public/browser/browser_thread.h" + +using content::BrowserThread; + +namespace app_list { + +DictionaryDataStore::DictionaryDataStore(const base::FilePath& data_file) + : data_file_(data_file) { + std::string token("app-launcher-data-store"); + token.append(data_file.AsUTF8Unsafe()); + + // Uses a SKIP_ON_SHUTDOWN file task runner because losing a couple + // associations is better than blocking shutdown. + base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool(); + file_task_runner_ = pool->GetSequencedTaskRunnerWithShutdownBehavior( + pool->GetNamedSequenceToken(token), + base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); + writer_.reset( + new base::ImportantFileWriter(data_file, file_task_runner_.get())); + + cached_dict_.reset(new base::DictionaryValue); +} + +DictionaryDataStore::~DictionaryDataStore() { + Flush(OnFlushedCallback()); +} + +void DictionaryDataStore::Flush(const OnFlushedCallback& on_flushed) { + if (writer_->HasPendingWrite()) + writer_->DoScheduledWrite(); + + if (on_flushed.is_null()) + return; + + file_task_runner_->PostTaskAndReply( + FROM_HERE, base::Bind(&base::DoNothing), on_flushed); +} + +void DictionaryDataStore::Load( + const DictionaryDataStore::OnLoadedCallback& on_loaded) { + base::PostTaskAndReplyWithResult( + file_task_runner_.get(), + FROM_HERE, + base::Bind(&DictionaryDataStore::LoadOnBlockingPool, this), + on_loaded); +} + +void DictionaryDataStore::ScheduleWrite() { + writer_->ScheduleWrite(this); +} + +scoped_ptr<base::DictionaryValue> DictionaryDataStore::LoadOnBlockingPool() { + DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); + + int error_code = JSONFileValueSerializer::JSON_NO_ERROR; + std::string error_message; + JSONFileValueSerializer serializer(data_file_); + base::Value* value = serializer.Deserialize(&error_code, &error_message); + base::DictionaryValue* dict_value = NULL; + if (error_code != JSONFileValueSerializer::JSON_NO_ERROR || + !value || + !value->GetAsDictionary(&dict_value) || + !dict_value) { + return scoped_ptr<base::DictionaryValue>(); + } + + base::DictionaryValue* return_dict = dict_value->DeepCopy(); + cached_dict_.reset(dict_value); + return make_scoped_ptr(return_dict).Pass(); +} + +bool DictionaryDataStore::SerializeData(std::string* data) { + JSONStringValueSerializer serializer(data); + serializer.set_pretty_print(true); + return serializer.Serialize(*cached_dict_.get()); +} + +} // namespace app_list diff --git a/chrome/browser/ui/app_list/search/common/dictionary_data_store.h b/chrome/browser/ui/app_list/search/common/dictionary_data_store.h new file mode 100644 index 0000000000..dac2f61fcb --- /dev/null +++ b/chrome/browser/ui/app_list/search/common/dictionary_data_store.h @@ -0,0 +1,74 @@ +// Copyright 2013 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_UI_APP_LIST_SEARCH_COMMON_DICTIONARY_DATA_STORE_H_ +#define CHROME_BROWSER_UI_APP_LIST_SEARCH_COMMON_DICTIONARY_DATA_STORE_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/callback_forward.h" +#include "base/files/file_path.h" +#include "base/files/important_file_writer.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" + +namespace base { +class DictionaryValue; +class SequencedTaskRunner; +} + +namespace app_list { + +// A simple JSON store to persist a dictionary. +class DictionaryDataStore + : public base::RefCountedThreadSafe<DictionaryDataStore>, + public base::ImportantFileWriter::DataSerializer { + public: + typedef base::Callback<void( + scoped_ptr<base::DictionaryValue>)> OnLoadedCallback; + typedef base::Closure OnFlushedCallback; + + explicit DictionaryDataStore(const base::FilePath& data_file); + + // Flushes pending writes. + void Flush(const OnFlushedCallback& on_flushed); + + // Reads the persisted data from disk asynchronously. |on_read| is called + // with the loaded and parsed data. If there is an error, |on_read| is called + // without data. + void Load(const OnLoadedCallback& on_loaded); + + // Schedule a job to persist the cached dictionary. + void ScheduleWrite(); + + // Used to get a pointer to the cached dictionary. Changes to this dictionary + // will not be persisted unless ScheduleWrite() is called. + base::DictionaryValue* cached_dict() { return cached_dict_.get(); } + + private: + friend class base::RefCountedThreadSafe<DictionaryDataStore>; + + virtual ~DictionaryDataStore(); + + // Reads data from backing file. + scoped_ptr<base::DictionaryValue> LoadOnBlockingPool(); + + // ImportantFileWriter::DataSerializer overrides: + virtual bool SerializeData(std::string* data) OVERRIDE; + + base::FilePath data_file_; + scoped_refptr<base::SequencedTaskRunner> file_task_runner_; + scoped_ptr<base::ImportantFileWriter> writer_; + + // Cached JSON dictionary to serve read and incremental change calls. + scoped_ptr<base::DictionaryValue> cached_dict_; + + DISALLOW_COPY_AND_ASSIGN(DictionaryDataStore); +}; + +} // namespace app_list + +#endif // CHROME_BROWSER_UI_APP_LIST_SEARCH_COMMON_DICTIONARY_DATA_STORE_H_ diff --git a/chrome/browser/ui/app_list/search/history_data_store.cc b/chrome/browser/ui/app_list/search/history_data_store.cc index b52598fba9..c8ea497e91 100644 --- a/chrome/browser/ui/app_list/search/history_data_store.cc +++ b/chrome/browser/ui/app_list/search/history_data_store.cc @@ -60,15 +60,17 @@ void GetSecondary(const base::ListValue* list, // ... // } // } -scoped_ptr<HistoryData::Associations> Parse(const base::DictionaryValue& dict) { +scoped_ptr<HistoryData::Associations> Parse( + scoped_ptr<base::DictionaryValue> dict) { std::string version; - if (!dict.GetStringWithoutPathExpansion(kKeyVersion, &version) || + if (!dict->GetStringWithoutPathExpansion(kKeyVersion, &version) || version != kCurrentVersion) { return scoped_ptr<HistoryData::Associations>(); } const base::DictionaryValue* assoc_dict = NULL; - if (!dict.GetDictionaryWithoutPathExpansion(kKeyAssociations, &assoc_dict) || + if (!dict->GetDictionaryWithoutPathExpansion(kKeyAssociations, + &assoc_dict) || !assoc_dict) { return scoped_ptr<HistoryData::Associations>(); } @@ -107,52 +109,29 @@ scoped_ptr<HistoryData::Associations> Parse(const base::DictionaryValue& dict) { return data.Pass(); } -// An empty callback used to ensure file tasks are cleared. -void EmptyCallback() {} - } // namespace HistoryDataStore::HistoryDataStore(const base::FilePath& data_file) - : data_file_(data_file) { - std::string token("app-launcher-history-data-store"); - token.append(data_file.AsUTF8Unsafe()); - - // Uses a SKIP_ON_SHUTDOWN file task runner because losing a couple - // associations is better than blocking shutdown. - base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool(); - file_task_runner_ = pool->GetSequencedTaskRunnerWithShutdownBehavior( - pool->GetNamedSequenceToken(token), - base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); - writer_.reset( - new base::ImportantFileWriter(data_file, file_task_runner_.get())); - - cached_json_.reset(new base::DictionaryValue); - cached_json_->SetString(kKeyVersion, kCurrentVersion); - cached_json_->Set(kKeyAssociations, new base::DictionaryValue); + : data_store_(new DictionaryDataStore(data_file)) { + base::DictionaryValue* dict = data_store_->cached_dict(); + DCHECK(dict); + dict->SetString(kKeyVersion, kCurrentVersion); + dict->Set(kKeyAssociations, new base::DictionaryValue); } HistoryDataStore::~HistoryDataStore() { - Flush(OnFlushedCallback()); } -void HistoryDataStore::Flush(const OnFlushedCallback& on_flushed) { - if (writer_->HasPendingWrite()) - writer_->DoScheduledWrite(); - - if (on_flushed.is_null()) - return; - - file_task_runner_->PostTaskAndReply( - FROM_HERE, base::Bind(&EmptyCallback), on_flushed); +void HistoryDataStore::Flush( + const DictionaryDataStore::OnFlushedCallback& on_flushed) { + data_store_->Flush(on_flushed); } void HistoryDataStore::Load( const HistoryDataStore::OnLoadedCallback& on_loaded) { - base::PostTaskAndReplyWithResult( - file_task_runner_.get(), - FROM_HERE, - base::Bind(&HistoryDataStore::LoadOnBlockingPool, this), - on_loaded); + data_store_->Load(base::Bind(&HistoryDataStore::OnDictionaryLoadedCallback, + this, + on_loaded)); } void HistoryDataStore::SetPrimary(const std::string& query, @@ -160,7 +139,7 @@ void HistoryDataStore::SetPrimary(const std::string& query, base::DictionaryValue* entry_dict = GetEntryDict(query); entry_dict->SetWithoutPathExpansion(kKeyPrimary, new base::StringValue(result)); - writer_->ScheduleWrite(this); + data_store_->ScheduleWrite(); } void HistoryDataStore::SetSecondary( @@ -172,7 +151,7 @@ void HistoryDataStore::SetSecondary( base::DictionaryValue* entry_dict = GetEntryDict(query); entry_dict->SetWithoutPathExpansion(kKeySecondary, results_list.release()); - writer_->ScheduleWrite(this); + data_store_->ScheduleWrite(); } void HistoryDataStore::SetUpdateTime(const std::string& query, @@ -181,18 +160,21 @@ void HistoryDataStore::SetUpdateTime(const std::string& query, entry_dict->SetWithoutPathExpansion(kKeyUpdateTime, new base::StringValue(base::Int64ToString( update_time.ToInternalValue()))); - writer_->ScheduleWrite(this); + data_store_->ScheduleWrite(); } void HistoryDataStore::Delete(const std::string& query) { base::DictionaryValue* assoc_dict = GetAssociationDict(); assoc_dict->RemoveWithoutPathExpansion(query, NULL); - writer_->ScheduleWrite(this); + data_store_->ScheduleWrite(); } base::DictionaryValue* HistoryDataStore::GetAssociationDict() { + base::DictionaryValue* cached_dict = data_store_->cached_dict(); + DCHECK(cached_dict); + base::DictionaryValue* assoc_dict = NULL; - CHECK(cached_json_->GetDictionary(kKeyAssociations, &assoc_dict) && + CHECK(cached_dict->GetDictionary(kKeyAssociations, &assoc_dict) && assoc_dict); return assoc_dict; @@ -212,29 +194,13 @@ base::DictionaryValue* HistoryDataStore::GetEntryDict( return entry_dict; } -scoped_ptr<HistoryData::Associations> HistoryDataStore::LoadOnBlockingPool() { - DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); - - int error_code = JSONFileValueSerializer::JSON_NO_ERROR; - std::string error_message; - JSONFileValueSerializer serializer(data_file_); - base::Value* value = serializer.Deserialize(&error_code, &error_message); - base::DictionaryValue* dict_value = NULL; - if (error_code != JSONFileValueSerializer::JSON_NO_ERROR || - !value || - !value->GetAsDictionary(&dict_value) || - !dict_value) { - return scoped_ptr<HistoryData::Associations>(); +void HistoryDataStore::OnDictionaryLoadedCallback( + OnLoadedCallback callback, scoped_ptr<base::DictionaryValue> dict) { + if (!dict) { + callback.Run(scoped_ptr<HistoryData::Associations>()); + } else { + callback.Run(Parse(dict.Pass()).Pass()); } - - cached_json_.reset(dict_value); - return Parse(*dict_value).Pass(); -} - -bool HistoryDataStore::SerializeData(std::string* data) { - JSONStringValueSerializer serializer(data); - serializer.set_pretty_print(true); - return serializer.Serialize(*cached_json_.get()); } } // namespace app_list diff --git a/chrome/browser/ui/app_list/search/history_data_store.h b/chrome/browser/ui/app_list/search/history_data_store.h index 2df529e0d1..56d8571091 100644 --- a/chrome/browser/ui/app_list/search/history_data_store.h +++ b/chrome/browser/ui/app_list/search/history_data_store.h @@ -14,6 +14,7 @@ #include "base/files/important_file_writer.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "chrome/browser/ui/app_list/search/common/dictionary_data_store.h" #include "chrome/browser/ui/app_list/search/history_data.h" namespace base { @@ -28,18 +29,16 @@ class HistoryDataStoreTest; } // A simple json store to persist HistoryData. -class HistoryDataStore : public base::RefCountedThreadSafe<HistoryDataStore>, - public base::ImportantFileWriter::DataSerializer { +class HistoryDataStore : public base::RefCountedThreadSafe<HistoryDataStore> { public: typedef base::Callback<void(scoped_ptr<HistoryData::Associations>)> OnLoadedCallback; - typedef base::Closure OnFlushedCallback; explicit HistoryDataStore(const base::FilePath& data_file); // Flushes pending writes. |on_flushed| is invoked when disk write is // finished. - void Flush(const OnFlushedCallback& on_flushed); + void Flush(const DictionaryDataStore::OnFlushedCallback& on_flushed); // Reads the persisted data from disk asynchronously. |on_read| is called // with the loaded and parsed data. If there is an error, |on_read| is called @@ -65,18 +64,10 @@ class HistoryDataStore : public base::RefCountedThreadSafe<HistoryDataStore>, // Gets entry dictionary for given |query|. Creates one if necessary. base::DictionaryValue* GetEntryDict(const std::string& query); - // Reads data from backing file. - scoped_ptr<HistoryData::Associations> LoadOnBlockingPool(); + void OnDictionaryLoadedCallback(OnLoadedCallback callback, + scoped_ptr<base::DictionaryValue> dict); - // ImportantFileWriter::DataSerializer overrides: - virtual bool SerializeData(std::string* data) OVERRIDE; - - base::FilePath data_file_; - scoped_refptr<base::SequencedTaskRunner> file_task_runner_; - scoped_ptr<base::ImportantFileWriter> writer_; - - // Cached json dictionary to serve read and incremental change calls. - scoped_ptr<base::DictionaryValue> cached_json_; + scoped_refptr<DictionaryDataStore> data_store_; DISALLOW_COPY_AND_ASSIGN(HistoryDataStore); }; diff --git a/chrome/browser/ui/app_list/search/history_data_store_unittest.cc b/chrome/browser/ui/app_list/search/history_data_store_unittest.cc index 123faa291c..3d71554875 100644 --- a/chrome/browser/ui/app_list/search/history_data_store_unittest.cc +++ b/chrome/browser/ui/app_list/search/history_data_store_unittest.cc @@ -59,7 +59,7 @@ class HistoryDataStoreTest : public testing::Test { } void Flush() { - store_->Flush(HistoryDataStore::OnFlushedCallback()); + store_->Flush(DictionaryDataStore::OnFlushedCallback()); } void Load() { diff --git a/chrome/browser/ui/app_list/search/people/people_result.cc b/chrome/browser/ui/app_list/search/people/people_result.cc index c80a39634d..03f37472b3 100644 --- a/chrome/browser/ui/app_list/search/people/people_result.cc +++ b/chrome/browser/ui/app_list/search/people/people_result.cc @@ -35,7 +35,12 @@ const int kIconSize = 32; const char kImageSizePath[] = "s32-p/"; const char kEmailUrlPrefix[] = "mailto:"; -const char kHangoutsExtensionId[] = "eigpckmcflhchknfhifenfgmjncfgcak"; +const char* const kHangoutsExtensionIds[] = { + "nckgahadagoaajjgafhacjanaoiihapd", + "ljclpkphhpbpinifbeabbhlfddcpfdde", + "ppleadejekpmccmnpjdimmlfljlkdfej", + "eggnbpckecmjlblplehfpjjdhhidfdoj" +}; // Add a query parameter to specify the size to fetch the image in. The // original profile image can be of an arbitrary size, we ask the server to @@ -59,7 +64,7 @@ PeopleResult::PeopleResult(Profile* profile, scoped_ptr<Person> person) set_relevance(person_->interaction_rank); set_details(UTF8ToUTF16(person_->email)); - is_chat_available_ = IsChatAvailable(); + RefreshHangoutsExtensionId(); SetDefaultActions(); image_ = gfx::ImageSkia( @@ -82,7 +87,7 @@ void PeopleResult::Open(int event_flags) { } void PeopleResult::InvokeAction(int action_index, int event_flags) { - if (!is_chat_available_) { + if (hangouts_extension_id_.empty()) { // If the hangouts app is not available, the only option we are showing // to the user is 'Send Email'. SendEmail(); @@ -119,7 +124,7 @@ void PeopleResult::SetDefaultActions() { Actions actions; ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); - if (is_chat_available_) { + if (!hangouts_extension_id_.empty()) { actions.push_back(Action( *bundle.GetImageSkiaNamed(IDR_PEOPLE_SEARCH_ACTION_CHAT), *bundle.GetImageSkiaNamed(IDR_PEOPLE_SEARCH_ACTION_CHAT_HOVER), @@ -158,7 +163,7 @@ void PeopleResult::OpenChat() { // See crbug.com/306672 extensions::ExtensionSystem::Get( profile_)->event_router()->DispatchEventToExtension( - kHangoutsExtensionId, event.Pass()); + hangouts_extension_id_, event.Pass()); } void PeopleResult::SendEmail() { @@ -170,12 +175,18 @@ void PeopleResult::SendEmail() { chrome::Navigate(¶ms); } -bool PeopleResult::IsChatAvailable() { +void PeopleResult::RefreshHangoutsExtensionId() { // TODO(rkc): Change this once we remove the hangoutsPrivate API. // See crbug.com/306672 - return extensions::ExtensionSystem::Get( - profile_)->event_router()->ExtensionHasEventListener( - kHangoutsExtensionId, OnHangoutRequested::kEventName); + for (size_t i = 0; i < arraysize(kHangoutsExtensionIds); ++i) { + if (extensions::ExtensionSystem::Get( + profile_)->event_router()->ExtensionHasEventListener( + kHangoutsExtensionIds[i], OnHangoutRequested::kEventName)) { + hangouts_extension_id_ = kHangoutsExtensionIds[i]; + return; + } + } + hangouts_extension_id_.clear(); } ChromeSearchResultType PeopleResult::GetType() { diff --git a/chrome/browser/ui/app_list/search/people/people_result.h b/chrome/browser/ui/app_list/search/people/people_result.h index d49853d168..2381f98025 100644 --- a/chrome/browser/ui/app_list/search/people/people_result.h +++ b/chrome/browser/ui/app_list/search/people/people_result.h @@ -35,7 +35,12 @@ class PeopleResult : public ChromeSearchResult { void OpenChat(); void SendEmail(); - bool IsChatAvailable(); + // Check if we have any variant of the hangouts extension installed and + // waiting on the onHangoutRequested event (the hangouts extension can have + // a total of four possible id's, depending on which release type of it is + // installed). If so, set the hangouts_extension_id_ to the id of the + // extension that is waiting, or set it to an empty string if not. + void RefreshHangoutsExtensionId(); Profile* profile_; scoped_ptr<Person> person_; @@ -43,9 +48,8 @@ class PeopleResult : public ChromeSearchResult { gfx::ImageSkia image_; base::WeakPtrFactory<PeopleResult> weak_factory_; - // Caches whether or not the hangouts app is available for us to - // send chat/video reqeusts to. - bool is_chat_available_; + // Caches the id of the hangouts extension. + std::string hangouts_extension_id_; DISALLOW_COPY_AND_ASSIGN(PeopleResult); }; diff --git a/chrome/browser/ui/ash/app_list/app_list_controller_ash.cc b/chrome/browser/ui/ash/app_list/app_list_controller_ash.cc index fdf01dd1a0..54b4a51575 100644 --- a/chrome/browser/ui/ash/app_list/app_list_controller_ash.cc +++ b/chrome/browser/ui/ash/app_list/app_list_controller_ash.cc @@ -41,11 +41,6 @@ AppListControllerDelegate::Pinnable PIN_FIXED; } -bool AppListControllerDelegateAsh::CanDoCreateShortcutsFlow( - bool is_platform_app) { - return false; -} - void AppListControllerDelegateAsh::CreateNewWindow(Profile* profile, bool incognito) { if (incognito) diff --git a/chrome/browser/ui/ash/app_list/app_list_controller_ash.h b/chrome/browser/ui/ash/app_list/app_list_controller_ash.h index 60005f509a..bf4c295a54 100644 --- a/chrome/browser/ui/ash/app_list/app_list_controller_ash.h +++ b/chrome/browser/ui/ash/app_list/app_list_controller_ash.h @@ -23,7 +23,6 @@ class AppListControllerDelegateAsh : public AppListControllerDelegate { virtual void PinApp(const std::string& extension_id) OVERRIDE; virtual void UnpinApp(const std::string& extension_id) OVERRIDE; virtual Pinnable GetPinnable() OVERRIDE; - virtual bool CanDoCreateShortcutsFlow(bool is_platform_app) OVERRIDE; virtual void CreateNewWindow(Profile* profile, bool incognito) OVERRIDE; virtual void ActivateApp(Profile* profile, const extensions::Extension* extension, diff --git a/chrome/browser/ui/ash/ash_keyboard_controller_proxy.cc b/chrome/browser/ui/ash/ash_keyboard_controller_proxy.cc index 2b5c833b9c..9c986f6ae9 100644 --- a/chrome/browser/ui/ash/ash_keyboard_controller_proxy.cc +++ b/chrome/browser/ui/ash/ash_keyboard_controller_proxy.cc @@ -9,6 +9,7 @@ #include "chrome/browser/extensions/extension_function_dispatcher.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_web_contents_observer.h" #include "chrome/browser/extensions/event_names.h" #include "chrome/browser/extensions/event_router.h" #include "chrome/browser/media/media_capture_devices_dispatcher.h" @@ -114,6 +115,7 @@ void AshKeyboardControllerProxy::SetupWebContents( new ExtensionFunctionDispatcher(ProfileManager::GetDefaultProfile(), this)); extensions::SetViewType(contents, extensions::VIEW_TYPE_VIRTUAL_KEYBOARD); + extensions::ExtensionWebContentsObserver::CreateForWebContents(contents); Observe(contents); } diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.cc b/chrome/browser/ui/ash/chrome_shell_delegate.cc index f0543bcd06..c94e533de7 100644 --- a/chrome/browser/ui/ash/chrome_shell_delegate.cc +++ b/chrome/browser/ui/ash/chrome_shell_delegate.cc @@ -200,21 +200,6 @@ void ChromeShellDelegate::ToggleFullscreen() { } } -void ChromeShellDelegate::ToggleMaximized() { - // Only toggle if the user has a window open. - aura::Window* window = ash::wm::GetActiveWindow(); - if (!window) - return; - - ash::wm::WindowState* window_state = ash::wm::GetWindowState(window); - // Get out of fullscreen when in fullscreen mode. - if (window_state->IsFullscreen()) { - ToggleFullscreen(); - return; - } - window_state->ToggleMaximized(); -} - void ChromeShellDelegate::RestoreTab() { if (tab_restore_helper_.get()) { DCHECK(!tab_restore_helper_->tab_restore_service()->IsLoaded()); @@ -254,7 +239,9 @@ app_list::AppListViewDelegate* // Shell will own the created delegate, and the delegate will own // the controller. Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord(); - return new AppListViewDelegate(new AppListControllerDelegateAsh(), profile); + return new AppListViewDelegate( + scoped_ptr<AppListControllerDelegate>(new AppListControllerDelegateAsh()), + profile); } ash::LauncherDelegate* ChromeShellDelegate::CreateLauncherDelegate( diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.h b/chrome/browser/ui/ash/chrome_shell_delegate.h index 0675acdb36..804d1c0770 100644 --- a/chrome/browser/ui/ash/chrome_shell_delegate.h +++ b/chrome/browser/ui/ash/chrome_shell_delegate.h @@ -45,7 +45,6 @@ class ChromeShellDelegate : public ash::ShellDelegate, virtual void NewTab() OVERRIDE; virtual void NewWindow(bool is_incognito) OVERRIDE; virtual void ToggleFullscreen() OVERRIDE; - virtual void ToggleMaximized() OVERRIDE; virtual void OpenFileManager() OVERRIDE; virtual void OpenCrosh() OVERRIDE; virtual void RestoreTab() OVERRIDE; diff --git a/chrome/browser/ui/ash/chrome_shell_delegate_browsertest.cc b/chrome/browser/ui/ash/chrome_shell_delegate_browsertest.cc index 892021730a..1caabc8501 100644 --- a/chrome/browser/ui/ash/chrome_shell_delegate_browsertest.cc +++ b/chrome/browser/ui/ash/chrome_shell_delegate_browsertest.cc @@ -6,6 +6,7 @@ #include "apps/shell_window.h" #include "apps/ui/native_app_window.h" +#include "ash/accelerators/accelerator_commands.h" #include "ash/ash_switches.h" #include "ash/shell.h" #include "ash/shell_delegate.h" @@ -39,6 +40,9 @@ bool IsInImmersiveFullscreen(BrowserWindow* browser_window) { typedef InProcessBrowserTest ChromeShellDelegateBrowserTest; +// TODO(oshima): Move these tests to ash once ToggleFullscreen is moved +// to ash. crbug.com/309837. + // Confirm that toggling window miximized works properly IN_PROC_BROWSER_TEST_F(ChromeShellDelegateBrowserTest, ToggleMaximized) { ash::ShellDelegate* shell_delegate = ash::Shell::GetInstance()->delegate(); @@ -46,23 +50,23 @@ IN_PROC_BROWSER_TEST_F(ChromeShellDelegateBrowserTest, ToggleMaximized) { ash::wm::WindowState* window_state = ash::wm::GetActiveWindowState(); ASSERT_TRUE(window_state); - // When not in fullscreen, ShellDelegate::ToggleMaximized toggles Maximized. + // When not in fullscreen, accelerators::ToggleMaximized toggles Maximized. EXPECT_FALSE(window_state->IsMaximized()); - shell_delegate->ToggleMaximized(); + ash::accelerators::ToggleMaximized(); EXPECT_TRUE(window_state->IsMaximized()); - shell_delegate->ToggleMaximized(); + ash::accelerators::ToggleMaximized(); EXPECT_FALSE(window_state->IsMaximized()); - // When in fullscreen ShellDelegate::ToggleMaximized gets out of fullscreen. + // When in fullscreen accelerators::ToggleMaximized gets out of fullscreen. EXPECT_FALSE(window_state->IsFullscreen()); Browser* browser = chrome::FindBrowserWithWindow(window_state->window()); ASSERT_TRUE(browser); chrome::ToggleFullscreenMode(browser); EXPECT_TRUE(window_state->IsFullscreen()); - shell_delegate->ToggleMaximized(); + ash::accelerators::ToggleMaximized(); EXPECT_FALSE(window_state->IsFullscreen()); EXPECT_FALSE(window_state->IsMaximized()); - shell_delegate->ToggleMaximized(); + ash::accelerators::ToggleMaximized(); EXPECT_FALSE(window_state->IsFullscreen()); EXPECT_TRUE(window_state->IsMaximized()); } diff --git a/chrome/browser/ui/ash/chrome_shell_delegate_chromeos.cc b/chrome/browser/ui/ash/chrome_shell_delegate_chromeos.cc index 0c37d2a6f3..2c5be45818 100644 --- a/chrome/browser/ui/ash/chrome_shell_delegate_chromeos.cc +++ b/chrome/browser/ui/ash/chrome_shell_delegate_chromeos.cc @@ -89,7 +89,11 @@ void ChromeShellDelegate::OpenFileManager() { service->GetInstalledExtension(kFileManagerAppId); // event_flags = 0 means this invokes the same behavior as the launcher // item is clicked without any keyboard modifiers. - OpenApplication(AppLaunchParams(profile, extension, 0 /* event_flags */)); + OpenApplication(AppLaunchParams( + profile, + extension, + 0 /* event_flags */, + chrome::HOST_DESKTOP_TYPE_ASH)); } else { // Activate the existing window. list.front()->GetBaseWindow()->Activate(); diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc index 4b7937b0f1..780c3b529d 100644 --- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc +++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc @@ -573,7 +573,11 @@ void ChromeLauncherController::LaunchApp(const std::string& app_id, return; } - AppLaunchParams params(GetProfileForNewWindows(), extension, event_flags); + AppLaunchParams params( + GetProfileForNewWindows(), + extension, + event_flags, + chrome::HOST_DESKTOP_TYPE_ASH); if (source != ash::LAUNCH_FROM_UNKNOWN && app_id == extension_misc::kWebStoreAppId) { // Get the corresponding source string. diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc index 99d30de2b2..c9f37ca00a 100644 --- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc +++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc @@ -12,11 +12,11 @@ #include "ash/launcher/launcher.h" #include "ash/launcher/launcher_button.h" #include "ash/launcher/launcher_model.h" -#include "ash/launcher/launcher_view.h" #include "ash/shelf/shelf_util.h" +#include "ash/shelf/shelf_view.h" #include "ash/shell.h" #include "ash/test/launcher_test_api.h" -#include "ash/test/launcher_view_test_api.h" +#include "ash/test/shelf_view_test_api.h" #include "ash/test/shell_test_api.h" #include "ash/wm/window_state.h" #include "ash/wm/window_util.h" @@ -50,6 +50,7 @@ #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_source.h" #include "content/public/browser/web_contents.h" +#include "content/public/test/browser_test_utils.h" #include "extensions/common/switches.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/app_list/views/apps_grid_view.h" @@ -219,7 +220,7 @@ class LauncherAppBrowserTest : public ExtensionBrowserTest { ExtensionService* service = extensions::ExtensionSystem::Get( profile())->extension_service(); const Extension* extension = - service->GetExtensionById(last_loaded_extension_id_, false); + service->GetExtensionById(last_loaded_extension_id(), false); EXPECT_TRUE(extension); OpenApplication(AppLaunchParams(profile(), @@ -236,7 +237,7 @@ class LauncherAppBrowserTest : public ExtensionBrowserTest { // First get app_id. const Extension* extension = - service->GetExtensionById(last_loaded_extension_id_, false); + service->GetExtensionById(last_loaded_extension_id(), false); const std::string app_id = extension->id(); // Then create a shortcut. @@ -277,7 +278,7 @@ class LauncherAppBrowserTest : public ExtensionBrowserTest { // Try to rip off |item_index|. void RipOffItemIndex(int index, aura::test::EventGenerator* generator, - ash::test::LauncherViewTestAPI* test, + ash::test::ShelfViewTestAPI* test, RipOffCommand command) { ash::internal::LauncherButton* button = test->GetButton(index); gfx::Point start_point = button->GetBoundsInScreen().CenterPoint(); @@ -848,11 +849,9 @@ IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, LaunchPinned) { EXPECT_EQ(++tab_count, tab_strip->count()); EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut_id)).status); WebContents* tab = tab_strip->GetActiveWebContents(); - content::WindowedNotificationObserver close_observer( - content::NOTIFICATION_WEB_CONTENTS_DESTROYED, - content::Source<WebContents>(tab)); + content::WebContentsDestroyedWatcher destroyed_watcher(tab); browser()->tab_strip_model()->CloseSelectedTabs(); - close_observer.Wait(); + destroyed_watcher.Wait(); EXPECT_EQ(--tab_count, tab_strip->count()); EXPECT_EQ(ash::STATUS_CLOSED, (*model_->ItemByID(shortcut_id)).status); } @@ -867,11 +866,9 @@ IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, LaunchUnpinned) { ash::LauncherID shortcut_id = CreateShortcut("app1"); EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut_id)).status); WebContents* tab = tab_strip->GetActiveWebContents(); - content::WindowedNotificationObserver close_observer( - content::NOTIFICATION_WEB_CONTENTS_DESTROYED, - content::Source<WebContents>(tab)); + content::WebContentsDestroyedWatcher destroyed_watcher(tab); browser()->tab_strip_model()->CloseSelectedTabs(); - close_observer.Wait(); + destroyed_watcher.Wait(); EXPECT_EQ(--tab_count, tab_strip->count()); EXPECT_EQ(ash::STATUS_CLOSED, (*model_->ItemByID(shortcut_id)).status); } @@ -884,7 +881,7 @@ IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, LaunchInBackground) { LoadAndLaunchExtension("app1", extension_misc::LAUNCH_TAB, NEW_BACKGROUND_TAB); EXPECT_EQ(++tab_count, tab_strip->count()); - ChromeLauncherController::instance()->LaunchApp(last_loaded_extension_id_, + ChromeLauncherController::instance()->LaunchApp(last_loaded_extension_id(), ash::LAUNCH_FROM_UNKNOWN, 0); } @@ -1548,8 +1545,8 @@ IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, DragAndDrop) { // Get a number of interfaces we need. aura::test::EventGenerator generator( ash::Shell::GetPrimaryRootWindow(), gfx::Point()); - ash::test::LauncherViewTestAPI test( - ash::test::LauncherTestAPI(launcher_).launcher_view()); + ash::test::ShelfViewTestAPI test( + ash::test::LauncherTestAPI(launcher_).shelf_view()); AppListService* service = AppListService::Get(); // There should be two items in our launcher by this time. @@ -1558,7 +1555,7 @@ IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, DragAndDrop) { // Open the app list menu and check that the drag and drop host was set. gfx::Rect app_list_bounds = - test.launcher_view()->GetAppListButtonView()->GetBoundsInScreen(); + test.shelf_view()->GetAppListButtonView()->GetBoundsInScreen(); generator.MoveMouseTo(app_list_bounds.CenterPoint().x(), app_list_bounds.CenterPoint().y()); base::MessageLoop::current()->RunUntilIdle(); @@ -1590,7 +1587,7 @@ IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, DragAndDrop) { // Drag the item into the launcher and check that a new item gets created. const views::ViewModel* vm_launcher = - test.launcher_view()->view_model_for_test(); + test.shelf_view()->view_model_for_test(); views::View* launcher1 = vm_launcher->view_at(1); gfx::Rect bounds_launcher_1 = launcher1->GetBoundsInScreen(); generator.MoveMouseTo(bounds_launcher_1.CenterPoint().x(), @@ -1667,8 +1664,8 @@ IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, DragAndDrop) { IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, DragOffShelf) { aura::test::EventGenerator generator( ash::Shell::GetPrimaryRootWindow(), gfx::Point()); - ash::test::LauncherViewTestAPI test( - ash::test::LauncherTestAPI(launcher_).launcher_view()); + ash::test::ShelfViewTestAPI test( + ash::test::LauncherTestAPI(launcher_).shelf_view()); // Create a known application and check that we have 3 items in the launcher. CreateShortcut("app1"); @@ -1765,8 +1762,8 @@ IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, ClickItem) { // Get a number of interfaces we need. aura::test::EventGenerator generator( ash::Shell::GetPrimaryRootWindow(), gfx::Point()); - ash::test::LauncherViewTestAPI test( - ash::test::LauncherTestAPI(launcher_).launcher_view()); + ash::test::ShelfViewTestAPI test( + ash::test::LauncherTestAPI(launcher_).shelf_view()); AppListService* service = AppListService::Get(); // There should be two items in our launcher by this time. EXPECT_EQ(2, model_->item_count()); @@ -1774,7 +1771,7 @@ IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, ClickItem) { // Open the app list menu and check that the drag and drop host was set. gfx::Rect app_list_bounds = - test.launcher_view()->GetAppListButtonView()->GetBoundsInScreen(); + test.shelf_view()->GetAppListButtonView()->GetBoundsInScreen(); generator.MoveMouseTo(app_list_bounds.CenterPoint().x(), app_list_bounds.CenterPoint().y()); generator.ClickLeftButton(); @@ -1866,8 +1863,8 @@ IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, OverflowBubble) { // No overflow yet. EXPECT_FALSE(launcher_->IsShowingOverflowBubble()); - ash::test::LauncherViewTestAPI test( - ash::test::LauncherTestAPI(launcher_).launcher_view()); + ash::test::ShelfViewTestAPI test( + ash::test::LauncherTestAPI(launcher_).shelf_view()); int items_added = 0; while (!test.IsOverflowButtonVisible()) { diff --git a/chrome/browser/ui/ash/multi_user_window_manager.cc b/chrome/browser/ui/ash/multi_user_window_manager.cc index a26c92f670..0a58841505 100644 --- a/chrome/browser/ui/ash/multi_user_window_manager.cc +++ b/chrome/browser/ui/ash/multi_user_window_manager.cc @@ -28,13 +28,6 @@ namespace { chrome::MultiUserWindowManager* g_instance = NULL; - -// Get the user id from a given profile. -std::string GetUserIDFromProfile(Profile* profile) { - return gaia::CanonicalizeEmail(gaia::SanitizeEmail( - profile->GetOriginalProfile()->GetProfileName())); -} - } // namespace namespace chrome { @@ -84,6 +77,12 @@ void MultiUserWindowManager::DeleteInstance() { g_instance = NULL; } +// static +std::string MultiUserWindowManager::GetUserIDFromProfile(Profile* profile) { + return gaia::CanonicalizeEmail(gaia::SanitizeEmail( + profile->GetOriginalProfile()->GetProfileName())); +} + void MultiUserWindowManager::SetWindowOwner(aura::Window* window, const std::string& user_id) { // Make sure the window is valid and there was no owner yet. diff --git a/chrome/browser/ui/ash/multi_user_window_manager.h b/chrome/browser/ui/ash/multi_user_window_manager.h index bff9762e26..fea8fd0cbe 100644 --- a/chrome/browser/ui/ash/multi_user_window_manager.h +++ b/chrome/browser/ui/ash/multi_user_window_manager.h @@ -62,6 +62,9 @@ class MultiUserWindowManager : public ash::SessionStateObserver, // Removes the instance. static void DeleteInstance(); + // Get the user id from a given profile. + static std::string GetUserIDFromProfile(Profile* profile); + // Assigns an owner to a passed window. Note that this window's parent should // be a direct child of the root window. // A user switch will automatically change the visibility - and - if the diff --git a/chrome/browser/ui/ash/multi_user_window_manager_unittest.cc b/chrome/browser/ui/ash/multi_user_window_manager_unittest.cc index d400c7bca6..02c60363cc 100644 --- a/chrome/browser/ui/ash/multi_user_window_manager_unittest.cc +++ b/chrome/browser/ui/ash/multi_user_window_manager_unittest.cc @@ -20,9 +20,6 @@ namespace ash { namespace test { -// This many test windows will get created. -const int kNumberOfTestWindows = 5; - // A test class for preparing the chrome::MultiUserWindowManager. It creates // various windows and instantiates the chrome::MultiUserWindowManager. class MultiUserWindowManagerTest : public AshTestBase { diff --git a/chrome/browser/ui/ash/session_state_delegate_chromeos.cc b/chrome/browser/ui/ash/session_state_delegate_chromeos.cc index b4a7ccddf7..e1ff42ba58 100644 --- a/chrome/browser/ui/ash/session_state_delegate_chromeos.cc +++ b/chrome/browser/ui/ash/session_state_delegate_chromeos.cc @@ -90,6 +90,14 @@ const std::string SessionStateDelegateChromeos::GetUserEmail( GetLRULoggedInUsers()[index]->display_email(); } +const std::string SessionStateDelegateChromeos::GetUserID( + ash::MultiProfileIndex index) const { + DCHECK_LT(index, NumberOfLoggedInUsers()); + return gaia::CanonicalizeEmail(gaia::SanitizeEmail( + chromeos::UserManager::Get()-> + GetLRULoggedInUsers()[index]->email())); +} + const gfx::ImageSkia& SessionStateDelegateChromeos::GetUserImage( ash::MultiProfileIndex index) const { DCHECK_LT(index, NumberOfLoggedInUsers()); @@ -107,11 +115,11 @@ void SessionStateDelegateChromeos::GetLoggedInUsers(ash::UserIdList* users) { } void SessionStateDelegateChromeos::SwitchActiveUser( - const std::string& user_email) { + const std::string& user_id) { // Disallow switching to an already active user since that might crash. - // Transform the |user_email| into a |user_id| for comparison & switching. - std::string user_id = - gaia::CanonicalizeEmail(gaia::SanitizeEmail(user_email)); + // Also check that we got a user id and not an email address. + DCHECK_EQ(user_id, + gaia::CanonicalizeEmail(gaia::SanitizeEmail(user_id))); if (user_id == chromeos::UserManager::Get()->GetActiveUser()->email()) return; chromeos::UserManager::Get()->SwitchActiveUser(user_id); diff --git a/chrome/browser/ui/ash/session_state_delegate_chromeos.h b/chrome/browser/ui/ash/session_state_delegate_chromeos.h index 69936a42f1..24a7964113 100644 --- a/chrome/browser/ui/ash/session_state_delegate_chromeos.h +++ b/chrome/browser/ui/ash/session_state_delegate_chromeos.h @@ -36,10 +36,12 @@ class SessionStateDelegateChromeos ash::MultiProfileIndex index) const OVERRIDE; virtual const std::string GetUserEmail( ash::MultiProfileIndex index) const OVERRIDE; + virtual const std::string GetUserID( + ash::MultiProfileIndex index) const OVERRIDE; virtual const gfx::ImageSkia& GetUserImage( ash::MultiProfileIndex index) const OVERRIDE; virtual void GetLoggedInUsers(ash::UserIdList* users) OVERRIDE; - virtual void SwitchActiveUser(const std::string& user_email) OVERRIDE; + virtual void SwitchActiveUser(const std::string& user_id) OVERRIDE; virtual void SwitchActiveUserToNext() OVERRIDE; virtual void AddSessionStateObserver( ash::SessionStateObserver* observer) OVERRIDE; diff --git a/chrome/browser/ui/ash/session_state_delegate_views.cc b/chrome/browser/ui/ash/session_state_delegate_views.cc index 9933890a18..2998204199 100644 --- a/chrome/browser/ui/ash/session_state_delegate_views.cc +++ b/chrome/browser/ui/ash/session_state_delegate_views.cc @@ -66,6 +66,12 @@ const std::string SessionStateDelegate::GetUserEmail( return ""; } +const std::string SessionStateDelegate::GetUserID( + ash::MultiProfileIndex index) const { + NOTIMPLEMENTED(); + return ""; +} + const gfx::ImageSkia& SessionStateDelegate::GetUserImage( ash::MultiProfileIndex index) const { NOTIMPLEMENTED(); @@ -77,7 +83,7 @@ void SessionStateDelegate::GetLoggedInUsers(ash::UserIdList* users) { NOTIMPLEMENTED(); } -void SessionStateDelegate::SwitchActiveUser(const std::string& user_email) { +void SessionStateDelegate::SwitchActiveUser(const std::string& user_id) { NOTIMPLEMENTED(); } diff --git a/chrome/browser/ui/ash/session_state_delegate_views.h b/chrome/browser/ui/ash/session_state_delegate_views.h index 482d5d1997..238c1b487a 100644 --- a/chrome/browser/ui/ash/session_state_delegate_views.h +++ b/chrome/browser/ui/ash/session_state_delegate_views.h @@ -33,10 +33,12 @@ class SessionStateDelegate : public ash::SessionStateDelegate { ash::MultiProfileIndex index) const OVERRIDE; virtual const std::string GetUserEmail( ash::MultiProfileIndex index) const OVERRIDE; + virtual const std::string GetUserID( + ash::MultiProfileIndex index) const OVERRIDE; virtual const gfx::ImageSkia& GetUserImage( ash::MultiProfileIndex index) const OVERRIDE; virtual void GetLoggedInUsers(ash::UserIdList* users) OVERRIDE; - virtual void SwitchActiveUser(const std::string& user_email) OVERRIDE; + virtual void SwitchActiveUser(const std::string& user_id) OVERRIDE; virtual void SwitchActiveUserToNext() OVERRIDE; virtual void AddSessionStateObserver( ash::SessionStateObserver* observer) OVERRIDE; diff --git a/chrome/browser/ui/autofill/autofill_dialog_controller_browsertest.cc b/chrome/browser/ui/autofill/autofill_dialog_controller_browsertest.cc index 2fbc3d7ce6..36bfd41b82 100644 --- a/chrome/browser/ui/autofill/autofill_dialog_controller_browsertest.cc +++ b/chrome/browser/ui/autofill/autofill_dialog_controller_browsertest.cc @@ -15,11 +15,13 @@ #include "chrome/browser/ui/autofill/autofill_dialog_view.h" #include "chrome/browser/ui/autofill/data_model_wrapper.h" #include "chrome/browser/ui/autofill/tab_autofill_manager_delegate.h" +#include "chrome/browser/ui/autofill/test_generated_credit_card_bubble_controller.h" #include "chrome/browser/ui/autofill/testable_autofill_dialog_view.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" +#include "components/autofill/content/browser/risk/proto/fingerprint.pb.h" #include "components/autofill/content/browser/wallet/mock_wallet_client.h" #include "components/autofill/content/browser/wallet/wallet_test_util.h" #include "components/autofill/core/browser/autofill_common_test.h" @@ -42,6 +44,8 @@ namespace autofill { namespace { +using testing::_; + void MockCallback(const FormStructure*) {} class MockAutofillMetrics : public AutofillMetrics { @@ -74,10 +78,11 @@ class MockAutofillMetrics : public AutofillMetrics { class TestAutofillDialogController : public AutofillDialogControllerImpl { public: - TestAutofillDialogController(content::WebContents* contents, - const FormData& form_data, - const AutofillMetrics& metric_logger, - scoped_refptr<content::MessageLoopRunner> runner) + TestAutofillDialogController( + content::WebContents* contents, + const FormData& form_data, + const AutofillMetrics& metric_logger, + scoped_refptr<content::MessageLoopRunner> runner) : AutofillDialogControllerImpl(contents, form_data, form_data.origin, @@ -121,10 +126,16 @@ class TestAutofillDialogController : public AutofillDialogControllerImpl { return false; } + void ForceFinishSubmit() { + DoFinishSubmit(); + } + // Increase visibility for testing. using AutofillDialogControllerImpl::view; using AutofillDialogControllerImpl::input_showing_popup; + MOCK_METHOD0(LoadRiskFingerprintData, void()); + virtual std::vector<DialogNotification> CurrentNotifications() OVERRIDE { return notifications_; } @@ -139,6 +150,8 @@ class TestAutofillDialogController : public AutofillDialogControllerImpl { using AutofillDialogControllerImpl::IsEditingExistingData; using AutofillDialogControllerImpl::IsManuallyEditingSection; + using AutofillDialogControllerImpl::IsSubmitPausedOn; + using AutofillDialogControllerImpl::OnDidLoadRiskFingerprintData; void set_use_validation(bool use_validation) { use_validation_ = use_validation; @@ -148,6 +161,10 @@ class TestAutofillDialogController : public AutofillDialogControllerImpl { return weak_ptr_factory_.GetWeakPtr(); } + wallet::MockWalletClient* GetTestingWalletClient() { + return &mock_wallet_client_; + } + protected: virtual PersonalDataManager* GetManager() OVERRIDE { return &test_manager_; @@ -212,6 +229,11 @@ class AutofillDialogControllerTest : public InProcessBrowserTest { field.autocomplete_attribute = "shipping tel"; form.fields.push_back(field); + test_generated_bubble_controller_ = + new testing::NiceMock<TestGeneratedCreditCardBubbleController>( + GetActiveWebContents()); + ASSERT_TRUE(test_generated_bubble_controller_->IsInstalled()); + message_loop_runner_ = new content::MessageLoopRunner; controller_ = new TestAutofillDialogController( GetActiveWebContents(), @@ -303,15 +325,23 @@ class AutofillDialogControllerTest : public InProcessBrowserTest { WaitForWebDB(); } + TestGeneratedCreditCardBubbleController* test_generated_bubble_controller() { + return test_generated_bubble_controller_; + } + private: void WaitForWebDB() { content::RunAllPendingInMessageLoop(content::BrowserThread::DB); } - MockAutofillMetrics metric_logger_; + testing::NiceMock<MockAutofillMetrics> metric_logger_; TestAutofillDialogController* controller_; // Weak reference. scoped_refptr<content::MessageLoopRunner> message_loop_runner_; scoped_ptr<content::DOMMessageQueue> dom_message_queue_; + + // Weak; owned by the active web contents. + TestGeneratedCreditCardBubbleController* test_generated_bubble_controller_; + DISALLOW_COPY_AND_ASSIGN(AutofillDialogControllerTest); }; @@ -583,49 +613,6 @@ IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, ShouldShowErrorBubble) { EXPECT_TRUE(controller()->ShouldShowErrorBubble()); } -// Tests that credit card number is disabled while editing a Wallet instrument. -IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, WalletCreditCardDisabled) { - std::vector<std::string> usernames; - usernames.push_back("user@example.com"); - controller()->OnUserNameFetchSuccess(usernames); - controller()->OnDidFetchWalletCookieValue(std::string()); - - scoped_ptr<wallet::WalletItems> wallet_items = - wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED); - // An expired card will be forced into edit mode. - wallet_items->AddInstrument(wallet::GetTestMaskedInstrumentWithDetails( - "instrument_id", - wallet::GetTestAddress(), - wallet::WalletItems::MaskedInstrument::VISA, - wallet::WalletItems::MaskedInstrument::EXPIRED)); - controller()->OnDidGetWalletItems(wallet_items.Pass()); - - const DetailInputs& edit_inputs = - controller()->RequestedFieldsForSection(SECTION_CC_BILLING); - size_t i; - for (i = 0; i < edit_inputs.size(); ++i) { - if (edit_inputs[i].type == CREDIT_CARD_NUMBER) { - EXPECT_FALSE(edit_inputs[i].editable); - break; - } - } - ASSERT_LT(i, edit_inputs.size()); - - // Select "Add new billing info..." while using Wallet. - ui::MenuModel* model = controller()->MenuModelForSection(SECTION_CC_BILLING); - model->ActivatedAt(model->GetItemCount() - 2); - - const DetailInputs& add_inputs = - controller()->RequestedFieldsForSection(SECTION_CC_BILLING); - for (i = 0; i < add_inputs.size(); ++i) { - if (add_inputs[i].type == CREDIT_CARD_NUMBER) { - EXPECT_TRUE(add_inputs[i].editable); - break; - } - } - ASSERT_LT(i, add_inputs.size()); -} - // Ensure that expired cards trigger invalid suggestions. IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, ExpiredCard) { CreditCard verified_card(test::GetCreditCard()); @@ -819,4 +806,58 @@ IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, MAYBE_PreservedSections) { } #endif // defined(TOOLKIT_VIEWS) || defined(OS_MACOSX) +// TODO(groby): figure out if the CVC challenge code actually works on Mac. +#if !defined(OS_MACOSX) +IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, + GeneratedCardLastFourAfterVerifyCvv) { + std::vector<std::string> usernames; + usernames.push_back("user@example.com"); + controller()->OnUserNameFetchSuccess(usernames); + controller()->OnDidFetchWalletCookieValue(std::string()); + + scoped_ptr<wallet::WalletItems> wallet_items = + wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED); + wallet_items->AddInstrument(wallet::GetTestMaskedInstrument()); + wallet_items->AddAddress(wallet::GetTestShippingAddress()); + + base::string16 last_four = + wallet_items->instruments()[0]->TypeAndLastFourDigits(); + controller()->OnDidGetWalletItems(wallet_items.Pass()); + + EXPECT_CALL(*controller(), LoadRiskFingerprintData()); + controller()->OnAccept(); + + EXPECT_CALL(*controller()->GetTestingWalletClient(), GetFullWallet(_)); + scoped_ptr<risk::Fingerprint> fingerprint(new risk::Fingerprint()); + fingerprint->mutable_machine_characteristics()->mutable_screen_size()-> + set_width(1024); + controller()->OnDidLoadRiskFingerprintData(fingerprint.Pass()); + + controller()->OnDidGetFullWallet( + wallet::GetTestFullWalletWithRequiredActions( + std::vector<wallet::RequiredAction>(1, wallet::VERIFY_CVV))); + + ASSERT_TRUE(controller()->IsSubmitPausedOn(wallet::VERIFY_CVV)); + + std::string fake_cvc("123"); + TestableAutofillDialogView* test_view = controller()->GetTestableView(); + test_view->SetTextContentsOfSuggestionInput(SECTION_CC_BILLING, + ASCIIToUTF16(fake_cvc)); + + EXPECT_CALL(*controller()->GetTestingWalletClient(), + AuthenticateInstrument(_, fake_cvc)); + controller()->OnAccept(); + + EXPECT_CALL(*controller()->GetTestingWalletClient(), GetFullWallet(_)); + controller()->OnDidAuthenticateInstrument(true); + controller()->OnDidGetFullWallet(wallet::GetTestFullWallet()); + controller()->ForceFinishSubmit(); + + RunMessageLoop(); + + EXPECT_EQ(1, test_generated_bubble_controller()->bubbles_shown()); + EXPECT_EQ(last_four, test_generated_bubble_controller()->backing_card_name()); +} +#endif // !defined(OS_MACOSX) + } // namespace autofill diff --git a/chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc b/chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc index ff9d063cf7..384a8d7f09 100644 --- a/chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc +++ b/chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc @@ -249,7 +249,6 @@ string16 GetValueForType(const DetailOutputMap& output, if (it->first->type == type) return it->second; } - NOTREACHED(); return string16(); } @@ -935,11 +934,6 @@ bool AutofillDialogControllerImpl::SectionIsActive(DialogSection section) return section != SECTION_CC_BILLING; } -bool AutofillDialogControllerImpl::IsSubmitPausedOn( - wallet::RequiredAction required_action) const { - return full_wallet_ && full_wallet_->HasRequiredAction(required_action); -} - void AutofillDialogControllerImpl::GetWalletItems() { ScopedViewUpdates updates(view_.get()); @@ -1151,10 +1145,6 @@ void AutofillDialogControllerImpl::ShowEditUiIfBadSuggestion( DetailInputs* inputs = MutableRequestedFieldsForSection(section); if (wrapper && IsEditingExistingData(section)) wrapper->FillInputs(inputs); - - for (DetailInputs::iterator it = inputs->begin(); it != inputs->end(); ++it) { - it->editable = InputIsEditable(*it, section); - } } bool AutofillDialogControllerImpl::InputWasEdited(ServerFieldType type, @@ -1561,31 +1551,6 @@ gfx::Image AutofillDialogControllerImpl::ExtraSuggestionIconForSection( model->GetInfo(AutofillType(CREDIT_CARD_TYPE))); } -// TODO(groby): Remove this deprecated method after Mac starts using -// IconsForFields. http://crbug.com/292876 -gfx::Image AutofillDialogControllerImpl::IconForField( - ServerFieldType type, const string16& user_input) const { - ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); - if (type == CREDIT_CARD_VERIFICATION_CODE) - return rb.GetImageNamed(IDR_CREDIT_CARD_CVC_HINT); - - if (type == CREDIT_CARD_NUMBER) { - const int input_card_idr = CreditCard::IconResourceId( - CreditCard::GetCreditCardType(user_input)); - if (input_card_idr != IDR_AUTOFILL_CC_GENERIC) - return rb.GetImageNamed(input_card_idr); - - // When the credit card type is unknown, no image should be shown. However, - // to simplify the view code on Mac, save space for the credit card image by - // returning a transparent image of the appropriate size. - gfx::ImageSkia image = *rb.GetImageSkiaNamed(input_card_idr); - return - gfx::Image(gfx::ImageSkiaOperations::CreateTransparentImage(image, 0)); - } - - return gfx::Image(); -} - FieldIconMap AutofillDialogControllerImpl::IconsForFields( const FieldValueMap& user_inputs) const { FieldIconMap result; @@ -1622,6 +1587,41 @@ string16 AutofillDialogControllerImpl::TooltipForField(ServerFieldType type) return string16(); } +bool AutofillDialogControllerImpl::InputIsEditable( + const DetailInput& input, + DialogSection section) { + if (section != SECTION_CC_BILLING) + return true; + + if (input.type == CREDIT_CARD_NUMBER) + return !IsEditingExistingData(section); + + // For CVC, only require (allow) input if the user has edited some other + // aspect of the card. + if (input.type == CREDIT_CARD_VERIFICATION_CODE && + IsEditingExistingData(section)) { + DetailOutputMap output; + view_->GetUserInput(section, &output); + WalletInstrumentWrapper wrapper(ActiveInstrument()); + + for (DetailOutputMap::iterator iter = output.begin(); iter != output.end(); + ++iter) { + if (iter->first->type == input.type) + continue; + + AutofillType type(iter->first->type); + if (type.group() == CREDIT_CARD && + iter->second != wrapper.GetInfo(type)) { + return true; + } + } + + return false; + } + + return true; +} + // TODO(groby): Add more tests. string16 AutofillDialogControllerImpl::InputValidityMessage( DialogSection section, @@ -2052,8 +2052,26 @@ bool AutofillDialogControllerImpl::OnAccept() { // This must come before SetIsSubmitting(). if (IsPayingWithWallet()) { - submitted_cardholder_name_ = - GetValueFromSection(SECTION_CC_BILLING, NAME_FULL); + // In the VERIFY_CVV case, hold onto the previously submitted cardholder + // name. + if (!IsSubmitPausedOn(wallet::VERIFY_CVV)) { + submitted_cardholder_name_ = + GetValueFromSection(SECTION_CC_BILLING, NAME_BILLING_FULL); + + // Snag the last four digits of the backing card now as it could be wiped + // out if a CVC challenge happens. + if (ActiveInstrument()) { + backing_card_last_four_ = ActiveInstrument()->TypeAndLastFourDigits(); + } else { + DetailOutputMap output; + view_->GetUserInput(SECTION_CC_BILLING, &output); + CreditCard card; + GetBillingInfoFromOutputs(output, &card, NULL, NULL); + backing_card_last_four_ = card.TypeAndLastFourDigits(); + } + } + DCHECK(!submitted_cardholder_name_.empty()); + DCHECK(!backing_card_last_four_.empty()); } SetIsSubmitting(true); @@ -2258,21 +2276,20 @@ void AutofillDialogControllerImpl::OnDidGetFullWallet( choose_another_instrument_or_address_ = true; SetIsSubmitting(false); GetWalletItems(); - view_->UpdateNotificationArea(); - view_->UpdateButtonStrip(); - view_->UpdateOverlay(); break; case wallet::VERIFY_CVV: SuggestionsUpdated(); - view_->UpdateButtonStrip(); - view_->UpdateNotificationArea(); break; default: DisableWallet(wallet::WalletClient::UNKNOWN_ERROR); - break; + return; } + + view_->UpdateNotificationArea(); + view_->UpdateButtonStrip(); + view_->UpdateOverlay(); } void AutofillDialogControllerImpl::OnPassiveSigninSuccess( @@ -2434,6 +2451,11 @@ bool AutofillDialogControllerImpl::TransmissionWillBeSecure() const { return source_url_.SchemeIs(content::kHttpsScheme); } +bool AutofillDialogControllerImpl::IsSubmitPausedOn( + wallet::RequiredAction required_action) const { + return full_wallet_ && full_wallet_->HasRequiredAction(required_action); +} + void AutofillDialogControllerImpl::ShowNewCreditCardBubble( scoped_ptr<CreditCard> new_card, scoped_ptr<AutofillProfile> billing_profile) { @@ -2884,13 +2906,7 @@ string16 AutofillDialogControllerImpl::GetValueFromSection( DetailOutputMap output; view_->GetUserInput(section, &output); - for (DetailOutputMap::iterator iter = output.begin(); iter != output.end(); - ++iter) { - if (iter->first->type == type) - return iter->second; - } - - return string16(); + return GetValueForType(output, type); } SuggestionsMenuModel* AutofillDialogControllerImpl:: @@ -2985,18 +3001,6 @@ base::string16 AutofillDialogControllerImpl::CreditCardNumberValidityMessage( return base::string16(); } -bool AutofillDialogControllerImpl::InputIsEditable( - const DetailInput& input, - DialogSection section) const { - if (input.type != CREDIT_CARD_NUMBER || !IsPayingWithWallet()) - return true; - - if (IsEditingExistingData(section)) - return false; - - return true; -} - bool AutofillDialogControllerImpl::AllSectionsAreValid() { for (size_t section = SECTION_MIN; section <= SECTION_MAX; ++section) { if (!SectionIsValid(static_cast<DialogSection>(section))) @@ -3479,21 +3483,11 @@ void AutofillDialogControllerImpl::MaybeShowCreditCardBubble() { if (!full_wallet_ || !full_wallet_->billing_address()) return; - base::string16 backing_last_four; - if (ActiveInstrument()) { - backing_last_four = ActiveInstrument()->TypeAndLastFourDigits(); - } else { - DetailOutputMap output; - view_->GetUserInput(SECTION_CC_BILLING, &output); - CreditCard card; - GetBillingInfoFromOutputs(output, &card, NULL, NULL); - backing_last_four = card.TypeAndLastFourDigits(); - } #if !defined(OS_ANDROID) GeneratedCreditCardBubbleController::Show( web_contents(), full_wallet_->TypeAndLastFourDigits(), - backing_last_four); + backing_card_last_four_); #endif } diff --git a/chrome/browser/ui/autofill/autofill_dialog_controller_impl.h b/chrome/browser/ui/autofill/autofill_dialog_controller_impl.h index ff432b2fb0..b320ed4bbf 100644 --- a/chrome/browser/ui/autofill/autofill_dialog_controller_impl.h +++ b/chrome/browser/ui/autofill/autofill_dialog_controller_impl.h @@ -131,14 +131,12 @@ class AutofillDialogControllerImpl : public AutofillDialogViewDelegate, virtual string16 LabelForSection(DialogSection section) const OVERRIDE; virtual SuggestionState SuggestionStateForSection( DialogSection section) OVERRIDE; - // TODO(groby): Remove this deprecated method after Mac starts using - // IconsForFields. http://crbug.com/292876 - virtual gfx::Image IconForField(ServerFieldType type, - const string16& user_input) const OVERRIDE; virtual FieldIconMap IconsForFields(const FieldValueMap& user_inputs) const OVERRIDE; virtual bool FieldControlsIcons(ServerFieldType type) const OVERRIDE; virtual string16 TooltipForField(ServerFieldType type) const OVERRIDE; + virtual bool InputIsEditable(const DetailInput& input, DialogSection section) + OVERRIDE; virtual string16 InputValidityMessage(DialogSection section, ServerFieldType type, const string16& value) OVERRIDE; @@ -285,6 +283,9 @@ class AutofillDialogControllerImpl : public AutofillDialogViewDelegate, // to the requesting site. virtual bool TransmissionWillBeSecure() const; + // Whether submission is currently waiting for |action| to be handled. + bool IsSubmitPausedOn(wallet::RequiredAction action) const; + // Shows a new credit card saved bubble and passes ownership of |new_card| and // |billing_profile| to the bubble. Exposed for testing. virtual void ShowNewCreditCardBubble( @@ -463,9 +464,6 @@ class AutofillDialogControllerImpl : public AutofillDialogViewDelegate, base::string16 CreditCardNumberValidityMessage( const base::string16& number) const; - // Whether a particular DetailInput in |section| should be edited or not. - bool InputIsEditable(const DetailInput& input, DialogSection section) const; - // Whether all of the input fields currently showing in the dialog have valid // contents. This validates only by checking "sure" messages, i.e. messages // that would have been displayed to the user during editing, as opposed to @@ -521,9 +519,6 @@ class AutofillDialogControllerImpl : public AutofillDialogViewDelegate, void HandleSaveOrUpdateRequiredActions( const std::vector<wallet::RequiredAction>& required_actions); - // Whether submission is currently waiting for |action| to be handled. - bool IsSubmitPausedOn(wallet::RequiredAction action) const; - // Shows a card generation overlay if necessary, then calls DoFinishSubmit. void FinishSubmit(); @@ -733,6 +728,10 @@ class AutofillDialogControllerImpl : public AutofillDialogViewDelegate, // saved. Never populated while incognito (as nothing's actually saved). scoped_ptr<CreditCard> newly_saved_card_; + // The last four digits of the backing card used for the current run of the + // dialog. Only applies to Wallet and is populated on submit. + base::string16 backing_card_last_four_; + // The timer that delays enabling submit button for a short period of time on // startup. base::OneShotTimer<AutofillDialogControllerImpl> submit_button_delay_timer_; diff --git a/chrome/browser/ui/autofill/autofill_dialog_controller_unittest.cc b/chrome/browser/ui/autofill/autofill_dialog_controller_unittest.cc index 799d7a2564..f0c23422dd 100644 --- a/chrome/browser/ui/autofill/autofill_dialog_controller_unittest.cc +++ b/chrome/browser/ui/autofill/autofill_dialog_controller_unittest.cc @@ -12,13 +12,14 @@ #include "base/prefs/pref_service.h" #include "base/run_loop.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" #include "base/strings/utf_string_conversions.h" #include "base/tuple.h" #include "chrome/browser/ui/autofill/autofill_dialog_controller_impl.h" #include "chrome/browser/ui/autofill/autofill_dialog_view.h" #include "chrome/browser/ui/autofill/generated_credit_card_bubble_controller.h" #include "chrome/browser/ui/autofill/mock_new_credit_card_bubble_controller.h" -#include "chrome/browser/ui/autofill/test_generated_credit_card_bubble_view.h" +#include "chrome/browser/ui/autofill/test_generated_credit_card_bubble_controller.h" #include "chrome/common/pref_names.h" #include "chrome/common/render_messages.h" #include "chrome/test/base/chrome_render_view_host_test_harness.h" @@ -45,12 +46,12 @@ #include "ui/base/win/scoped_ole_initializer.h" #endif -using testing::_; - namespace autofill { namespace { +using testing::_; + const char kFakeEmail[] = "user@example.com"; const char kFakeFingerprintEncoded[] = "CgVaAwiACA=="; const char kEditedBillingAddress[] = "123 edited billing address"; @@ -113,14 +114,6 @@ scoped_ptr<wallet::WalletItems> CompleteAndValidWalletItems() { return items.Pass(); } -scoped_ptr<wallet::FullWallet> CreateFullWallet(const char* required_action) { - base::DictionaryValue dict; - scoped_ptr<base::ListValue> list(new base::ListValue()); - list->AppendString(required_action); - dict.Set("required_action", list.release()); - return wallet::FullWallet::CreateFullWallet(dict); -} - scoped_ptr<risk::Fingerprint> GetFakeFingerprint() { scoped_ptr<risk::Fingerprint> fingerprint(new risk::Fingerprint()); // Add some data to the proto, else the encoded content is empty. @@ -321,6 +314,7 @@ class TestAutofillDialogController MOCK_METHOD0(LoadRiskFingerprintData, void()); using AutofillDialogControllerImpl::OnDidLoadRiskFingerprintData; using AutofillDialogControllerImpl::IsEditingExistingData; + using AutofillDialogControllerImpl::IsSubmitPausedOn; protected: virtual PersonalDataManager* GetManager() OVERRIDE { @@ -373,34 +367,6 @@ class TestAutofillDialogController DISALLOW_COPY_AND_ASSIGN(TestAutofillDialogController); }; -class TestGeneratedCreditCardBubbleController : - public GeneratedCreditCardBubbleController { - public: - explicit TestGeneratedCreditCardBubbleController( - content::WebContents* contents) - : GeneratedCreditCardBubbleController(contents) { - contents->SetUserData(UserDataKey(), this); - CHECK_EQ(contents->GetUserData(UserDataKey()), this); - } - - virtual ~TestGeneratedCreditCardBubbleController() {} - - MOCK_METHOD2(SetupAndShow, void(const base::string16& backing_card_name, - const base::string16& fronting_card_name)); - - protected: - virtual base::WeakPtr<GeneratedCreditCardBubbleView> CreateBubble() OVERRIDE { - return TestGeneratedCreditCardBubbleView::Create(GetWeakPtr()); - } - - virtual bool CanShow() const OVERRIDE { - return true; - } - - private: - DISALLOW_COPY_AND_ASSIGN(TestGeneratedCreditCardBubbleController); -}; - class AutofillDialogControllerTest : public ChromeRenderViewHostTestHarness { protected: AutofillDialogControllerTest(): form_structure_(NULL) {} @@ -424,6 +390,8 @@ class AutofillDialogControllerTest : public ChromeRenderViewHostTestHarness { test_generated_bubble_controller_ = new testing::NiceMock<TestGeneratedCreditCardBubbleController>( web_contents()); + ASSERT_TRUE(test_generated_bubble_controller_->IsInstalled()); + mock_new_card_bubble_controller_.reset( new MockNewCreditCardBubbleController); @@ -469,6 +437,7 @@ class AutofillDialogControllerTest : public ChromeRenderViewHostTestHarness { controller()->OnDidGetWalletItems(CompleteAndValidWalletItems()); } + // Fills the inputs in SECTION_CC with data. void FillCreditCardInputs() { DetailOutputMap cc_outputs; const DetailInputs& cc_inputs = @@ -480,6 +449,24 @@ class AutofillDialogControllerTest : public ChromeRenderViewHostTestHarness { controller()->GetView()->SetUserInput(SECTION_CC, cc_outputs); } + // Fills the inputs in SECTION_CC_BILLING with valid data. + void FillCCBillingInputs() { + DetailOutputMap outputs; + const DetailInputs& inputs = + controller()->RequestedFieldsForSection(SECTION_CC_BILLING); + AutofillProfile full_profile(test::GetVerifiedProfile()); + CreditCard full_card(test::GetCreditCard()); + for (size_t i = 0; i < inputs.size(); ++i) { + const DetailInput& input = inputs[i]; + outputs[&input] = full_profile.GetInfo(AutofillType(input.type), + "en-US"); + + if (outputs[&input].empty()) + outputs[&input] = full_card.GetInfo(AutofillType(input.type), "en-US"); + } + controller()->GetView()->SetUserInput(SECTION_CC_BILLING, outputs); + } + // Activates the 'Add new foo' option from the |section|'s suggestions // dropdown and fills the |section|'s inputs with the data from the // |data_model|. If |section| is SECTION_CC, also fills in '123' for the CVC. @@ -993,7 +980,7 @@ TEST_F(AutofillDialogControllerTest, NewAutofillProfileIsDefault) { TEST_F(AutofillDialogControllerTest, AutofillProfileVariants) { SwitchToAutofill(); - EXPECT_CALL(*controller()->GetView(), ModelChanged()).Times(1); + EXPECT_CALL(*controller()->GetView(), ModelChanged()); ui::MenuModel* shipping_model = controller()->MenuModelForSection(SECTION_SHIPPING); ASSERT_TRUE(!!shipping_model); @@ -1235,10 +1222,9 @@ TEST_F(AutofillDialogControllerTest, BillingVsShippingPhoneNumber) { TEST_F(AutofillDialogControllerTest, AcceptLegalDocuments) { EXPECT_CALL(*controller()->GetTestingWalletClient(), - AcceptLegalDocuments(_, _)).Times(1); - EXPECT_CALL(*controller()->GetTestingWalletClient(), - GetFullWallet(_)).Times(1); - EXPECT_CALL(*controller(), LoadRiskFingerprintData()).Times(1); + AcceptLegalDocuments(_, _)); + EXPECT_CALL(*controller()->GetTestingWalletClient(), GetFullWallet(_)); + EXPECT_CALL(*controller(), LoadRiskFingerprintData()); scoped_ptr<wallet::WalletItems> wallet_items = CompleteAndValidWalletItems(); wallet_items->AddLegalDocument(wallet::GetTestLegalDocument()); @@ -1345,10 +1331,9 @@ TEST_F(AutofillDialogControllerTest, SelectInstrument) { } TEST_F(AutofillDialogControllerTest, SaveAddress) { - EXPECT_CALL(*controller()->GetView(), ModelChanged()).Times(1); + EXPECT_CALL(*controller()->GetView(), ModelChanged()); EXPECT_CALL(*controller()->GetTestingWalletClient(), - SaveToWalletMock(testing::IsNull(), - testing::NotNull())).Times(1); + SaveToWalletMock(testing::IsNull(), testing::NotNull())); scoped_ptr<wallet::WalletItems> wallet_items = wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED); @@ -1368,11 +1353,11 @@ TEST_F(AutofillDialogControllerTest, SaveAddress) { } TEST_F(AutofillDialogControllerTest, SaveInstrument) { - EXPECT_CALL(*controller()->GetView(), ModelChanged()).Times(1); + EXPECT_CALL(*controller()->GetView(), ModelChanged()); EXPECT_CALL(*controller()->GetTestingWalletClient(), - SaveToWalletMock(testing::NotNull(), - testing::IsNull())).Times(1); + SaveToWalletMock(testing::NotNull(), testing::IsNull())); + FillCCBillingInputs(); scoped_ptr<wallet::WalletItems> wallet_items = wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED); wallet_items->AddAddress(wallet::GetTestShippingAddress()); @@ -1380,11 +1365,11 @@ TEST_F(AutofillDialogControllerTest, SaveInstrument) { } TEST_F(AutofillDialogControllerTest, SaveInstrumentWithInvalidInstruments) { - EXPECT_CALL(*controller()->GetView(), ModelChanged()).Times(1); + EXPECT_CALL(*controller()->GetView(), ModelChanged()); EXPECT_CALL(*controller()->GetTestingWalletClient(), - SaveToWalletMock(testing::NotNull(), - testing::IsNull())).Times(1); + SaveToWalletMock(testing::NotNull(), testing::IsNull())); + FillCCBillingInputs(); scoped_ptr<wallet::WalletItems> wallet_items = wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED); wallet_items->AddAddress(wallet::GetTestShippingAddress()); @@ -1394,12 +1379,12 @@ TEST_F(AutofillDialogControllerTest, SaveInstrumentWithInvalidInstruments) { TEST_F(AutofillDialogControllerTest, SaveInstrumentAndAddress) { EXPECT_CALL(*controller()->GetTestingWalletClient(), - SaveToWalletMock(testing::NotNull(), - testing::NotNull())).Times(1); + SaveToWalletMock(testing::NotNull(), testing::NotNull())); - controller()->OnDidGetWalletItems( - wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED)); - AcceptAndLoadFakeFingerprint(); + FillCCBillingInputs(); + scoped_ptr<wallet::WalletItems> wallet_items = + wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED); + SubmitWithWalletItems(wallet_items.Pass()); } MATCHER(IsUpdatingExistingData, "updating existing Wallet data") { @@ -1414,8 +1399,7 @@ MATCHER(UsesLocalBillingAddress, "uses the local billing address") { // matched shipping address, then a shipping address should be added. TEST_F(AutofillDialogControllerTest, BillingForShipping) { EXPECT_CALL(*controller()->GetTestingWalletClient(), - SaveToWalletMock(testing::IsNull(), - testing::NotNull())).Times(1); + SaveToWalletMock(testing::IsNull(), testing::NotNull())); controller()->OnDidGetWalletItems(CompleteAndValidWalletItems()); // Select "Same as billing" in the address menu. @@ -1482,8 +1466,7 @@ TEST_F(AutofillDialogControllerTest, SaveInstrumentSameAsBilling) { controller()->OnAccept(); EXPECT_CALL(*controller()->GetTestingWalletClient(), - SaveToWalletMock(testing::NotNull(), - UsesLocalBillingAddress())).Times(1); + SaveToWalletMock(testing::NotNull(), UsesLocalBillingAddress())); AcceptAndLoadFakeFingerprint(); } @@ -1491,7 +1474,7 @@ TEST_F(AutofillDialogControllerTest, CancelNoSave) { EXPECT_CALL(*controller()->GetTestingWalletClient(), SaveToWalletMock(_, _)).Times(0); - EXPECT_CALL(*controller()->GetView(), ModelChanged()).Times(1); + EXPECT_CALL(*controller()->GetView(), ModelChanged()); controller()->OnDidGetWalletItems( wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED)); @@ -1570,10 +1553,9 @@ TEST_F(AutofillDialogControllerTest, AddAutofillProfile) { } TEST_F(AutofillDialogControllerTest, VerifyCvv) { + EXPECT_CALL(*controller()->GetTestingWalletClient(), GetFullWallet(_)); EXPECT_CALL(*controller()->GetTestingWalletClient(), - GetFullWallet(_)).Times(1); - EXPECT_CALL(*controller()->GetTestingWalletClient(), - AuthenticateInstrument(_, _)).Times(1); + AuthenticateInstrument(_, _)); SubmitWithWalletItems(CompleteAndValidWalletItems()); @@ -1587,7 +1569,10 @@ TEST_F(AutofillDialogControllerTest, VerifyCvv) { controller()->SuggestionStateForSection(SECTION_CC_BILLING); EXPECT_TRUE(suggestion_state.extra_text.empty()); - controller()->OnDidGetFullWallet(CreateFullWallet("verify_cvv")); + controller()->OnDidGetFullWallet( + wallet::GetTestFullWalletWithRequiredActions( + std::vector<wallet::RequiredAction>(1, wallet::VERIFY_CVV))); + ASSERT_TRUE(controller()->IsSubmitPausedOn(wallet::VERIFY_CVV)); EXPECT_FALSE( NotificationsOfType(DialogNotification::REQUIRED_ACTION).empty()); @@ -1606,8 +1591,7 @@ TEST_F(AutofillDialogControllerTest, VerifyCvv) { } TEST_F(AutofillDialogControllerTest, ErrorDuringSubmit) { - EXPECT_CALL(*controller()->GetTestingWalletClient(), - GetFullWallet(_)).Times(1); + EXPECT_CALL(*controller()->GetTestingWalletClient(), GetFullWallet(_)); SubmitWithWalletItems(CompleteAndValidWalletItems()); @@ -1621,11 +1605,12 @@ TEST_F(AutofillDialogControllerTest, ErrorDuringSubmit) { } TEST_F(AutofillDialogControllerTest, ErrorDuringVerifyCvv) { - EXPECT_CALL(*controller()->GetTestingWalletClient(), - GetFullWallet(_)).Times(1); + EXPECT_CALL(*controller()->GetTestingWalletClient(), GetFullWallet(_)); SubmitWithWalletItems(CompleteAndValidWalletItems()); - controller()->OnDidGetFullWallet(CreateFullWallet("verify_cvv")); + controller()->OnDidGetFullWallet( + wallet::GetTestFullWalletWithRequiredActions( + std::vector<wallet::RequiredAction>(1, wallet::VERIFY_CVV))); ASSERT_TRUE(controller()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK)); ASSERT_TRUE(controller()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_CANCEL)); @@ -1654,7 +1639,7 @@ TEST_F(AutofillDialogControllerTest, WalletServerSideValidation) { wallet::FormFieldError(wallet::FormFieldError::INVALID_POSTAL_CODE, wallet::FormFieldError::SHIPPING_ADDRESS)); - EXPECT_CALL(*controller()->GetView(), UpdateForErrors()).Times(1); + EXPECT_CALL(*controller()->GetView(), UpdateForErrors()); controller()->OnDidSaveToWallet(std::string(), std::string(), required_actions, @@ -1918,7 +1903,7 @@ TEST_F(AutofillDialogControllerTest, SaveDetailsInChrome) { TEST_F(AutofillDialogControllerTest, UpgradeMinimalAddress) { // A minimal address being selected should trigger error validation in the // view. Called once for each incomplete suggestion. - EXPECT_CALL(*controller()->GetView(), UpdateForErrors()).Times(1); + EXPECT_CALL(*controller()->GetView(), UpdateForErrors()); scoped_ptr<wallet::WalletItems> wallet_items = wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED); @@ -1939,8 +1924,7 @@ TEST_F(AutofillDialogControllerTest, UpgradeMinimalAddress) { TEST_F(AutofillDialogControllerTest, RiskNeverLoadsWithPendingLegalDocuments) { EXPECT_CALL(*controller(), LoadRiskFingerprintData()).Times(0); - scoped_ptr<wallet::WalletItems> wallet_items = - wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED); + scoped_ptr<wallet::WalletItems> wallet_items = CompleteAndValidWalletItems(); wallet_items->AddLegalDocument(wallet::GetTestLegalDocument()); controller()->OnDidGetWalletItems(wallet_items.Pass()); controller()->OnAccept(); @@ -1949,13 +1933,12 @@ TEST_F(AutofillDialogControllerTest, RiskNeverLoadsWithPendingLegalDocuments) { TEST_F(AutofillDialogControllerTest, RiskLoadsAfterAcceptingLegalDocuments) { EXPECT_CALL(*controller(), LoadRiskFingerprintData()).Times(0); - scoped_ptr<wallet::WalletItems> wallet_items = - wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED); + scoped_ptr<wallet::WalletItems> wallet_items = CompleteAndValidWalletItems(); wallet_items->AddLegalDocument(wallet::GetTestLegalDocument()); controller()->OnDidGetWalletItems(wallet_items.Pass()); testing::Mock::VerifyAndClear(controller()); - EXPECT_CALL(*controller(), LoadRiskFingerprintData()).Times(1); + EXPECT_CALL(*controller(), LoadRiskFingerprintData()); controller()->OnAccept(); @@ -2037,8 +2020,7 @@ TEST_F(AutofillDialogControllerTest, ShippingSectionCanBeHiddenForWallet) { EXPECT_FALSE(controller()->SectionIsActive(SECTION_SHIPPING)); EXPECT_FALSE(controller()->IsShippingAddressRequired()); - EXPECT_CALL(*controller()->GetTestingWalletClient(), - GetFullWallet(_)).Times(1); + EXPECT_CALL(*controller()->GetTestingWalletClient(), GetFullWallet(_)); scoped_ptr<wallet::WalletItems> wallet_items = wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED); wallet_items->AddInstrument(wallet::GetTestMaskedInstrument()); @@ -2117,10 +2099,11 @@ TEST_F(AutofillDialogControllerTest, ChooseAnotherInstrumentOrAddress) { EXPECT_EQ(0U, NotificationsOfType( DialogNotification::REQUIRED_ACTION).size()); - EXPECT_CALL(*controller()->GetTestingWalletClient(), - GetWalletItems()).Times(1); + EXPECT_CALL(*controller()->GetTestingWalletClient(), GetWalletItems()); controller()->OnDidGetFullWallet( - CreateFullWallet("choose_another_instrument_or_address")); + wallet::GetTestFullWalletWithRequiredActions( + std::vector<wallet::RequiredAction>( + 1, wallet::CHOOSE_ANOTHER_INSTRUMENT_OR_ADDRESS))); EXPECT_EQ(1U, NotificationsOfType( DialogNotification::REQUIRED_ACTION).size()); controller()->OnDidGetWalletItems(CompleteAndValidWalletItems()); @@ -2131,25 +2114,23 @@ TEST_F(AutofillDialogControllerTest, ChooseAnotherInstrumentOrAddress) { } TEST_F(AutofillDialogControllerTest, NewCardBubbleShown) { - EXPECT_CALL(*test_generated_bubble_controller(), SetupAndShow(_, _)).Times(0); - SwitchToAutofill(); FillCreditCardInputs(); controller()->OnAccept(); controller()->ViewClosed(); EXPECT_EQ(1, mock_new_card_bubble_controller()->bubbles_shown()); + EXPECT_EQ(0, test_generated_bubble_controller()->bubbles_shown()); } TEST_F(AutofillDialogControllerTest, GeneratedCardBubbleShown) { - EXPECT_CALL(*test_generated_bubble_controller(), SetupAndShow(_, _)).Times(1); - SubmitWithWalletItems(CompleteAndValidWalletItems()); controller()->OnDidGetFullWallet(wallet::GetTestFullWallet()); controller()->ForceFinishSubmit(); controller()->ViewClosed(); EXPECT_EQ(0, mock_new_card_bubble_controller()->bubbles_shown()); + EXPECT_EQ(1, test_generated_bubble_controller()->bubbles_shown()); } // Verify that new Wallet data is fetched when the user switches away from the @@ -2457,7 +2438,7 @@ TEST_F(AutofillDialogControllerTest, SaveCreditCardIncludesName_WithBilling) { TestPersonalDataManager* test_pdm = controller()->GetTestingManager(); AutofillProfile test_profile(test::GetVerifiedProfile()); - EXPECT_CALL(*controller()->GetView(), ModelChanged()).Times(1); + EXPECT_CALL(*controller()->GetView(), ModelChanged()); test_pdm->AddTestingProfile(&test_profile); ASSERT_TRUE(controller()->MenuModelForSection(SECTION_BILLING)); @@ -2474,4 +2455,78 @@ TEST_F(AutofillDialogControllerTest, SaveCreditCardIncludesName_WithBilling) { controller()->ViewClosed(); } +TEST_F(AutofillDialogControllerTest, InputEditability) { + // Empty wallet items: all fields are editable. + scoped_ptr<wallet::WalletItems> items = + wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED); + controller()->OnDidGetWalletItems(items.Pass()); + + DialogSection sections[] = { SECTION_CC_BILLING, SECTION_SHIPPING }; + for (size_t i = 0; i < arraysize(sections); ++i) { + const DetailInputs& inputs = + controller()->RequestedFieldsForSection(sections[i]); + for (size_t j = 0; j < inputs.size(); ++j) { + EXPECT_TRUE(controller()->InputIsEditable(inputs[j], sections[i])); + } + } + + // Expired instrument: CC number + CVV are not editable. + items = wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED); + scoped_ptr<wallet::WalletItems::MaskedInstrument> expired_instrument = + wallet::GetTestMaskedInstrumentExpired(); + items->AddInstrument(expired_instrument.Pass()); + controller()->OnDidGetWalletItems(items.Pass()); + EXPECT_TRUE(controller()->IsEditingExistingData(SECTION_CC_BILLING)); + + const DetailInputs& inputs = + controller()->RequestedFieldsForSection(SECTION_CC_BILLING); + DetailOutputMap outputs; + CopyInitialValues(inputs, &outputs); + controller()->GetView()->SetUserInput(SECTION_CC_BILLING, outputs); + + for (size_t i = 0; i < arraysize(sections); ++i) { + const DetailInputs& inputs = + controller()->RequestedFieldsForSection(sections[i]); + for (size_t j = 0; j < inputs.size(); ++j) { + if (inputs[j].type == CREDIT_CARD_NUMBER || + inputs[j].type == CREDIT_CARD_VERIFICATION_CODE) { + EXPECT_FALSE(controller()->InputIsEditable(inputs[j], sections[i])); + } else { + EXPECT_TRUE(controller()->InputIsEditable(inputs[j], sections[i])); + } + } + } + + // User changes the billing address; same story. + SetOutputValue(inputs, ADDRESS_BILLING_ZIP, ASCIIToUTF16("77025"), &outputs); + controller()->GetView()->SetUserInput(SECTION_CC_BILLING, outputs); + for (size_t i = 0; i < arraysize(sections); ++i) { + const DetailInputs& inputs = + controller()->RequestedFieldsForSection(sections[i]); + for (size_t j = 0; j < inputs.size(); ++j) { + if (inputs[j].type == CREDIT_CARD_NUMBER || + inputs[j].type == CREDIT_CARD_VERIFICATION_CODE) { + EXPECT_FALSE(controller()->InputIsEditable(inputs[j], sections[i])); + } else { + EXPECT_TRUE(controller()->InputIsEditable(inputs[j], sections[i])); + } + } + } + + // User changes a detail of the CC itself (expiration date), CVV is now + // editable (and mandatory). + SetOutputValue(inputs, CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("06"), &outputs); + controller()->GetView()->SetUserInput(SECTION_CC_BILLING, outputs); + for (size_t i = 0; i < arraysize(sections); ++i) { + const DetailInputs& inputs = + controller()->RequestedFieldsForSection(sections[i]); + for (size_t j = 0; j < inputs.size(); ++j) { + if (inputs[j].type == CREDIT_CARD_NUMBER) + EXPECT_FALSE(controller()->InputIsEditable(inputs[j], sections[i])); + else + EXPECT_TRUE(controller()->InputIsEditable(inputs[j], sections[i])); + } + } +} + } // namespace autofill diff --git a/chrome/browser/ui/autofill/autofill_dialog_types.h b/chrome/browser/ui/autofill/autofill_dialog_types.h index a31b647885..a06f5b0cf0 100644 --- a/chrome/browser/ui/autofill/autofill_dialog_types.h +++ b/chrome/browser/ui/autofill/autofill_dialog_types.h @@ -45,10 +45,6 @@ struct DetailInput { // When non-empty, indicates the starting value for this input. This will be // used when the user is editing existing data. string16 initial_value; - - // Whether the input is able to be edited (e.g. text changed in textfields, - // index changed in comboboxes). - bool editable; }; // Determines whether |input| and |field| match. diff --git a/chrome/browser/ui/autofill/autofill_dialog_view_delegate.h b/chrome/browser/ui/autofill/autofill_dialog_view_delegate.h index bcb33c65d9..eee85b9d99 100644 --- a/chrome/browser/ui/autofill/autofill_dialog_view_delegate.h +++ b/chrome/browser/ui/autofill/autofill_dialog_view_delegate.h @@ -119,11 +119,6 @@ class AutofillDialogViewDelegate { // Returns the current state of suggestions for |section|. virtual SuggestionState SuggestionStateForSection(DialogSection section) = 0; - // TODO(groby): Remove this deprecated method after Mac starts using - // IconsForFields. http://crbug.com/292876 - virtual gfx::Image IconForField(ServerFieldType type, - const string16& user_input) const = 0; - // Returns the icons to be displayed along with the given |user_inputs| in a // section. virtual FieldIconMap IconsForFields( @@ -136,6 +131,10 @@ class AutofillDialogViewDelegate { // Returns a tooltip for the given field, or an empty string if none exists. virtual string16 TooltipForField(ServerFieldType type) const = 0; + // Whether a particular DetailInput in |section| should be edited or not. + virtual bool InputIsEditable(const DetailInput& input, + DialogSection section) = 0; + // Decides whether input of |value| is valid for a field of type |type|. If // valid, the returned string will be empty. Otherwise it will contain an // error message. diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc index 3599ca469b..c7c1751dc4 100644 --- a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc +++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc @@ -30,9 +30,6 @@ namespace { // Used to indicate that no line is currently selected by the user. const int kNoSelection = -1; -// Size difference between name and subtext in pixels. -const int kLabelFontSizeDelta = -2; - // The vertical height of each row in pixels. const size_t kRowHeight = 24; @@ -40,6 +37,9 @@ const size_t kRowHeight = 24; const size_t kSeparatorHeight = 1; #if !defined(OS_ANDROID) +// Size difference between name and subtext in pixels. +const int kLabelFontSizeDelta = -2; + const size_t kNamePadding = AutofillPopupView::kNamePadding; const size_t kIconPadding = AutofillPopupView::kIconPadding; const size_t kEndPadding = AutofillPopupView::kEndPadding; diff --git a/chrome/browser/ui/autofill/generated_credit_card_bubble_controller.h b/chrome/browser/ui/autofill/generated_credit_card_bubble_controller.h index bdb1614de8..4b3ee00dc6 100644 --- a/chrome/browser/ui/autofill/generated_credit_card_bubble_controller.h +++ b/chrome/browser/ui/autofill/generated_credit_card_bubble_controller.h @@ -116,11 +116,15 @@ class GeneratedCreditCardBubbleController // will show in the omnibox. bool ShouldDisplayBubbleInitially() const; + // Exposed for testing. + base::string16 fronting_card_name() const { return fronting_card_name_; } + base::string16 backing_card_name() const { return backing_card_name_; } + // Generates the correct bubble text and text highlighting ranges and shows a // bubble to educate the user about generated (fronting) cards and how they // are used to bill their original (backing) card. Exposed for testing. - virtual void SetupAndShow(const base::string16& backing_card_name, - const base::string16& fronting_card_name); + virtual void SetupAndShow(const base::string16& fronting_card_name, + const base::string16& backing_card_name); private: friend class diff --git a/chrome/browser/ui/autofill/generated_credit_card_bubble_controller_unittest.cc b/chrome/browser/ui/autofill/generated_credit_card_bubble_controller_unittest.cc index e8370bcd81..42f9f50904 100644 --- a/chrome/browser/ui/autofill/generated_credit_card_bubble_controller_unittest.cc +++ b/chrome/browser/ui/autofill/generated_credit_card_bubble_controller_unittest.cc @@ -10,6 +10,7 @@ #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/ui/autofill/generated_credit_card_bubble_controller.h" +#include "chrome/browser/ui/autofill/test_generated_credit_card_bubble_controller.h" #include "chrome/browser/ui/autofill/test_generated_credit_card_bubble_view.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/testing_profile.h" @@ -42,39 +43,6 @@ base::string16 RangeOfString(const base::string16& string, return string.substr(range.start(), range.end() - range.start()); } -class TestGeneratedCreditCardBubbleController - : public GeneratedCreditCardBubbleController { - public: - explicit TestGeneratedCreditCardBubbleController( - content::WebContents* contents) - : GeneratedCreditCardBubbleController(contents) { - contents->SetUserData(UserDataKey(), this); - } - - virtual ~TestGeneratedCreditCardBubbleController() {} - - bool IsInstalled() const { - return web_contents()->GetUserData(UserDataKey()) == this; - } - - TestGeneratedCreditCardBubbleView* GetTestingBubble() { - return static_cast<TestGeneratedCreditCardBubbleView*>( - GeneratedCreditCardBubbleController::bubble().get()); - } - - protected: - virtual base::WeakPtr<GeneratedCreditCardBubbleView> CreateBubble() OVERRIDE { - return TestGeneratedCreditCardBubbleView::Create(GetWeakPtr()); - } - - virtual bool CanShow() const OVERRIDE { - return true; - } - - private: - DISALLOW_COPY_AND_ASSIGN(TestGeneratedCreditCardBubbleController); -}; - class GeneratedCreditCardBubbleControllerTest : public testing::Test { public: GeneratedCreditCardBubbleControllerTest() diff --git a/chrome/browser/ui/autofill/mock_autofill_dialog_view_delegate.cc b/chrome/browser/ui/autofill/mock_autofill_dialog_view_delegate.cc index e9b4f5c2e3..57e6093080 100644 --- a/chrome/browser/ui/autofill/mock_autofill_dialog_view_delegate.cc +++ b/chrome/browser/ui/autofill/mock_autofill_dialog_view_delegate.cc @@ -29,6 +29,9 @@ MockAutofillDialogViewDelegate::MockAutofillDialogViewDelegate() { gfx::Image(), string16(), gfx::Image())); + DefaultValue<FieldIconMap>::Set(FieldIconMap()); + DefaultValue<std::vector<DialogNotification> >::Set( + std::vector<DialogNotification>()); // SECTION_CC *must* have a CREDIT_CARD_VERIFICATION_CODE field. const DetailInput kCreditCardInputs[] = { @@ -49,6 +52,11 @@ MockAutofillDialogViewDelegate::MockAutofillDialogViewDelegate() { .WillByDefault(Return(false)); } +void MockAutofillDialogViewDelegate::SetWebContents( + content::WebContents* contents) { + testing::DefaultValue<content::WebContents*>::Set(contents); +} + MockAutofillDialogViewDelegate::~MockAutofillDialogViewDelegate() { using testing::DefaultValue; @@ -57,6 +65,9 @@ MockAutofillDialogViewDelegate::~MockAutofillDialogViewDelegate() { DefaultValue<ValidityMessages>::Clear(); DefaultValue<string16>::Clear(); DefaultValue<const DetailInputs&>::Clear(); + DefaultValue<FieldIconMap>::Clear(); + DefaultValue<std::vector<DialogNotification> >::Clear(); + DefaultValue<content::WebContents*>::Clear(); } } // namespace autofill diff --git a/chrome/browser/ui/autofill/mock_autofill_dialog_view_delegate.h b/chrome/browser/ui/autofill/mock_autofill_dialog_view_delegate.h index 99ccfc5e0a..3eb153d05f 100644 --- a/chrome/browser/ui/autofill/mock_autofill_dialog_view_delegate.h +++ b/chrome/browser/ui/autofill/mock_autofill_dialog_view_delegate.h @@ -49,23 +49,21 @@ class MockAutofillDialogViewDelegate : public AutofillDialogViewDelegate { MOCK_METHOD1(SuggestionStateForSection, SuggestionState(DialogSection)); MOCK_METHOD1(EditClickedForSection, void(DialogSection section)); MOCK_METHOD1(EditCancelledForSection, void(DialogSection section)); - // TODO(groby): Remove this deprecated method after Mac starts using - // IconsForFields. http://crbug.com/292876 - MOCK_CONST_METHOD2(IconForField, - gfx::Image(ServerFieldType, const string16&)); MOCK_CONST_METHOD1(IconsForFields, FieldIconMap(const FieldValueMap&)); MOCK_CONST_METHOD1(FieldControlsIcons, bool(ServerFieldType)); MOCK_CONST_METHOD1(TooltipForField, base::string16(ServerFieldType)); + MOCK_METHOD2(InputIsEditable, bool(const DetailInput& input, + DialogSection section)); MOCK_METHOD3(InputValidityMessage, string16(DialogSection, ServerFieldType, const string16&)); MOCK_METHOD2(InputsAreValid, ValidityMessages(DialogSection, const DetailOutputMap&)); - MOCK_METHOD6(UserEditedOrActivatedInput,void(DialogSection, - const DetailInput*, - gfx::NativeView, - const gfx::Rect&, - const string16&, - bool was_edit)); + MOCK_METHOD6(UserEditedOrActivatedInput, void(DialogSection, + const DetailInput*, + gfx::NativeView, + const gfx::Rect&, + const string16&, + bool was_edit)); MOCK_METHOD1(HandleKeyPressEventInInput, bool(const content::NativeWebKeyboardEvent& event)); MOCK_METHOD0(FocusMoved, void()); @@ -83,6 +81,9 @@ class MockAutofillDialogViewDelegate : public AutofillDialogViewDelegate { MOCK_METHOD0(profile, Profile*()); MOCK_METHOD0(GetWebContents, content::WebContents*()); + // Set which web contents initiated showing the dialog. + void SetWebContents(content::WebContents* contents); + private: DetailInputs default_inputs_; DetailInputs cc_default_inputs_; // Default inputs for SECTION_CC. diff --git a/chrome/browser/ui/autofill/test_generated_credit_card_bubble_controller.cc b/chrome/browser/ui/autofill/test_generated_credit_card_bubble_controller.cc new file mode 100644 index 0000000000..9c4b4f7152 --- /dev/null +++ b/chrome/browser/ui/autofill/test_generated_credit_card_bubble_controller.cc @@ -0,0 +1,50 @@ +// Copyright 2013 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/ui/autofill/test_generated_credit_card_bubble_controller.h" + +#include "chrome/browser/ui/autofill/test_generated_credit_card_bubble_view.h" +#include "content/public/browser/web_contents.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace autofill { + +TestGeneratedCreditCardBubbleController:: + TestGeneratedCreditCardBubbleController(content::WebContents* contents) + : GeneratedCreditCardBubbleController(contents), + bubbles_shown_(0) { + contents->SetUserData(UserDataKey(), this); +} + +TestGeneratedCreditCardBubbleController:: + ~TestGeneratedCreditCardBubbleController() {} + +bool TestGeneratedCreditCardBubbleController::IsInstalled() const { + return web_contents()->GetUserData(UserDataKey()) == this; +} + +TestGeneratedCreditCardBubbleView* TestGeneratedCreditCardBubbleController:: + GetTestingBubble() { + return static_cast<TestGeneratedCreditCardBubbleView*>( + GeneratedCreditCardBubbleController::bubble().get()); +} + +base::WeakPtr<GeneratedCreditCardBubbleView> + TestGeneratedCreditCardBubbleController::CreateBubble() { + return TestGeneratedCreditCardBubbleView::Create(GetWeakPtr()); +} + +bool TestGeneratedCreditCardBubbleController::CanShow() const { + return true; +} + +void TestGeneratedCreditCardBubbleController::SetupAndShow( + const base::string16& fronting_card_name, + const base::string16& backing_card_name) { + GeneratedCreditCardBubbleController::SetupAndShow(fronting_card_name, + backing_card_name); + ++bubbles_shown_; +} + +} // namespace autofill diff --git a/chrome/browser/ui/autofill/test_generated_credit_card_bubble_controller.h b/chrome/browser/ui/autofill/test_generated_credit_card_bubble_controller.h new file mode 100644 index 0000000000..eef69f442b --- /dev/null +++ b/chrome/browser/ui/autofill/test_generated_credit_card_bubble_controller.h @@ -0,0 +1,51 @@ +// Copyright 2013 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_UI_AUTOFILL_TEST_GENERATED_CREDIT_CARD_BUBBLE_CONTROLLER_H_ +#define CHROME_BROWSER_UI_AUTOFILL_TEST_GENERATED_CREDIT_CARD_BUBBLE_CONTROLLER_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "chrome/browser/ui/autofill/generated_credit_card_bubble_controller.h" + +namespace autofill { + +class TestGeneratedCreditCardBubbleView; + +class TestGeneratedCreditCardBubbleController + : public GeneratedCreditCardBubbleController { + public: + explicit TestGeneratedCreditCardBubbleController( + content::WebContents* contents); + virtual ~TestGeneratedCreditCardBubbleController(); + + // Whether this controller is installed on |web_contents()|. + bool IsInstalled() const; + + // Get an invisible, dumb, test-only bubble. + TestGeneratedCreditCardBubbleView* GetTestingBubble(); + + // Made public for testing. + using GeneratedCreditCardBubbleController::fronting_card_name; + using GeneratedCreditCardBubbleController::backing_card_name; + + int bubbles_shown() const { return bubbles_shown_; } + + protected: + // GeneratedCreditCardBubbleController: + virtual base::WeakPtr<GeneratedCreditCardBubbleView> CreateBubble() OVERRIDE; + virtual bool CanShow() const OVERRIDE; + virtual void SetupAndShow(const base::string16& fronting_card_name, + const base::string16& backing_card_name) OVERRIDE; + + private: + // How many bubbles have been shown via this controller. + int bubbles_shown_; + + DISALLOW_COPY_AND_ASSIGN(TestGeneratedCreditCardBubbleController); +}; + +} // namespace autofill + +#endif // CHROME_BROWSER_UI_AUTOFILL_TEST_GENERATED_CREDIT_CARD_BUBBLE_CONTROLLER_H_ diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc index 799f03cceb..e56c71d2a3 100644 --- a/chrome/browser/ui/browser.cc +++ b/chrome/browser/ui/browser.cc @@ -697,7 +697,8 @@ Browser::DownloadClosePreventionType Browser::OkToCloseWithInProgressDownloads( if (!g_browser_process->profile_manager()) return DOWNLOAD_CLOSE_OK; - int total_download_count = DownloadService::DownloadCountAllProfiles(); + int total_download_count = + DownloadService::NonMaliciousDownloadCountAllProfiles(); if (total_download_count == 0) return DOWNLOAD_CLOSE_OK; // No downloads; can definitely close. @@ -731,9 +732,9 @@ Browser::DownloadClosePreventionType Browser::OkToCloseWithInProgressDownloads( DownloadService* download_service = DownloadServiceFactory::GetForBrowserContext(profile()); if ((profile_window_count == 0) && - (download_service->DownloadCount() > 0) && + (download_service->NonMaliciousDownloadCount() > 0) && profile()->IsOffTheRecord()) { - *num_downloads_blocking = download_service->DownloadCount(); + *num_downloads_blocking = download_service->NonMaliciousDownloadCount(); return DOWNLOAD_CLOSE_LAST_WINDOW_IN_INCOGNITO_PROFILE; } diff --git a/chrome/browser/ui/browser_close_browsertest.cc b/chrome/browser/ui/browser_close_browsertest.cc index 4f2b145036..cbc544c5bf 100644 --- a/chrome/browser/ui/browser_close_browsertest.cc +++ b/chrome/browser/ui/browser_close_browsertest.cc @@ -255,6 +255,8 @@ class BrowserCloseTest : public InProcessBrowserTest { // an assertion has failed and the test should be aborted. bool ExecuteDownloadCloseCheckCase(size_t i) { const DownloadsCloseCheckCase& check_case(download_close_check_cases[i]); + SCOPED_TRACE(testing::Message() << "Case" << i + << ": " << check_case.DebugString()); // Test invariant: so that we don't actually try and close the browser, // we always enter the function with a single browser window open on the @@ -270,18 +272,14 @@ class BrowserCloseTest : public InProcessBrowserTest { return false; Browser* entry_browser = FirstUnclosedBrowser(); - EXPECT_EQ(first_profile_, entry_browser->profile()) - << "Case" << i - << ": " << check_case.DebugString(); + EXPECT_EQ(first_profile_, entry_browser->profile()); if (first_profile_ != entry_browser->profile()) return false; - int total_download_count = DownloadService::DownloadCountAllProfiles(); - EXPECT_EQ(0, total_download_count) - << "Case " << i - << ": " << check_case.DebugString(); + int total_download_count = + DownloadService::NonMaliciousDownloadCountAllProfiles(); + EXPECT_EQ(0, total_download_count); if (0 != total_download_count) return false; - Profile* first_profile_incognito = first_profile_->GetOffTheRecordProfile(); Profile* second_profile_incognito = second_profile_->GetOffTheRecordProfile(); @@ -356,12 +354,9 @@ class BrowserCloseTest : public InProcessBrowserTest { Browser::DownloadClosePreventionType type = browser_to_probe->OkToCloseWithInProgressDownloads( &num_downloads_blocking); - EXPECT_EQ(check_case.type, type) << "Case " << i - << ": " << check_case.DebugString(); + EXPECT_EQ(check_case.type, type); if (type != Browser::DOWNLOAD_CLOSE_OK) - EXPECT_EQ(check_case.num_blocking, num_downloads_blocking) - << "Case " << i - << ": " << check_case.DebugString(); + EXPECT_EQ(check_case.num_blocking, num_downloads_blocking); // Release all the downloads. CompleteAllDownloads(browser_to_probe); diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc index 2afe9bec2f..ff9cdefba8 100644 --- a/chrome/browser/ui/browser_command_controller.cc +++ b/chrome/browser/ui/browser_command_controller.cc @@ -28,6 +28,7 @@ #include "chrome/browser/ui/chrome_pages.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/tabs/tab_strip_model_utils.h" +#include "chrome/browser/ui/webui/inspect_ui.h" #include "chrome/common/content_restriction.h" #include "chrome/common/pref_names.h" #include "chrome/common/profiling.h" @@ -55,6 +56,12 @@ #include "chrome/browser/ui/ash/ash_util.h" #endif +#if defined(OS_CHROMEOS) +#include "ash/session_state_delegate.h" +#include "ash/shell.h" +#include "chrome/browser/ui/ash/multi_user_window_manager.h" +#endif + using content::NavigationEntry; using content::NavigationController; using content::WebContents; @@ -452,6 +459,21 @@ void BrowserCommandController::ExecuteCommandWithDisposition( // mechanism to pass accelerators back into Ash. http://crbug.com/285308 #endif +#if defined(OS_CHROMEOS) + case IDC_VISIT_DESKTOP_OF_LRU_USER_2: + case IDC_VISIT_DESKTOP_OF_LRU_USER_3: { + // When running the multi user mode on Chrome OS, windows can "visit" + // another user's desktop. + const std::string& user_id = + ash::Shell::GetInstance()->session_state_delegate()->GetUserID( + IDC_VISIT_DESKTOP_OF_LRU_USER_2 == id ? 1 : 2); + chrome::MultiUserWindowManager::GetInstance()->ShowWindowForUser( + browser_->window()->GetNativeWindow(), + user_id); + break; + } +#endif + #if defined(OS_WIN) // Windows 8 specific commands. case IDC_METRO_SNAP_ENABLE: @@ -629,6 +651,9 @@ void BrowserCommandController::ExecuteCommandWithDisposition( case IDC_DEV_TOOLS_CONSOLE: ToggleDevToolsWindow(browser_, DEVTOOLS_TOGGLE_ACTION_SHOW_CONSOLE); break; + case IDC_DEV_TOOLS_DEVICES: + InspectUI::InspectDevices(browser_); + break; case IDC_DEV_TOOLS_INSPECT: ToggleDevToolsWindow(browser_, DEVTOOLS_TOGGLE_ACTION_INSPECT); break; @@ -845,6 +870,10 @@ void BrowserCommandController::InitCommandState() { #if defined(USE_ASH) command_updater_.UpdateCommandEnabled(IDC_MINIMIZE_WINDOW, true); #endif +#if defined(OS_CHROMEOS) + command_updater_.UpdateCommandEnabled(IDC_VISIT_DESKTOP_OF_LRU_USER_2, true); + command_updater_.UpdateCommandEnabled(IDC_VISIT_DESKTOP_OF_LRU_USER_3, true); +#endif // Page-related commands command_updater_.UpdateCommandEnabled(IDC_EMAIL_PAGE_LOCATION, true); @@ -1079,6 +1108,8 @@ void BrowserCommandController::UpdateCommandsForDevTools() { dev_tools_enabled); command_updater_.UpdateCommandEnabled(IDC_DEV_TOOLS_CONSOLE, dev_tools_enabled); + command_updater_.UpdateCommandEnabled(IDC_DEV_TOOLS_DEVICES, + dev_tools_enabled); command_updater_.UpdateCommandEnabled(IDC_DEV_TOOLS_INSPECT, dev_tools_enabled); command_updater_.UpdateCommandEnabled(IDC_DEV_TOOLS_TOGGLE, diff --git a/chrome/browser/ui/browser_dialogs.h b/chrome/browser/ui/browser_dialogs.h index c3ad4004fc..72af390f99 100644 --- a/chrome/browser/ui/browser_dialogs.h +++ b/chrome/browser/ui/browser_dialogs.h @@ -77,6 +77,14 @@ void ShowCreateWebAppShortcutsDialog(gfx::NativeWindow parent_window, content::WebContents* web_contents); #endif +// Shows the create chrome app shortcut dialog box. +// On Mac, this creates a shortcut without prompting. +// |close_callback| may be null. +void ShowCreateChromeAppShortcutsDialog(gfx::NativeWindow parent_window, + Profile* profile, + const extensions::Extension* app, + const base::Closure& close_callback); + // Shows a color chooser that reports to the given WebContents. content::ColorChooser* ShowColorChooser(content::WebContents* web_contents, SkColor initial_color); diff --git a/chrome/browser/ui/browser_focus_uitest.cc b/chrome/browser/ui/browser_focus_uitest.cc index 869f43d047..4a6f59dc98 100644 --- a/chrome/browser/ui/browser_focus_uitest.cc +++ b/chrome/browser/ui/browser_focus_uitest.cc @@ -871,6 +871,19 @@ IN_PROC_BROWSER_TEST_F(BrowserFocusTest, DISABLED_FocusOnReloadCrashedTab) { ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER)); } +// Tests that focus goes to frame after crashed tab. +// TODO(shrikant): Find out where the focus should be deterministically. +// Currently focused_view after crash seem to be non null in debug mode +// (invalidated pointer 0xcccccc). +IN_PROC_BROWSER_TEST_F(BrowserFocusTest, DISABLED_FocusAfterCrashedTab) { + ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); + ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); + + content::CrashTab(browser()->tab_strip_model()->GetActiveWebContents()); + + ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER)); +} + // Tests that when a new tab is opened from the omnibox, the focus is moved from // the omnibox for the current tab. IN_PROC_BROWSER_TEST_F(BrowserFocusTest, diff --git a/chrome/browser/ui/browser_navigator.cc b/chrome/browser/ui/browser_navigator.cc index 4c25a8edcb..7726b996d8 100644 --- a/chrome/browser/ui/browser_navigator.cc +++ b/chrome/browser/ui/browser_navigator.cc @@ -354,14 +354,16 @@ content::WebContents* CreateTargetContents(const chrome::NavigateParams& params, return target_contents; } -// If a prerendered page exists for |url|, replace the page at |target_contents| -// with it. -bool SwapInPrerender(WebContents* target_contents, const GURL& url) { +// If a prerendered page exists for |url|, replace the page at +// |params->target_contents| with it and update to point to the swapped-in +// WebContents. +bool SwapInPrerender(const GURL& url, chrome::NavigateParams* params) { prerender::PrerenderManager* prerender_manager = prerender::PrerenderManagerFactory::GetForProfile( - Profile::FromBrowserContext(target_contents->GetBrowserContext())); + Profile::FromBrowserContext( + params->target_contents->GetBrowserContext())); return prerender_manager && - prerender_manager->MaybeUsePrerenderedPage(target_contents, url); + prerender_manager->MaybeUsePrerenderedPage(url, params); } bool SwapInInstantNTP(chrome::NavigateParams* params, @@ -540,8 +542,8 @@ void Navigate(NavigateParams* params) { // Check if this is a singleton tab that already exists int singleton_index = chrome::GetIndexOfSingletonTab(params); - // Did we use Instant's NTP contents? - bool swapped_in_instant = false; + // Did we use Instant's NTP contents or a prerender? + bool swapped_in = false; // If no target WebContents was specified, we need to construct one if // we are supposed to target a new tab; unless it's a singleton that already @@ -557,8 +559,8 @@ void Navigate(NavigateParams* params) { } if (params->disposition != CURRENT_TAB) { - swapped_in_instant = SwapInInstantNTP(params, url, NULL); - if (!swapped_in_instant) + swapped_in = SwapInInstantNTP(params, url, NULL); + if (!swapped_in) params->target_contents = CreateTargetContents(*params, url); // This function takes ownership of |params->target_contents| until it @@ -568,20 +570,21 @@ void Navigate(NavigateParams* params) { // ... otherwise if we're loading in the current tab, the target is the // same as the source. DCHECK(params->source_contents); - swapped_in_instant = SwapInInstantNTP(params, url, - params->source_contents); - if (!swapped_in_instant) + swapped_in = SwapInInstantNTP(params, url, params->source_contents); + if (!swapped_in) params->target_contents = params->source_contents; DCHECK(params->target_contents); + // Prerender expects |params->target_contents| to be attached to a browser + // window, so only call for CURRENT_TAB navigations. (Others are currently + // unsupported because of session storage namespaces anyway.) + if (!swapped_in) + swapped_in = SwapInPrerender(url, params); } if (user_initiated) params->target_contents->UserGestureDone(); - if (!swapped_in_instant) { - if (SwapInPrerender(params->target_contents, url)) - return; - + if (!swapped_in) { // Try to handle non-navigational URLs that popup dialogs and such, these // should not actually navigate. if (!HandleNonNavigationAboutURL(url)) { @@ -607,7 +610,7 @@ void Navigate(NavigateParams* params) { params->source_contents->GetView()->Focus(); if (params->source_contents == params->target_contents || - (swapped_in_instant && params->disposition == CURRENT_TAB)) { + (swapped_in && params->disposition == CURRENT_TAB)) { // The navigation occurred in the source tab. params->browser->UpdateUIForNavigationInTab(params->target_contents, params->transition, diff --git a/chrome/browser/ui/browser_tab_contents.cc b/chrome/browser/ui/browser_tab_contents.cc index 95e26752d7..246302db0f 100644 --- a/chrome/browser/ui/browser_tab_contents.cc +++ b/chrome/browser/ui/browser_tab_contents.cc @@ -8,6 +8,7 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/content_settings/tab_specific_content_settings.h" #include "chrome/browser/extensions/api/web_navigation/web_navigation_api.h" +#include "chrome/browser/extensions/extension_web_contents_observer.h" #include "chrome/browser/extensions/tab_helper.h" #include "chrome/browser/external_protocol/external_protocol_observer.h" #include "chrome/browser/favicon/favicon_tab_helper.h" @@ -129,6 +130,7 @@ void BrowserTabContents::AttachTabHelpers(WebContents* web_contents) { chrome_browser_net::PredictorTabHelper::CreateForWebContents(web_contents); WebContentsModalDialogManager::CreateForWebContents(web_contents); CoreTabHelper::CreateForWebContents(web_contents); + extensions::ExtensionWebContentsObserver::CreateForWebContents(web_contents); extensions::TabHelper::CreateForWebContents(web_contents); extensions::WebNavigationTabObserver::CreateForWebContents(web_contents); ExternalProtocolObserver::CreateForWebContents(web_contents); diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h index 2acfbf68c0..7811ace7de 100644 --- a/chrome/browser/ui/browser_window.h +++ b/chrome/browser/ui/browser_window.h @@ -296,10 +296,6 @@ class BrowserWindow : public ui::BaseWindow { virtual void HandleKeyboardEvent( const content::NativeWebKeyboardEvent& event) = 0; - // Shows the create chrome app shortcut dialog box. - virtual void ShowCreateChromeAppShortcutsDialog(Profile* profile, - const extensions::Extension* app) = 0; - // Clipboard commands applied to the whole browser window. virtual void Cut() = 0; virtual void Copy() = 0; diff --git a/chrome/browser/ui/chrome_pages.cc b/chrome/browser/ui/chrome_pages.cc index a42b5e2940..1fe3475592 100644 --- a/chrome/browser/ui/chrome_pages.cc +++ b/chrome/browser/ui/chrome_pages.cc @@ -141,7 +141,8 @@ void ShowHelp(Browser* browser, HelpSource source) { Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord(); const extensions::Extension* extension = profile->GetExtensionService()-> GetInstalledExtension(genius_app::kGeniusAppId); - OpenApplication(AppLaunchParams(profile, extension, 0)); + OpenApplication( + AppLaunchParams(profile, extension, 0, browser->host_desktop_type())); return; } #endif diff --git a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h index b616cb50e0..18ff948163 100644 --- a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h +++ b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h @@ -127,17 +127,18 @@ class NativeAppWindowCocoa : public apps::NativeAppWindow, virtual bool IsFrameless() const OVERRIDE; virtual gfx::Insets GetFrameInsets() const OVERRIDE; virtual bool IsVisible() const OVERRIDE; - - // WebContentsObserver implementation. - virtual void RenderViewHostChanged( - content::RenderViewHost* old_host, - content::RenderViewHost* new_host) OVERRIDE; - // These are used to simulate Mac-style hide/show. Since windows can be hidden // and shown using the app.window API, this sets is_hidden_with_app_ to // differentiate the reason a window was hidden. virtual void ShowWithApp() OVERRIDE; virtual void HideWithApp() OVERRIDE; + // Calls setContent[Min|Max]Size with the current size constraints. + virtual void UpdateWindowMinMaxSize() OVERRIDE; + + // WebContentsObserver implementation. + virtual void RenderViewHostChanged( + content::RenderViewHost* old_host, + content::RenderViewHost* new_host) OVERRIDE; virtual void SetAlwaysOnTop(bool always_on_top) OVERRIDE; @@ -198,8 +199,6 @@ class NativeAppWindowCocoa : public apps::NativeAppWindow, bool is_fullscreen_; NSRect restored_bounds_; - gfx::Size min_size_; - gfx::Size max_size_; bool shows_resize_controls_; bool shows_fullscreen_controls_; diff --git a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm index 4cebcd9907..1e803aff39 100644 --- a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm +++ b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm @@ -62,14 +62,6 @@ enum { namespace { -// When gfx::Size is used as a min/max size, a zero represents an unbounded -// component. This method checks whether either component is specified. -// Note we can't use gfx::Size::IsEmpty as it returns true if either width or -// height is zero. -bool IsBoundedSize(const gfx::Size& size) { - return size.width() != 0 || size.height() != 0; -} - void SetFullScreenCollectionBehavior(NSWindow* window, bool allow_fullscreen) { NSWindowCollectionBehavior behavior = [window collectionBehavior]; if (allow_fullscreen) @@ -316,26 +308,18 @@ NativeAppWindowCocoa::NativeAppWindowCocoa( window_class = [ShellFramelessNSWindow class]; } - min_size_ = params.minimum_size; - max_size_ = params.maximum_size; - shows_resize_controls_ = params.resizable && - (min_size_.IsEmpty() || max_size_.IsEmpty() || min_size_ != max_size_); - shows_fullscreen_controls_ = params.resizable && !IsBoundedSize(max_size_); + ShellWindow::SizeConstraints size_constraints = + shell_window_->size_constraints(); + shows_resize_controls_ = + params.resizable && !size_constraints.HasFixedSize(); + shows_fullscreen_controls_ = + params.resizable && !size_constraints.HasMaximumSize(); window.reset([[window_class alloc] initWithContentRect:cocoa_bounds styleMask:GetWindowStyleMask() backing:NSBackingStoreBuffered defer:NO]); [window setTitle:base::SysUTF8ToNSString(extension()->name())]; - if (IsBoundedSize(min_size_)) { - [window setContentMinSize: - NSMakeSize(min_size_.width(), min_size_.height())]; - } - if (IsBoundedSize(max_size_)) { - CGFloat max_width = max_size_.width() ? max_size_.width() : CGFLOAT_MAX; - CGFloat max_height = max_size_.height() ? max_size_.height() : CGFLOAT_MAX; - [window setContentMaxSize:NSMakeSize(max_width, max_height)]; - } if (base::mac::IsOSSnowLeopard() && [window respondsToSelector:@selector(setBottomCornerRounded:)]) @@ -367,6 +351,7 @@ NativeAppWindowCocoa::NativeAppWindowCocoa( [[window_controller_ window] setDelegate:window_controller_]; [window_controller_ setAppWindow:this]; + UpdateWindowMinMaxSize(); extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryCocoa( shell_window_->profile(), @@ -1027,3 +1012,16 @@ void NativeAppWindowCocoa::UpdateRestoredBounds() { if (IsRestored(*this)) restored_bounds_ = [window() frame]; } + +void NativeAppWindowCocoa::UpdateWindowMinMaxSize() { + gfx::Size min_size = shell_window_->size_constraints().GetMinimumSize(); + [window() setContentMinSize:NSMakeSize(min_size.width(), min_size.height())]; + + gfx::Size max_size = shell_window_->size_constraints().GetMinimumSize(); + const int kUnboundedSize = ShellWindow::SizeConstraints::kUnboundedSize; + CGFloat max_width = max_size.width() == kUnboundedSize ? + CGFLOAT_MAX : max_size.width(); + CGFloat max_height = max_size.height() == kUnboundedSize ? + CGFLOAT_MAX : max_size.height(); + [window() setContentMaxSize:NSMakeSize(max_width, max_height)]; +} diff --git a/chrome/browser/ui/cocoa/autofill/autofill_account_chooser.h b/chrome/browser/ui/cocoa/autofill/autofill_account_chooser.h index 42f2cd9b35..75a50a7201 100644 --- a/chrome/browser/ui/cocoa/autofill/autofill_account_chooser.h +++ b/chrome/browser/ui/cocoa/autofill/autofill_account_chooser.h @@ -26,6 +26,7 @@ namespace autofill { - (id)initWithFrame:(NSRect)frame delegate:(autofill::AutofillDialogViewDelegate*)delegate; - (void)update; +- (void)performLayout; @end diff --git a/chrome/browser/ui/cocoa/autofill/autofill_account_chooser.mm b/chrome/browser/ui/cocoa/autofill/autofill_account_chooser.mm index 9294d30e22..fb2f8d2ba6 100644 --- a/chrome/browser/ui/cocoa/autofill/autofill_account_chooser.mm +++ b/chrome/browser/ui/cocoa/autofill/autofill_account_chooser.mm @@ -121,7 +121,7 @@ void AddMenuItem(NSMenu *menu, id target, SEL selector, NSString* title, self, @selector(optionsMenuChanged:), base::SysUTF16ToNSString(model->GetLabelAt(i)), - model->GetCommandIdAt(i), + i, model->IsEnabledAt(i), model->IsItemCheckedAt(i)); } diff --git a/chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.h b/chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.h index 4cded2c499..78ce04bd43 100644 --- a/chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.h +++ b/chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.h @@ -151,6 +151,7 @@ class AutofillDialogCocoa : public AutofillDialogView, - (void)hideSignIn; - (void)modelChanged; - (void)updateErrorBubble; +- (void)onSignInResize:(NSSize)size; @end diff --git a/chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.mm b/chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.mm index 640f46dd4a..763f5b3f6b 100644 --- a/chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.mm +++ b/chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.mm @@ -29,6 +29,13 @@ #include "ui/gfx/platform_font.h" const CGFloat kAccountChooserHeight = 20.0; +const CGFloat kMinimumContentsHeight = 101; + +// Height of all decorations & paddings on main dialog together. +const CGFloat kDecorationHeight = kAccountChooserHeight + + autofill::kDetailTopPadding + + chrome_style::kClientBottomPadding + + chrome_style::kTitleTopPadding; namespace autofill { @@ -158,7 +165,8 @@ TestableAutofillDialogView* AutofillDialogCocoa::GetTestableView() { } void AutofillDialogCocoa::OnSignInResize(const gfx::Size& pref_size) { - // TODO(groby): Implement Mac support for this. + [sheet_delegate_ onSignInResize: + NSMakeSize(pref_size.width(), pref_size.height())]; } void AutofillDialogCocoa::SubmitForTesting() { @@ -235,6 +243,12 @@ void AutofillDialogCocoa::OnConstrainedWindowClosed( @interface AutofillDialogWindowController () +// Compute maximum allowed height for the dialog. +- (CGFloat)maxHeight; + +// Update size constraints on sign-in container. +- (void)updateSignInSizeConstraints; + // Notification that the WebContent's view frame has changed. - (void)onContentViewFrameDidChange:(NSNotification*)notification; @@ -338,8 +352,30 @@ void AutofillDialogCocoa::OnConstrainedWindowClosed( [super dealloc]; } +- (CGFloat)maxHeight { + NSRect dialogFrameRect = [[self window] frame]; + NSRect browserFrameRect = + [webContents_->GetView()->GetTopLevelNativeWindow() frame]; + dialogFrameRect.size.height = + NSMaxY(dialogFrameRect) - NSMinY(browserFrameRect); + dialogFrameRect = [[self window] contentRectForFrameRect:dialogFrameRect]; + return NSHeight(dialogFrameRect); +} + +- (void)updateSignInSizeConstraints { + // Adjust for the size of all decorations and paddings outside main content. + CGFloat minHeight = kMinimumContentsHeight - kDecorationHeight; + CGFloat maxHeight = std::max([self maxHeight] - kDecorationHeight, minHeight); + CGFloat width = NSWidth([[[self window] contentView] frame]); + + [signInContainer_ constrainSizeToMinimum:NSMakeSize(width, minHeight) + maximum:NSMakeSize(width, maxHeight)]; +} + - (void)onContentViewFrameDidChange:(NSNotification*)notification { - [self requestRelayout]; + [self updateSignInSizeConstraints]; + if ([[signInContainer_ view] isHidden]) + [self requestRelayout]; } - (void)cancelRelayout { @@ -355,26 +391,21 @@ void AutofillDialogCocoa::OnConstrainedWindowClosed( - (NSSize)preferredSize { NSSize contentSize; - // TODO(groby): Currently, keep size identical to main container. - // Change to allow autoresize of web contents. - contentSize = [mainContainer_ preferredSize]; + // Overall size is determined by either main container or sign in view. + if (![[mainContainer_ view] isHidden]) + contentSize = [mainContainer_ preferredSize]; + else + contentSize = [signInContainer_ preferredSize]; + + // Always make room for the header. NSSize headerSize = NSMakeSize(contentSize.width, kAccountChooserHeight); - NSSize size = NSMakeSize( - std::max(contentSize.width, headerSize.width), - contentSize.height + headerSize.height + autofill::kDetailTopPadding); - size.height += chrome_style::kClientBottomPadding + - chrome_style::kTitleTopPadding; + NSSize size = contentSize; + size.height += kDecorationHeight; // Show as much of the main view as is possible without going past the // bottom of the browser window. - NSRect dialogFrameRect = [[self window] frame]; - NSRect browserFrameRect = - [webContents_->GetView()->GetTopLevelNativeWindow() frame]; - dialogFrameRect.size.height = - NSMaxY(dialogFrameRect) - NSMinY(browserFrameRect); - dialogFrameRect = [[self window] contentRectForFrameRect:dialogFrameRect]; - size.height = std::min(NSHeight(dialogFrameRect), size.height); + size.height = std::min(size.height, [self maxHeight]); if (![[overlayController_ view] isHidden]) { CGFloat height = [overlayController_ heightForWidth:size.width]; @@ -420,6 +451,7 @@ void AutofillDialogCocoa::OnConstrainedWindowClosed( [titleTextField_ setFrame:titleRect]; [accountChooser_ setFrame:headerRect]; + [accountChooser_ performLayout]; if ([[signInContainer_ view] isHidden]) { [[mainContainer_ view] setFrame:mainRect]; [mainContainer_ performLayout]; @@ -523,6 +555,7 @@ void AutofillDialogCocoa::OnConstrainedWindowClosed( } - (content::NavigationController*)showSignIn { + [self updateSignInSizeConstraints]; [signInContainer_ loadSignInPage]; [[mainContainer_ view] setHidden:YES]; [[signInContainer_ view] setHidden:NO]; @@ -554,6 +587,11 @@ void AutofillDialogCocoa::OnConstrainedWindowClosed( [mainContainer_ updateErrorBubble]; } +- (void)onSignInResize:(NSSize)size { + [signInContainer_ setPreferredSize:size]; + [self requestRelayout]; +} + @end diff --git a/chrome/browser/ui/cocoa/autofill/autofill_overlay_controller.mm b/chrome/browser/ui/cocoa/autofill/autofill_overlay_controller.mm index 8b3ac3ed3b..12a984cea2 100644 --- a/chrome/browser/ui/cocoa/autofill/autofill_overlay_controller.mm +++ b/chrome/browser/ui/cocoa/autofill/autofill_overlay_controller.mm @@ -96,7 +96,12 @@ SkColor kSubtleBorderColor = 0xffdfdfdf; [label_ setFont:message.font.GetNativeFont()]; [label_ setStringValue:base::SysUTF16ToNSString(message.text)]; [label_ setTextColor:gfx::SkColorToCalibratedNSColor(message.text_color)]; - [label_ sizeToFit]; + + // Resize only height, preserve width. This guarantees text stays centered in + // the dialog. + NSSize labelSize = [label_ frame].size; + labelSize.height = [[label_ cell] cellSize].height; + [label_ setFrameSize:labelSize]; [self setHidden:message.text.empty()]; } diff --git a/chrome/browser/ui/cocoa/autofill/autofill_section_container.mm b/chrome/browser/ui/cocoa/autofill/autofill_section_container.mm index 17c03c2e58..59343e0d28 100644 --- a/chrome/browser/ui/cocoa/autofill/autofill_section_container.mm +++ b/chrome/browser/ui/cocoa/autofill/autofill_section_container.mm @@ -125,6 +125,9 @@ bool CompareInputRows(const autofill::DetailInput* input1, // Create a view with all inputs requested by |delegate_|. Autoreleased. - (LayoutView*)makeInputControls; +// Refresh all field icons based on |delegate_| status. +- (void)updateFieldIcons; + @end @implementation AutofillSectionContainer @@ -413,11 +416,9 @@ bool CompareInputRows(const autofill::DetailInput* input1, [self validateFor:autofill::VALIDATE_EDIT]; } - // Update the icon for the textfield. - gfx::Image icon = delegate_->IconForField(type, fieldValue); - if (!icon.IsEmpty()) { - [[textfield cell] setIcon:icon.ToNSImage()]; - } + // Update the icon if necessary. + if (delegate_->FieldControlsIcons(type)) + [self updateFieldIcons]; } - (autofill::ServerFieldType)fieldTypeForControl:(NSControl*)control { @@ -472,23 +473,17 @@ bool CompareInputRows(const autofill::DetailInput* input1, NSControl<AutofillInputField>* field = [inputs_ viewWithTag:iter->type]; DCHECK(field); - [field setEnabled:iter->editable]; + // TODO(groby): We need to account for the fact editability state can change + // after any input in the same section is edited by the user. + [field setEnabled:delegate_->InputIsEditable(*iter, section_)]; if (shouldClobber || [field isDefault]) { [field setFieldValue:base::SysUTF16ToNSString(iter->initial_value)]; - AutofillTextField* textField = - base::mac::ObjCCast<AutofillTextField>(field); - if (textField) { - gfx::Image icon = - delegate_->IconForField(iter->type, iter->initial_value); - if (!icon.IsEmpty()) { - [[textField cell] setIcon:icon.ToNSImage()]; - } - } } if (shouldClobber) [field setValidityMessage:@""]; } + [self updateFieldIcons]; [self modelChanged]; } @@ -573,9 +568,6 @@ bool CompareInputRows(const autofill::DetailInput* input1, [[AutofillTextField alloc] init]); [[field cell] setPlaceholderString: l10n_util::GetNSStringWithFixup(input.placeholder_text_rid)]; - [[field cell] setIcon: - delegate_->IconForField( - input.type, input.initial_value).AsNSImage()]; [field setDefaultValue:@""]; control.reset(field.release()); } @@ -591,9 +583,29 @@ bool CompareInputRows(const autofill::DetailInput* input1, layout->AddView(control); } + [self updateFieldIcons]; return view.autorelease(); } +- (void)updateFieldIcons { + autofill::FieldValueMap fieldValues; + for (NSControl<AutofillInputField>* input in [inputs_ subviews]) { + DCHECK([input isKindOfClass:[NSControl class]]); + DCHECK([input conformsToProtocol:@protocol(AutofillInputField)]); + autofill::ServerFieldType fieldType = [self fieldTypeForControl:input]; + NSString* value = [input fieldValue]; + fieldValues[fieldType] = base::SysNSStringToUTF16(value); + } + + autofill::FieldIconMap fieldIcons = delegate_->IconsForFields(fieldValues); + for (autofill::FieldIconMap::const_iterator iter = fieldIcons.begin(); + iter!= fieldIcons.end(); ++iter) { + AutofillTextField* textfield = base::mac::ObjCCastStrict<AutofillTextField>( + [inputs_ viewWithTag:iter->first]); + [[textfield cell] setIcon:iter->second.ToNSImage()]; + } +} + @end diff --git a/chrome/browser/ui/cocoa/autofill/autofill_sign_in_container.h b/chrome/browser/ui/cocoa/autofill/autofill_sign_in_container.h index d16cc7e0e2..0279c2087c 100644 --- a/chrome/browser/ui/cocoa/autofill/autofill_sign_in_container.h +++ b/chrome/browser/ui/cocoa/autofill/autofill_sign_in_container.h @@ -25,11 +25,18 @@ class NavigationController; autofill::AutofillDialogCocoa* dialog_; // Not owned. scoped_ptr<content::WebContents> webContents_; scoped_ptr<autofill::AutofillDialogSignInDelegate> signInDelegate_; + + NSSize maxSize_; + NSSize minSize_; + NSSize preferredSize_; } +@property(assign, nonatomic) NSSize preferredSize; + - (id)initWithDialog:(autofill::AutofillDialogCocoa*)dialog; - (void)loadSignInPage; - (content::NavigationController*)navigationController; +- (void)constrainSizeToMinimum:(NSSize)minSize maximum:(NSSize)maximum; @end diff --git a/chrome/browser/ui/cocoa/autofill/autofill_sign_in_container.mm b/chrome/browser/ui/cocoa/autofill/autofill_sign_in_container.mm index 05cb12e7c3..02c1b4d5fe 100644 --- a/chrome/browser/ui/cocoa/autofill/autofill_sign_in_container.mm +++ b/chrome/browser/ui/cocoa/autofill/autofill_sign_in_container.mm @@ -16,6 +16,8 @@ @implementation AutofillSignInContainer +@synthesize preferredSize = preferredSize_; + - (id)initWithDialog:(autofill::AutofillDialogCocoa*)dialog { if (self = [super init]) { dialog_ = dialog; @@ -33,12 +35,18 @@ - (void)loadSignInPage { DCHECK(webContents_.get()); + + // Prevent accidentaly empty |maxSize_|. + if (NSEqualSizes(NSMakeSize(0, 0), maxSize_)) { + maxSize_ = [[[self view] window] frame].size; + } + signInDelegate_.reset( new autofill::AutofillDialogSignInDelegate( dialog_, webContents_.get(), dialog_->delegate()->GetWebContents()->GetDelegate(), - // TODO(groby): Implement proper minimum and maximum sizing on Mac. - dialog_->GetSize(), dialog_->GetSize())); + gfx::Size(NSSizeToCGSize(minSize_)), + gfx::Size(NSSizeToCGSize(maxSize_)))); webContents_->GetController().LoadURL( autofill::wallet::GetSignInUrl(), content::Referrer(), @@ -50,4 +58,25 @@ return &webContents_->GetController(); } +- (void)constrainSizeToMinimum:(NSSize)minSize maximum:(NSSize)maxSize { + minSize_ = minSize; + maxSize_ = maxSize; + + // Notify the web contents of its new auto-resize limits. + if (signInDelegate_ && ![[self view] isHidden]) { + signInDelegate_->UpdateLimitsAndEnableAutoResize( + gfx::Size(NSSizeToCGSize(minSize_)), + gfx::Size(NSSizeToCGSize(maxSize_))); + } +} + +- (void)setPreferredSize:(NSSize)size { + preferredSize_ = size; + + // Always request re-layout if preferredSize changes. + id delegate = [[[self view] window] windowController]; + if ([delegate respondsToSelector:@selector(requestRelayout)]) + [delegate performSelector:@selector(requestRelayout)]; +} + @end diff --git a/chrome/browser/ui/cocoa/autofill/autofill_textfield.mm b/chrome/browser/ui/cocoa/autofill/autofill_textfield.mm index cf8658ae2b..32df150eb1 100644 --- a/chrome/browser/ui/cocoa/autofill/autofill_textfield.mm +++ b/chrome/browser/ui/cocoa/autofill/autofill_textfield.mm @@ -79,7 +79,6 @@ const CGFloat kGap = 6.0; // gap between icon and text. - (void)setValidityMessage:(NSString*)validityMessage { validityMessage_.reset([validityMessage copy]); [[self cell] setInvalid:[self invalid]]; - [self setNeedsDisplay:YES]; } - (BOOL)invalid { @@ -93,12 +92,18 @@ const CGFloat kGap = 6.0; // gap between icon and text. @synthesize invalid = invalid_; @synthesize defaultValue = defaultValue_; +- (void)setInvalid:(BOOL)invalid { + invalid_ = invalid; + [[self controlView] setNeedsDisplay:YES]; +} + - (NSImage*) icon{ return icon_; } - (void)setIcon:(NSImage*) icon { icon_.reset([icon retain]); + [[self controlView] setNeedsDisplay:YES]; } - (NSString*)fieldValue { diff --git a/chrome/browser/ui/cocoa/browser/avatar_button_controller_unittest.mm b/chrome/browser/ui/cocoa/browser/avatar_button_controller_unittest.mm index bae78026db..a014160d25 100644 --- a/chrome/browser/ui/cocoa/browser/avatar_button_controller_unittest.mm +++ b/chrome/browser/ui/cocoa/browser/avatar_button_controller_unittest.mm @@ -5,19 +5,26 @@ #import "chrome/browser/ui/cocoa/browser/avatar_button_controller.h" #include "base/mac/scoped_nsobject.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/autocomplete/autocomplete_classifier_factory.h" +#include "chrome/browser/bookmarks/bookmark_test_helpers.h" #include "chrome/browser/managed_mode/managed_user_service.h" #include "chrome/browser/managed_mode/managed_user_service_factory.h" +#include "chrome/browser/prefs/pref_service_syncable.h" +#include "chrome/browser/search_engines/template_url_service_factory.h" #include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/browser_window.h" #import "chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller.h" #include "chrome/browser/ui/cocoa/cocoa_profile_test.h" #include "chrome/browser/ui/cocoa/info_bubble_window.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/testing_profile.h" class AvatarButtonControllerTest : public CocoaProfileTest { public: - virtual void SetUp() { + virtual void SetUp() OVERRIDE { CocoaProfileTest::SetUp(); ASSERT_TRUE(browser()); @@ -26,7 +33,7 @@ class AvatarButtonControllerTest : public CocoaProfileTest { [[controller_ view] setHidden:YES]; } - virtual void TearDown() { + virtual void TearDown() OVERRIDE { browser()->window()->Close(); CocoaProfileTest::TearDown(); } @@ -82,18 +89,31 @@ TEST_F(AvatarButtonControllerTest, DoubleOpen) { } TEST_F(AvatarButtonControllerTest, ManagedUserLabel) { - // Create a second profile to enable the avatar menu. - testing_profile_manager()->CreateTestingProfile("p2"); - + DCHECK(!profile()->IsManaged()); EXPECT_FALSE([controller() labelButtonView]); - // Transform the first profile to a managed user profile. - ManagedUserServiceFactory::GetForProfile(profile())->InitForTesting(); - + // Create a second, managed profile to enable the avatar menu. + std::string name = "p2"; + TestingProfile* profile = testing_profile_manager()->CreateTestingProfile( + name, scoped_ptr<PrefServiceSyncable>(), ASCIIToUTF16(name), 0, "asdf"); + EXPECT_TRUE(profile->IsManaged()); + + // http://crbug.com/39725 + TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse( + profile, &TemplateURLServiceFactory::BuildInstanceFor); + AutocompleteClassifierFactory::GetInstance()->SetTestingFactoryAndUse( + profile, &AutocompleteClassifierFactory::BuildInstanceFor); + profile->CreateBookmarkModel(true); + test::WaitForBookmarkModelToLoad(profile); + + Browser* browser = + new Browser(Browser::CreateParams(profile, chrome::GetActiveDesktop())); // Build a new controller to check if it is initialized correctly for a // managed user profile. base::scoped_nsobject<AvatarButtonController> controller( - [[AvatarButtonController alloc] initWithBrowser:browser()]); + [[AvatarButtonController alloc] initWithBrowser:browser]); EXPECT_TRUE([controller labelButtonView]); + + browser->window()->Close(); } diff --git a/chrome/browser/ui/cocoa/browser_window_cocoa.h b/chrome/browser/ui/cocoa/browser_window_cocoa.h index db7c621f8b..0370bc0090 100644 --- a/chrome/browser/ui/cocoa/browser_window_cocoa.h +++ b/chrome/browser/ui/cocoa/browser_window_cocoa.h @@ -127,9 +127,6 @@ class BrowserWindowCocoa : bool* is_keyboard_shortcut) OVERRIDE; virtual void HandleKeyboardEvent( const content::NativeWebKeyboardEvent& event) OVERRIDE; - virtual void ShowCreateChromeAppShortcutsDialog( - Profile* profile, - const extensions::Extension* app) OVERRIDE; virtual void Cut() OVERRIDE; virtual void Copy() OVERRIDE; virtual void Paste() OVERRIDE; diff --git a/chrome/browser/ui/cocoa/browser_window_cocoa.mm b/chrome/browser/ui/cocoa/browser_window_cocoa.mm index d3e9ac8a27..230ba44f46 100644 --- a/chrome/browser/ui/cocoa/browser_window_cocoa.mm +++ b/chrome/browser/ui/cocoa/browser_window_cocoa.mm @@ -44,7 +44,6 @@ #import "chrome/browser/ui/cocoa/website_settings_bubble_controller.h" #include "chrome/browser/ui/search/search_model.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" -#include "chrome/browser/ui/web_applications/web_app_ui.h" #include "chrome/browser/web_applications/web_app.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" @@ -104,13 +103,6 @@ NSPoint GetPointForBubble(content::WebContents* web_contents, return point; } -void CreateShortcuts(const ShellIntegration::ShortcutInfo& shortcut_info) { - // creation_locations will be ignored by CreatePlatformShortcuts on Mac. - ShellIntegration::ShortcutLocations creation_locations; - web_app::CreateShortcuts(shortcut_info, creation_locations, - web_app::SHORTCUT_CREATION_BY_USER); -} - } // namespace BrowserWindowCocoa::BrowserWindowCocoa(Browser* browser, @@ -598,14 +590,6 @@ void BrowserWindowCocoa::HandleKeyboardEvent( [BrowserWindowUtils handleKeyboardEvent:event.os_event inWindow:window()]; } -void BrowserWindowCocoa::ShowCreateChromeAppShortcutsDialog( - Profile* profile, const extensions::Extension* app) { - // Normally we would show a dialog, but since we always create the app - // shortcut in /Applications there are no options for the user to choose. - web_app::UpdateShortcutInfoAndIconForApp(*app, profile, - base::Bind(&CreateShortcuts)); -} - void BrowserWindowCocoa::Cut() { [NSApp sendAction:@selector(cut:) to:nil from:nil]; } diff --git a/chrome/browser/ui/cocoa/download/download_item_controller.mm b/chrome/browser/ui/cocoa/download/download_item_controller.mm index 1c7d29561c..8981e4084d 100644 --- a/chrome/browser/ui/cocoa/download/download_item_controller.mm +++ b/chrome/browser/ui/cocoa/download/download_item_controller.mm @@ -172,7 +172,7 @@ class DownloadShelfContextMenuMac : public DownloadShelfContextMenu { *font_list_, kTextWidth)); confirmButtonTitle = base::SysUTF16ToNSString(downloadModel->GetWarningConfirmButtonText()); - if (downloadModel->IsMalicious()) + if (downloadModel->MightBeMalicious()) alertIcon = rb.GetNativeImageNamed(IDR_SAFEBROWSING_WARNING).ToNSImage(); else alertIcon = rb.GetNativeImageNamed(IDR_WARNING).ToNSImage(); diff --git a/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm b/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm index a17f3d8cde..92daa8e133 100644 --- a/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm +++ b/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm @@ -15,6 +15,7 @@ #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/extensions/extension_toolbar_model.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/sessions/session_tab_helper.h" #include "chrome/browser/ui/browser.h" @@ -137,8 +138,11 @@ const CGFloat kBrowserActionBubbleYOffset = 3.0; toIndex:(NSUInteger)index animate:(BOOL)animate; -// Handles when the given BrowserActionButton object is clicked. -- (void)browserActionClicked:(BrowserActionButton*)button; +// Handles when the given BrowserActionButton object is clicked and whether +// it should grant tab permissions. API-simulated clicks should not grant. +- (BOOL)browserActionClicked:(BrowserActionButton*)button + shouldGrant:(BOOL)shouldGrant; +- (BOOL)browserActionClicked:(BrowserActionButton*)button; // Returns whether the given extension should be displayed. Only displays // incognito-enabled extensions in incognito mode. Otherwise returns YES. @@ -238,6 +242,17 @@ class ExtensionServiceObserverBridge : public content::NotificationObserver, [owner_ resizeContainerAndAnimate:NO]; } + virtual bool BrowserActionShowPopup(const Extension* extension) OVERRIDE { + // Do not override other popups and only show in active window. + ExtensionPopupController* popup = [ExtensionPopupController popup]; + if (popup || !browser_->window()->IsActive()) + return false; + + BrowserActionButton* button = [owner_ buttonForExtension:extension]; + return button && [owner_ browserActionClicked:button + shouldGrant:NO]; + } + private: // The object we need to inform when we get a notification. Weak. Owns us. BrowserActionsController* owner_; @@ -739,10 +754,12 @@ class ExtensionServiceObserverBridge : public content::NotificationObserver, } } -- (void)browserActionClicked:(BrowserActionButton*)button { +- (BOOL)browserActionClicked:(BrowserActionButton*)button + shouldGrant:(BOOL)shouldGrant { const Extension* extension = [button extension]; GURL popupUrl; - switch (toolbarModel_->ExecuteBrowserAction(extension, browser_, &popupUrl)) { + switch (toolbarModel_->ExecuteBrowserAction(extension, browser_, &popupUrl, + shouldGrant)) { case ExtensionToolbarModel::ACTION_NONE: break; case ExtensionToolbarModel::ACTION_SHOW_POPUP: { @@ -752,17 +769,24 @@ class ExtensionServiceObserverBridge : public content::NotificationObserver, anchoredAt:arrowPoint arrowLocation:info_bubble::kTopRight devMode:NO]; - break; + return YES; } } + return NO; +} + +- (BOOL)browserActionClicked:(BrowserActionButton*)button { + return [self browserActionClicked:button + shouldGrant:YES]; } - (BOOL)shouldDisplayBrowserAction:(const Extension*)extension { // Only display incognito-enabled extensions while in incognito mode. return (!profile_->IsOffTheRecord() || - extensions::ExtensionSystem::Get(profile_)->extension_service()-> - IsIncognitoEnabled(extension->id())); + extension_util::IsIncognitoEnabled( + extension->id(), + extensions::ExtensionSystem::Get(profile_)->extension_service())); } - (void)showChevronIfNecessaryInFrame:(NSRect)frame animate:(BOOL)animate { diff --git a/chrome/browser/ui/cocoa/first_run_dialog.mm b/chrome/browser/ui/cocoa/first_run_dialog.mm index 2ffa267f43..4117e62506 100644 --- a/chrome/browser/ui/cocoa/first_run_dialog.mm +++ b/chrome/browser/ui/cocoa/first_run_dialog.mm @@ -29,7 +29,7 @@ #include "chrome/browser/browser_process.h" #include "chrome/common/pref_names.h" #include "chrome/installer/util/google_update_settings.h" -#import "components/breakpad/breakpad_mac.h" +#import "components/breakpad/app/breakpad_mac.h" #endif @interface FirstRunDialogController (PrivateMethods) diff --git a/chrome/browser/ui/cocoa/infobars/infobar_cocoa.h b/chrome/browser/ui/cocoa/infobars/infobar_cocoa.h index e8f1a83d54..7419f31bc0 100644 --- a/chrome/browser/ui/cocoa/infobars/infobar_cocoa.h +++ b/chrome/browser/ui/cocoa/infobars/infobar_cocoa.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_UI_COCOA_INFOBARS_INFOBAR_COCOA_H_ #include "base/mac/scoped_nsobject.h" +#include "base/memory/weak_ptr.h" #include "chrome/browser/infobars/infobar.h" @class InfoBarController; @@ -28,10 +29,15 @@ class InfoBarCocoa : public InfoBar { void RemoveSelfCocoa(); InfoBarService* OwnerCocoa(); + base::WeakPtr<InfoBarCocoa> GetWeakPtr(); + private: // The Objective-C class that contains most of the info bar logic. base::scoped_nsobject<InfoBarController> controller_; + // Used to vend the link back to this for |controller_|. + base::WeakPtrFactory<InfoBarCocoa> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(InfoBarCocoa); }; diff --git a/chrome/browser/ui/cocoa/infobars/infobar_cocoa.mm b/chrome/browser/ui/cocoa/infobars/infobar_cocoa.mm index c015171d91..398cd5fe39 100644 --- a/chrome/browser/ui/cocoa/infobars/infobar_cocoa.mm +++ b/chrome/browser/ui/cocoa/infobars/infobar_cocoa.mm @@ -12,7 +12,8 @@ const int InfoBar::kMaximumArrowTargetHalfWidth = 14; const int InfoBar::kDefaultBarTargetHeight = 36; InfoBarCocoa::InfoBarCocoa(InfoBarService* owner, InfoBarDelegate* delegate) - : InfoBar(owner, delegate) { + : InfoBar(owner, delegate), + weak_ptr_factory_(this) { } InfoBarCocoa::~InfoBarCocoa() { @@ -25,3 +26,7 @@ void InfoBarCocoa::RemoveSelfCocoa() { InfoBarService* InfoBarCocoa::OwnerCocoa() { return owner(); } + +base::WeakPtr<InfoBarCocoa> InfoBarCocoa::GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} diff --git a/chrome/browser/ui/cocoa/infobars/infobar_container_controller.mm b/chrome/browser/ui/cocoa/infobars/infobar_container_controller.mm index f6110909f5..ffaa38c83d 100644 --- a/chrome/browser/ui/cocoa/infobars/infobar_container_controller.mm +++ b/chrome/browser/ui/cocoa/infobars/infobar_container_controller.mm @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h" + #include "base/logging.h" #include "base/mac/mac_util.h" #include "base/message_loop/message_loop.h" @@ -12,7 +14,6 @@ #import "chrome/browser/ui/cocoa/browser_window_controller.h" #import "chrome/browser/ui/cocoa/infobars/infobar_cocoa.h" #import "chrome/browser/ui/cocoa/infobars/infobar_container_cocoa.h" -#import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h" #import "chrome/browser/ui/cocoa/infobars/infobar_controller.h" #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h" #import "chrome/browser/ui/cocoa/view_id_util.h" @@ -99,8 +100,10 @@ } - (void)removeInfoBar:(InfoBarCocoa*)infobar { - [infobar->controller() infobarWillClose]; - [self removeController:infobar->controller()]; + InfoBarController* controller = infobar->controller(); + [controller infobarWillClose]; + infobar->set_controller(nil); + [self removeController:controller]; base::MessageLoop::current()->DeleteSoon(FROM_HERE, infobar); } diff --git a/chrome/browser/ui/cocoa/infobars/infobar_controller.h b/chrome/browser/ui/cocoa/infobars/infobar_controller.h index 5948169600..0699d372dc 100644 --- a/chrome/browser/ui/cocoa/infobars/infobar_controller.h +++ b/chrome/browser/ui/cocoa/infobars/infobar_controller.h @@ -5,6 +5,7 @@ #import <Cocoa/Cocoa.h> #include "base/mac/scoped_nsobject.h" +#include "base/memory/weak_ptr.h" @protocol InfoBarContainerControllerBase; class InfoBarCocoa; @@ -19,7 +20,7 @@ class InfoBarService; @interface InfoBarController : NSViewController<NSTextViewDelegate> { @private id<InfoBarContainerControllerBase> containerController_; // weak, owns us - InfoBarCocoa* infobar_; // weak, owns us + base::WeakPtr<InfoBarCocoa> infobar_; @protected IBOutlet InfoBarGradientView* infoBarView_; @@ -41,7 +42,7 @@ class InfoBarService; @property(nonatomic, readonly) InfoBarDelegate* delegate; @property(nonatomic, readonly) InfoBarCocoa* infobar; -// Initializes a new InfoBarController. +// Initializes a new InfoBarController and takes a WeakPtr to |infobar|. - (id)initWithInfoBar:(InfoBarCocoa*)infobar; // Returns YES if the infobar is owned. If this is NO, it is not safe to call diff --git a/chrome/browser/ui/cocoa/infobars/infobar_controller.mm b/chrome/browser/ui/cocoa/infobars/infobar_controller.mm index 3a7d0d564f..1385f81d21 100644 --- a/chrome/browser/ui/cocoa/infobars/infobar_controller.mm +++ b/chrome/browser/ui/cocoa/infobars/infobar_controller.mm @@ -29,13 +29,12 @@ @implementation InfoBarController @synthesize containerController = containerController_; -@synthesize infobar = infobar_; - (id)initWithInfoBar:(InfoBarCocoa*)infobar { if ((self = [super initWithNibName:@"InfoBar" bundle:base::mac::FrameworkBundle()])) { DCHECK(infobar); - infobar_ = infobar; + infobar_ = infobar->GetWeakPtr(); } return self; } @@ -79,6 +78,10 @@ [super dealloc]; } +- (InfoBarCocoa*)infobar { + return infobar_.get(); +} + // Called when someone clicks on the embedded link. - (BOOL)textView:(NSTextView*)textView clickedOnLink:(id)link @@ -89,7 +92,7 @@ } - (BOOL)isOwned { - return infobar_->OwnerCocoa() != NULL; + return infobar_ && infobar_->OwnerCocoa() != NULL; } // Called when someone clicks on the ok button. diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc index 6d77910ad7..e0efa58bbc 100644 --- a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc +++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc @@ -925,7 +925,7 @@ void ContentSettingMediaStreamBubbleModel::SetMediaMenus() { if (!cameras.empty()) { std::string preferred_camera; if (requested_camera.empty()) { - preferred_camera = prefs->GetString(prefs::kDefaultAudioCaptureDevice); + preferred_camera = prefs->GetString(prefs::kDefaultVideoCaptureDevice); camera_menu.disabled = false; } else { // Disable the menu since the website is managing the camera devices diff --git a/chrome/browser/ui/extensions/application_launch.cc b/chrome/browser/ui/extensions/application_launch.cc index d3eceb7860..d68404f0d3 100644 --- a/chrome/browser/ui/extensions/application_launch.cc +++ b/chrome/browser/ui/extensions/application_launch.cc @@ -27,7 +27,6 @@ #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/extensions/extension_enable_flow.h" #include "chrome/browser/ui/extensions/extension_enable_flow_delegate.h" -#include "chrome/browser/ui/host_desktop.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/web_applications/web_app.h" #include "chrome/common/chrome_switches.h" @@ -59,6 +58,60 @@ using extensions::ExtensionPrefs; namespace { +// Attempts to launch a packaged app, prompting the user to enable it if +// necessary. If a prompt is required it will be shown inside the AppList. +// This class manages its own lifetime. +class EnableViaAppListFlow : public ExtensionEnableFlowDelegate { + public: + EnableViaAppListFlow(ExtensionService* service, + Profile* profile, + const std::string& extension_id, + const base::Closure& callback) + : service_(service), + profile_(profile), + extension_id_(extension_id), + callback_(callback) { + } + + virtual ~EnableViaAppListFlow() { + } + + void Run() { + DCHECK(!service_->IsExtensionEnabled(extension_id_)); + flow_.reset(new ExtensionEnableFlow(profile_, extension_id_, this)); + flow_->StartForCurrentlyNonexistentWindow( + base::Bind(&EnableViaAppListFlow::ShowAppList, base::Unretained(this))); + } + + private: + gfx::NativeWindow ShowAppList() { + AppListService::Get()->Show(); + return AppListService::Get()->GetAppListWindow(); + } + + // ExtensionEnableFlowDelegate overrides. + virtual void ExtensionEnableFlowFinished() OVERRIDE { + const Extension* extension = + service_->GetExtensionById(extension_id_, false); + if (!extension) + return; + callback_.Run(); + delete this; + } + + virtual void ExtensionEnableFlowAborted(bool user_initiated) OVERRIDE { + delete this; + } + + ExtensionService* service_; + Profile* profile_; + std::string extension_id_; + base::Closure callback_; + scoped_ptr<ExtensionEnableFlow> flow_; + + DISALLOW_COPY_AND_ASSIGN(EnableViaAppListFlow); +}; + // Get the launch URL for a given extension, with optional override/fallback. // |override_url|, if non-empty, will be preferred over the extension's // launch url. @@ -115,55 +168,44 @@ ui::WindowShowState DetermineWindowShowState( return ui::SHOW_STATE_DEFAULT; } -WebContents* OpenApplicationWindow( - Profile* profile, - const Extension* extension, - extension_misc::LaunchContainer container, - const GURL& url_input, - Browser** app_browser, - const gfx::Rect& override_bounds) { +WebContents* OpenApplicationWindow(const AppLaunchParams& params) { + Profile* const profile = params.profile; + const extensions::Extension* const extension = params.extension; + const GURL url_input = params.override_url; + DCHECK(!url_input.is_empty() || extension); GURL url = UrlForExtension(extension, url_input); + Browser::CreateParams browser_params( + Browser::TYPE_POPUP, profile, params.desktop_type); - std::string app_name; - app_name = extension ? + browser_params.app_name = extension ? web_app::GenerateApplicationNameFromExtensionId(extension->id()) : web_app::GenerateApplicationNameFromURL(url); - Browser::Type type = Browser::TYPE_POPUP; - - gfx::Rect window_bounds; - if (extension) { - window_bounds.set_width( + if (!params.override_bounds.IsEmpty()) { + browser_params.initial_bounds = params.override_bounds; + } else if (extension) { + browser_params.initial_bounds.set_width( extensions::AppLaunchInfo::GetLaunchWidth(extension)); - window_bounds.set_height( + browser_params.initial_bounds.set_height( extensions::AppLaunchInfo::GetLaunchHeight(extension)); } - if (!override_bounds.IsEmpty()) - window_bounds = override_bounds; - Browser::CreateParams params(type, profile, chrome::GetActiveDesktop()); - params.app_name = app_name; - params.initial_bounds = window_bounds; - params.initial_show_state = DetermineWindowShowState(profile, - container, - extension); + browser_params.initial_show_state = DetermineWindowShowState(profile, + params.container, + extension); Browser* browser = NULL; #if defined(OS_WIN) // On Windows 8's single window Metro mode we don't allow multiple Chrome // windows to be created. We instead attempt to reuse an existing Browser // window. - if (win8::IsSingleWindowMetroMode()) { - browser = chrome::FindBrowserWithProfile( - profile, chrome::HOST_DESKTOP_TYPE_NATIVE); - } + if (win8::IsSingleWindowMetroMode()) + browser = chrome::FindBrowserWithProfile(profile, params.desktop_type); + #endif if (!browser) - browser = new Browser(params); - - if (app_browser) - *app_browser = browser; + browser = new Browser(browser_params); WebContents* web_contents = chrome::AddSelectedTabWithURL( browser, url, content::PAGE_TRANSITION_AUTO_TOPLEVEL); @@ -178,19 +220,20 @@ WebContents* OpenApplicationWindow( return web_contents; } -WebContents* OpenApplicationTab(Profile* profile, - const Extension* extension, - const GURL& override_url, - WindowOpenDisposition disposition) { +WebContents* OpenApplicationTab(const AppLaunchParams& launch_params) { + Profile* const profile = launch_params.profile; + const extensions::Extension* extension = launch_params.extension; + WindowOpenDisposition disposition = launch_params.disposition; + Browser* browser = chrome::FindTabbedBrowser(profile, false, - chrome::GetActiveDesktop()); + launch_params.desktop_type); WebContents* contents = NULL; if (!browser) { // No browser for this profile, need to open a new one. browser = new Browser(Browser::CreateParams(Browser::TYPE_TABBED, profile, - chrome::GetActiveDesktop())); + launch_params.desktop_type)); browser->window()->Show(); // There's no current tab in this browser window, so add a new one. disposition = NEW_FOREGROUND_TAB; @@ -214,7 +257,7 @@ WebContents* OpenApplicationTab(Profile* profile, if (launch_type == ExtensionPrefs::LAUNCH_PINNED) add_type |= TabStripModel::ADD_PINNED; - GURL extension_url = UrlForExtension(extension, override_url); + GURL extension_url = UrlForExtension(extension, launch_params.override_url); chrome::NavigateParams params(browser, extension_url, content::PAGE_TRANSITION_AUTO_TOPLEVEL); params.tabstrip_add_types = add_type; @@ -271,72 +314,17 @@ WebContents* OpenApplicationTab(Profile* profile, return contents; } -// Attempts to launch a packaged app, prompting the user to enable it if -// necessary. If a prompt is required it will be shown inside the AppList. -// This class manages its own lifetime. -class EnableViaAppListFlow : public ExtensionEnableFlowDelegate { - public: - EnableViaAppListFlow(ExtensionService* service, - Profile* profile, - const std::string& extension_id, - const base::Closure& callback) - : service_(service), - profile_(profile), - extension_id_(extension_id), - callback_(callback) { - } - - virtual ~EnableViaAppListFlow() { - } - - void Run() { - DCHECK(!service_->IsExtensionEnabled(extension_id_)); - flow_.reset(new ExtensionEnableFlow(profile_, extension_id_, this)); - flow_->StartForCurrentlyNonexistentWindow( - base::Bind(&EnableViaAppListFlow::ShowAppList, base::Unretained(this))); - } - - private: - gfx::NativeWindow ShowAppList() { - AppListService::Get()->Show(); - return AppListService::Get()->GetAppListWindow(); - } - - // ExtensionEnableFlowDelegate overrides. - virtual void ExtensionEnableFlowFinished() OVERRIDE { - const Extension* extension = - service_->GetExtensionById(extension_id_, false); - if (!extension) - return; - callback_.Run(); - delete this; - } - - virtual void ExtensionEnableFlowAborted(bool user_initiated) OVERRIDE { - delete this; - } - - ExtensionService* service_; - Profile* profile_; - std::string extension_id_; - base::Closure callback_; - scoped_ptr<ExtensionEnableFlow> flow_; - - DISALLOW_COPY_AND_ASSIGN(EnableViaAppListFlow); -}; - WebContents* OpenEnabledApplication(const AppLaunchParams& params) { Profile* profile = params.profile; const extensions::Extension* extension = params.extension; - extension_misc::LaunchContainer container = params.container; - const GURL& override_url = params.override_url; - const gfx::Rect& override_bounds = params.override_bounds; + WebContents* tab = NULL; ExtensionPrefs* prefs = extensions::ExtensionSystem::Get(profile)-> extension_service()->extension_prefs(); prefs->SetActiveBit(extension->id(), true); - UMA_HISTOGRAM_ENUMERATION("Extensions.AppLaunchContainer", container, 100); + UMA_HISTOGRAM_ENUMERATION( + "Extensions.AppLaunchContainer", params.container, 100); if (extension->is_platform_app()) { #if !defined(OS_CHROMEOS) @@ -352,9 +340,10 @@ WebContents* OpenEnabledApplication(const AppLaunchParams& params) { if (!field_trial_value.empty()) { GURL gurl(l10n_util::GetStringFUTF8(IDS_APP_LAUNCH_NOT_SIGNED_IN_LINK, UTF8ToUTF16(extension->id()))); - chrome::NavigateParams params(profile, gurl, - content::PAGE_TRANSITION_LINK); - chrome::Navigate(¶ms); + chrome::NavigateParams navigate_params(profile, gurl, + content::PAGE_TRANSITION_LINK); + navigate_params.host_desktop_type = params.desktop_type; + chrome::Navigate(&navigate_params); return NULL; } } @@ -369,19 +358,17 @@ WebContents* OpenEnabledApplication(const AppLaunchParams& params) { // the onLaunched event. prefs->SetLastLaunchTime(extension->id(), base::Time::Now()); - switch (container) { + switch (params.container) { case extension_misc::LAUNCH_NONE: { NOTREACHED(); break; } case extension_misc::LAUNCH_PANEL: case extension_misc::LAUNCH_WINDOW: - tab = OpenApplicationWindow(profile, extension, container, - override_url, NULL, override_bounds); + tab = OpenApplicationWindow(params); break; case extension_misc::LAUNCH_TAB: { - tab = OpenApplicationTab(profile, extension, override_url, - params.disposition); + tab = OpenApplicationTab(params); break; } default: @@ -401,6 +388,7 @@ AppLaunchParams::AppLaunchParams(Profile* profile, extension(extension), container(container), disposition(disposition), + desktop_type(chrome::GetActiveDesktop()), override_url(), override_bounds(), command_line(NULL) {} @@ -412,6 +400,7 @@ AppLaunchParams::AppLaunchParams(Profile* profile, extension(extension), container(extension_misc::LAUNCH_NONE), disposition(disposition), + desktop_type(chrome::GetActiveDesktop()), override_url(), override_bounds(), command_line(NULL) { @@ -427,11 +416,13 @@ AppLaunchParams::AppLaunchParams(Profile* profile, AppLaunchParams::AppLaunchParams(Profile* profile, const extensions::Extension* extension, - int event_flags) + int event_flags, + chrome::HostDesktopType desktop_type) : profile(profile), extension(extension), container(extension_misc::LAUNCH_NONE), disposition(ui::DispositionFromEventFlags(event_flags)), + desktop_type(desktop_type), override_url(), override_bounds(), command_line(NULL) { @@ -475,14 +466,15 @@ void OpenApplicationWithReenablePrompt(const AppLaunchParams& params) { WebContents* OpenAppShortcutWindow(Profile* profile, const GURL& url, const gfx::Rect& override_bounds) { - Browser* app_browser; - WebContents* tab = OpenApplicationWindow( + AppLaunchParams launch_params( profile, NULL, // this is a URL app. No extension. extension_misc::LAUNCH_WINDOW, - url, - &app_browser, - override_bounds); + NEW_WINDOW); + launch_params.override_url = url; + launch_params.override_bounds = override_bounds; + + WebContents* tab = OpenApplicationWindow(launch_params); if (!tab) return NULL; diff --git a/chrome/browser/ui/extensions/application_launch.h b/chrome/browser/ui/extensions/application_launch.h index 63d30b8fbd..69c5d67262 100644 --- a/chrome/browser/ui/extensions/application_launch.h +++ b/chrome/browser/ui/extensions/application_launch.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_UI_EXTENSIONS_APPLICATION_LAUNCH_H_ #include "base/files/file_path.h" +#include "chrome/browser/ui/host_desktop.h" #include "chrome/common/extensions/extension_constants.h" #include "ui/base/window_open_disposition.h" #include "ui/gfx/rect.h" @@ -37,10 +38,13 @@ struct AppLaunchParams { WindowOpenDisposition disposition); // Helper to create AppLaunchParams using event flags that allows user to - // override the user-configured container using modifier keys. + // override the user-configured container using modifier keys, falling back to + // ExtensionPrefs::GetLaunchContainer() with no modifiers. |desktop_type| + // indicates the desktop upon which to launch (Ash or Native). AppLaunchParams(Profile* profile, const extensions::Extension* extension, - int event_flags); + int event_flags, + chrome::HostDesktopType desktop_type); // The profile to load the application from. Profile* profile; @@ -54,6 +58,9 @@ struct AppLaunchParams { // If container is TAB, this field controls how the tab is opened. WindowOpenDisposition disposition; + // The desktop type to launch on. Uses GetActiveDesktop() if unspecified. + chrome::HostDesktopType desktop_type; + // If non-empty, use override_url in place of the application's launch url. GURL override_url; diff --git a/chrome/browser/ui/gtk/apps/native_app_window_gtk.cc b/chrome/browser/ui/gtk/apps/native_app_window_gtk.cc index c9b429de1c..0600574386 100644 --- a/chrome/browser/ui/gtk/apps/native_app_window_gtk.cc +++ b/chrome/browser/ui/gtk/apps/native_app_window_gtk.cc @@ -95,29 +95,7 @@ NativeAppWindowGtk::NativeAppWindowGtk(ShellWindow* shell_window, if (always_on_top_) gtk_window_set_keep_above(window_, TRUE); - int min_width = params.minimum_size.width(); - int min_height = params.minimum_size.height(); - int max_width = params.maximum_size.width(); - int max_height = params.maximum_size.height(); - GdkGeometry hints; - int hints_mask = 0; - if (min_width || min_height) { - hints.min_height = min_height; - hints.min_width = min_width; - hints_mask |= GDK_HINT_MIN_SIZE; - } - if (max_width || max_height) { - hints.max_height = max_height ? max_height : G_MAXINT; - hints.max_width = max_width ? max_width : G_MAXINT; - hints_mask |= GDK_HINT_MAX_SIZE; - } - if (hints_mask) { - gtk_window_set_geometry_hints( - window_, - GTK_WIDGET(window_), - &hints, - static_cast<GdkWindowHints>(hints_mask)); - } + UpdateWindowMinMaxSize(); // In some (older) versions of compiz, raising top-level windows when they // are partially off-screen causes them to get snapped back on screen, not @@ -675,3 +653,30 @@ bool NativeAppWindowGtk::IsVisible() const { void NativeAppWindowGtk::HideWithApp() {} void NativeAppWindowGtk::ShowWithApp() {} + +void NativeAppWindowGtk::UpdateWindowMinMaxSize() { + GdkGeometry hints; + int hints_mask = 0; + if (shell_window_->size_constraints().HasMinimumSize()) { + gfx::Size min_size = shell_window_->size_constraints().GetMinimumSize(); + hints.min_height = min_size.height(); + hints.min_width = min_size.width(); + hints_mask |= GDK_HINT_MIN_SIZE; + } + if (shell_window_->size_constraints().HasMaximumSize()) { + gfx::Size max_size = shell_window_->size_constraints().GetMaximumSize(); + const int kUnboundedSize = ShellWindow::SizeConstraints::kUnboundedSize; + hints.max_height = max_size.height() == kUnboundedSize ? + G_MAXINT : max_size.height(); + hints.max_width = max_size.width() == kUnboundedSize ? + G_MAXINT : max_size.width(); + hints_mask |= GDK_HINT_MAX_SIZE; + } + if (hints_mask) { + gtk_window_set_geometry_hints( + window_, + GTK_WIDGET(window_), + &hints, + static_cast<GdkWindowHints>(hints_mask)); + } +} diff --git a/chrome/browser/ui/gtk/apps/native_app_window_gtk.h b/chrome/browser/ui/gtk/apps/native_app_window_gtk.h index 462256ee17..8ba631709e 100644 --- a/chrome/browser/ui/gtk/apps/native_app_window_gtk.h +++ b/chrome/browser/ui/gtk/apps/native_app_window_gtk.h @@ -83,6 +83,8 @@ class NativeAppWindowGtk : public apps::NativeAppWindow, virtual bool IsVisible() const OVERRIDE; virtual void HideWithApp() OVERRIDE; virtual void ShowWithApp() OVERRIDE; + // Calls gtk_window_set_geometry_hints with the current size constraints. + virtual void UpdateWindowMinMaxSize() OVERRIDE; // web_modal::WebContentsModalDialogHost implementation. virtual gfx::NativeView GetHostView() const OVERRIDE; diff --git a/chrome/browser/ui/gtk/browser_actions_toolbar_gtk.cc b/chrome/browser/ui/gtk/browser_actions_toolbar_gtk.cc index 34f300ac07..7f6049e685 100644 --- a/chrome/browser/ui/gtk/browser_actions_toolbar_gtk.cc +++ b/chrome/browser/ui/gtk/browser_actions_toolbar_gtk.cc @@ -7,6 +7,7 @@ #include <gtk/gtk.h> #include <algorithm> +#include <utility> #include <vector> #include "base/bind.h" @@ -21,6 +22,7 @@ #include "chrome/browser/extensions/extension_context_menu_model.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/sessions/session_tab_helper.h" #include "chrome/browser/ui/browser.h" @@ -252,21 +254,25 @@ class BrowserActionButton : public content::NotificationObserver, } private: - // Activate the browser action. - void Activate(GtkWidget* widget) { + // Activate the browser action. Returns true if a popup was shown. Showing the + // popup will grant tab permissions if |should_grant| is true. Popup's shown + // via an API should not grant permissions. + bool Activate(GtkWidget* widget, bool should_grant) { ExtensionToolbarModel* model = toolbar_->model(); const Extension* extension = extension_; Browser* browser = toolbar_->browser(); GURL popup_url; - switch (model->ExecuteBrowserAction(extension, browser, &popup_url)) { + switch (model->ExecuteBrowserAction( + extension, browser, &popup_url, should_grant)) { case ExtensionToolbarModel::ACTION_NONE: break; case ExtensionToolbarModel::ACTION_SHOW_POPUP: ExtensionPopupGtk::Show(popup_url, browser, widget, ExtensionPopupGtk::SHOW); - break; + return true; } + return false; } // MenuGtk::Delegate implementation. @@ -323,7 +329,7 @@ class BrowserActionButton : public content::NotificationObserver, static void OnClicked(GtkWidget* widget, BrowserActionButton* button) { if (button->enabled_) - button->Activate(widget); + button->Activate(widget, true); } static gboolean OnExposeEvent(GtkWidget* widget, @@ -364,7 +370,7 @@ class BrowserActionButton : public content::NotificationObserver, // The anchor might be in the overflow menu. Then we point to the chevron. if (!gtk_widget_get_visible(anchor)) anchor = button->toolbar_->chevron(); - button->Activate(anchor); + button->Activate(anchor, true); return TRUE; } @@ -669,14 +675,17 @@ void BrowserActionsToolbarGtk::CreateButtonForExtension( UpdateVisibility(); } -GtkWidget* BrowserActionsToolbarGtk::GetBrowserActionWidget( +BrowserActionButton* BrowserActionsToolbarGtk::GetBrowserActionButton( const Extension* extension) { ExtensionButtonMap::iterator it = extension_button_map_.find( extension->id()); - if (it == extension_button_map_.end()) - return NULL; + return it == extension_button_map_.end() ? NULL : it->second.get(); +} - return it->second.get()->widget(); +GtkWidget* BrowserActionsToolbarGtk::GetBrowserActionWidget( + const Extension* extension) { + BrowserActionButton* button = GetBrowserActionButton(extension); + return button == NULL ? NULL : button->widget(); } void BrowserActionsToolbarGtk::RemoveButtonForExtension( @@ -694,8 +703,9 @@ bool BrowserActionsToolbarGtk::ShouldDisplayBrowserAction( const Extension* extension) { // Only display incognito-enabled extensions while in incognito mode. return (!profile_->IsOffTheRecord() || - extensions::ExtensionSystem::Get(profile_)->extension_service()-> - IsIncognitoEnabled(extension->id())); + extension_util:: IsIncognitoEnabled( + extension->id(), + extensions::ExtensionSystem::Get(profile_)->extension_service())); } void BrowserActionsToolbarGtk::HidePopup() { @@ -768,6 +778,24 @@ void BrowserActionsToolbarGtk::BrowserActionMoved(const Extension* extension, gtk_box_reorder_child(GTK_BOX(button_hbox_.get()), button_widget, index); } +bool BrowserActionsToolbarGtk::BrowserActionShowPopup( + const Extension* extension) { + // Do not override other popups and only show in active window. + if (ExtensionPopupGtk::get_current_extension_popup() || + !browser_->window()->IsActive()) { + return false; + } + + BrowserActionButton* button = GetBrowserActionButton(extension); + if (button == NULL || button->widget() == NULL) + return false; + + GtkWidget* anchor = button->widget(); + if (!gtk_widget_get_visible(anchor)) + anchor = button->toolbar_->chevron(); + return button->Activate(anchor, false); +} + void BrowserActionsToolbarGtk::ModelLoaded() { SetContainerWidth(); } @@ -807,7 +835,8 @@ void BrowserActionsToolbarGtk::ExecuteCommand(int command_id, int event_flags) { const Extension* extension = model_->toolbar_items()[command_id].get(); GURL popup_url; - switch (model_->ExecuteBrowserAction(extension, browser(), &popup_url)) { + switch (model_->ExecuteBrowserAction( + extension, browser(), &popup_url, true)) { case ExtensionToolbarModel::ACTION_NONE: break; case ExtensionToolbarModel::ACTION_SHOW_POPUP: @@ -1077,13 +1106,13 @@ gboolean BrowserActionsToolbarGtk::OnOverflowMenuButtonPress( item_index = model_->IncognitoIndexToOriginal(item_index); const Extension* extension = model_->toolbar_items()[item_index].get(); - ExtensionButtonMap::iterator it = extension_button_map_.find(extension->id()); - if (it == extension_button_map_.end()) { + BrowserActionButton* button = GetBrowserActionButton(extension); + if (button == NULL) { NOTREACHED(); return FALSE; } - MenuGtk* menu = it->second.get()->GetContextMenu(); + MenuGtk* menu = button->GetContextMenu(); if (!menu) return FALSE; diff --git a/chrome/browser/ui/gtk/browser_actions_toolbar_gtk.h b/chrome/browser/ui/gtk/browser_actions_toolbar_gtk.h index 8cb03c79e5..cb4209b930 100644 --- a/chrome/browser/ui/gtk/browser_actions_toolbar_gtk.h +++ b/chrome/browser/ui/gtk/browser_actions_toolbar_gtk.h @@ -52,6 +52,8 @@ class BrowserActionsToolbarGtk : public ExtensionToolbarModel::Observer, // |extension|. Used in positioning the ExtensionInstalledBubble for // BrowserActions. GtkWidget* GetBrowserActionWidget(const extensions::Extension* extension); + BrowserActionButton* GetBrowserActionButton( + const extensions::Extension* extension); int button_count() { return extension_button_map_.size(); } @@ -118,6 +120,8 @@ class BrowserActionsToolbarGtk : public ExtensionToolbarModel::Observer, const extensions::Extension* extension) OVERRIDE; virtual void BrowserActionMoved(const extensions::Extension* extension, int index) OVERRIDE; + virtual bool BrowserActionShowPopup( + const extensions::Extension* extension) OVERRIDE; virtual void ModelLoaded() OVERRIDE; // gfx::AnimationDelegate implementation. diff --git a/chrome/browser/ui/gtk/browser_window_gtk.cc b/chrome/browser/ui/gtk/browser_window_gtk.cc index e108d45489..e17c22b839 100644 --- a/chrome/browser/ui/gtk/browser_window_gtk.cc +++ b/chrome/browser/ui/gtk/browser_window_gtk.cc @@ -1173,11 +1173,6 @@ void BrowserWindowGtk::HandleKeyboardEvent( gtk_window_activate_key(window_, os_event); } -void BrowserWindowGtk::ShowCreateChromeAppShortcutsDialog( - Profile* profile, const extensions::Extension* app) { - CreateChromeApplicationShortcutsDialogGtk::Show(window_, profile, app); -} - void BrowserWindowGtk::Cut() { gtk_window_util::DoCut( window_, browser_->tab_strip_model()->GetActiveWebContents()); diff --git a/chrome/browser/ui/gtk/browser_window_gtk.h b/chrome/browser/ui/gtk/browser_window_gtk.h index 5ad7b58fa2..9aeac84acd 100644 --- a/chrome/browser/ui/gtk/browser_window_gtk.h +++ b/chrome/browser/ui/gtk/browser_window_gtk.h @@ -162,9 +162,6 @@ class BrowserWindowGtk bool* is_keyboard_shortcut) OVERRIDE; virtual void HandleKeyboardEvent( const content::NativeWebKeyboardEvent& event) OVERRIDE; - virtual void ShowCreateChromeAppShortcutsDialog( - Profile* profile, - const extensions::Extension* app) OVERRIDE; virtual void Cut() OVERRIDE; virtual void Copy() OVERRIDE; virtual void Paste() OVERRIDE; diff --git a/chrome/browser/ui/gtk/create_application_shortcuts_dialog_gtk.cc b/chrome/browser/ui/gtk/create_application_shortcuts_dialog_gtk.cc index c3035cfdbc..b2e14a9619 100644 --- a/chrome/browser/ui/gtk/create_application_shortcuts_dialog_gtk.cc +++ b/chrome/browser/ui/gtk/create_application_shortcuts_dialog_gtk.cc @@ -56,14 +56,16 @@ void ShowCreateWebAppShortcutsDialog(gfx::NativeWindow parent_window, new CreateWebApplicationShortcutsDialogGtk(parent_window, web_contents); } -} // namespace chrome - -void CreateChromeApplicationShortcutsDialogGtk::Show(GtkWindow* parent, - Profile* profile, - const Extension* app) { - new CreateChromeApplicationShortcutsDialogGtk(parent, profile, app); +void ShowCreateChromeAppShortcutsDialog( + gfx::NativeWindow parent_window, + Profile* profile, + const extensions::Extension* app, + const base::Closure& close_callback) { + new CreateChromeApplicationShortcutsDialogGtk(parent_window, profile, app, + close_callback); } +} // namespace chrome CreateApplicationShortcutsDialogGtk::CreateApplicationShortcutsDialogGtk( GtkWindow* parent) @@ -314,10 +316,12 @@ CreateChromeApplicationShortcutsDialogGtk:: CreateChromeApplicationShortcutsDialogGtk( GtkWindow* parent, Profile* profile, - const Extension* app) - : CreateApplicationShortcutsDialogGtk(parent), - app_(app), - profile_path_(profile->GetPath()) { + const Extension* app, + const base::Closure& close_callback) + : CreateApplicationShortcutsDialogGtk(parent), + app_(app), + profile_path_(profile->GetPath()), + close_callback_(close_callback) { // Place Chrome app shortcuts in the "Chrome Apps" submenu. shortcut_menu_subdir_ = web_app::GetAppShortcutsSubdirName(); @@ -330,6 +334,12 @@ CreateChromeApplicationShortcutsDialogGtk:: this)); } +CreateChromeApplicationShortcutsDialogGtk:: + ~CreateChromeApplicationShortcutsDialogGtk() { + if (!close_callback_.is_null()) + close_callback_.Run(); +} + // Called when the app's ShortcutInfo (with icon) is loaded. void CreateChromeApplicationShortcutsDialogGtk::OnShortcutInfoLoaded( const ShellIntegration::ShortcutInfo& shortcut_info) { diff --git a/chrome/browser/ui/gtk/create_application_shortcuts_dialog_gtk.h b/chrome/browser/ui/gtk/create_application_shortcuts_dialog_gtk.h index 1491b09b19..434bdf3648 100644 --- a/chrome/browser/ui/gtk/create_application_shortcuts_dialog_gtk.h +++ b/chrome/browser/ui/gtk/create_application_shortcuts_dialog_gtk.h @@ -112,17 +112,14 @@ class CreateWebApplicationShortcutsDialogGtk class CreateChromeApplicationShortcutsDialogGtk : public CreateApplicationShortcutsDialogGtk { public: - // Displays the dialog box to create application shortcuts for |app|. - static void Show(GtkWindow* parent, Profile* profile, - const extensions::Extension* app); - - CreateChromeApplicationShortcutsDialogGtk(GtkWindow* parent, - Profile* profile, - const extensions::Extension* app); - + CreateChromeApplicationShortcutsDialogGtk( + GtkWindow* parent, + Profile* profile, + const extensions::Extension* app, + const base::Closure& close_callback); protected: - virtual ~CreateChromeApplicationShortcutsDialogGtk() {} + virtual ~CreateChromeApplicationShortcutsDialogGtk(); virtual void CreateDesktopShortcut( const ShellIntegration::ShortcutInfo& shortcut_info, @@ -132,9 +129,9 @@ class CreateChromeApplicationShortcutsDialogGtk void OnShortcutInfoLoaded( const ShellIntegration::ShortcutInfo& shortcut_info); - private: const extensions::Extension* app_; base::FilePath profile_path_; + base::Closure close_callback_; DISALLOW_COPY_AND_ASSIGN(CreateChromeApplicationShortcutsDialogGtk); }; diff --git a/chrome/browser/ui/gtk/download/download_item_gtk.cc b/chrome/browser/ui/gtk/download/download_item_gtk.cc index 34f1f2fda8..6a9e194dd4 100644 --- a/chrome/browser/ui/gtk/download/download_item_gtk.cc +++ b/chrome/browser/ui/gtk/download/download_item_gtk.cc @@ -237,13 +237,15 @@ DownloadItemGtk::DownloadItemGtk(DownloadShelfGtk* parent_shelf, gtk_util::CenterWidgetInHBox(dangerous_hbox_.get(), dangerous_decline, false, 0); - // Create the ok button. - GtkWidget* dangerous_accept = gtk_button_new_with_label( - UTF16ToUTF8(download_model_.GetWarningConfirmButtonText()).c_str()); - g_signal_connect(dangerous_accept, "clicked", - G_CALLBACK(OnDangerousAcceptThunk), this); - gtk_util::CenterWidgetInHBox(dangerous_hbox_.get(), dangerous_accept, false, - 0); + // Create the ok button, if this is the kind that can be bypassed. + if (!download_model_.IsMalicious()) { + GtkWidget* dangerous_accept = gtk_button_new_with_label( + UTF16ToUTF8(download_model_.GetWarningConfirmButtonText()).c_str()); + g_signal_connect(dangerous_accept, "clicked", + G_CALLBACK(OnDangerousAcceptThunk), this); + gtk_util::CenterWidgetInHBox( + dangerous_hbox_.get(), dangerous_accept, false, 0); + } // Put it in an alignment so that padding will be added on the left and // right. @@ -630,15 +632,16 @@ void DownloadItemGtk::UpdateDangerWarning() { void DownloadItemGtk::UpdateDangerIcon() { if (theme_service_->UsingNativeTheme()) { - const char* stock = download_model_.IsMalicious() ? + const char* stock = download_model_.MightBeMalicious() ? GTK_STOCK_DIALOG_ERROR : GTK_STOCK_DIALOG_WARNING; gtk_image_set_from_stock( GTK_IMAGE(dangerous_image_), stock, GTK_ICON_SIZE_SMALL_TOOLBAR); } else { // Set the warning icon. ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); - int pixbuf_id = download_model_.IsMalicious() ? IDR_SAFEBROWSING_WARNING - : IDR_WARNING; + int pixbuf_id = + download_model_.MightBeMalicious() ? IDR_SAFEBROWSING_WARNING + : IDR_WARNING; gtk_image_set_from_pixbuf(GTK_IMAGE(dangerous_image_), rb.GetNativeImageNamed(pixbuf_id).ToGdkPixbuf()); } diff --git a/chrome/browser/ui/gtk/first_run_dialog.cc b/chrome/browser/ui/gtk/first_run_dialog.cc index eca4ef29af..8480f31216 100644 --- a/chrome/browser/ui/gtk/first_run_dialog.cc +++ b/chrome/browser/ui/gtk/first_run_dialog.cc @@ -10,7 +10,6 @@ #include "base/i18n/rtl.h" #include "base/message_loop/message_loop.h" #include "base/strings/utf_string_conversions.h" -#include "chrome/app/breakpad_linux.h" #include "chrome/browser/first_run/first_run_dialog.h" #include "chrome/browser/platform_util.h" #include "chrome/browser/process_singleton.h" @@ -20,6 +19,7 @@ #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "chrome/installer/util/google_update_settings.h" +#include "components/breakpad/app/breakpad_linux.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "grit/locale_settings.h" @@ -164,7 +164,7 @@ void FirstRunDialog::OnResponseDialog(GtkWidget* widget, int response) { if (report_crashes_ && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(report_crashes_))) { if (GoogleUpdateSettings::SetCollectStatsConsent(true)) - InitCrashReporter(); + breakpad::InitCrashReporter(); } else { GoogleUpdateSettings::SetCollectStatsConsent(false); } diff --git a/chrome/browser/ui/gtk/global_menu_bar.cc b/chrome/browser/ui/gtk/global_menu_bar.cc index e10164a904..10336fb4cb 100644 --- a/chrome/browser/ui/gtk/global_menu_bar.cc +++ b/chrome/browser/ui/gtk/global_menu_bar.cc @@ -133,6 +133,7 @@ GlobalMenuBarCommand tools_menu[] = { { IDS_VIEW_SOURCE, IDC_VIEW_SOURCE }, { IDS_DEV_TOOLS, IDC_DEV_TOOLS }, { IDS_DEV_TOOLS_CONSOLE, IDC_DEV_TOOLS_CONSOLE }, + { IDS_DEV_TOOLS_DEVICES, IDC_DEV_TOOLS_DEVICES }, { MENU_END, MENU_END } }; diff --git a/chrome/browser/ui/panels/panel_host.cc b/chrome/browser/ui/panels/panel_host.cc index 66ce28164a..73dd5a21a8 100644 --- a/chrome/browser/ui/panels/panel_host.cc +++ b/chrome/browser/ui/panels/panel_host.cc @@ -9,6 +9,7 @@ #include "base/message_loop/message_loop.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chrome_page_zoom.h" +#include "chrome/browser/extensions/extension_web_contents_observer.h" #include "chrome/browser/extensions/window_controller.h" #include "chrome/browser/favicon/favicon_tab_helper.h" #include "chrome/browser/profiles/profile.h" @@ -63,6 +64,8 @@ void PanelHost::Init(const GURL& url) { FaviconTabHelper::CreateForWebContents(web_contents_.get()); PrefsTabHelper::CreateForWebContents(web_contents_.get()); + extensions::ExtensionWebContentsObserver::CreateForWebContents( + web_contents_.get()); web_contents_->GetController().LoadURL( url, content::Referrer(), content::PAGE_TRANSITION_LINK, std::string()); diff --git a/chrome/browser/ui/prefs/prefs_tab_helper.cc b/chrome/browser/ui/prefs/prefs_tab_helper.cc index db0b657a22..7e1691453c 100644 --- a/chrome/browser/ui/prefs/prefs_tab_helper.cc +++ b/chrome/browser/ui/prefs/prefs_tab_helper.cc @@ -42,78 +42,6 @@ DEFINE_WEB_CONTENTS_USER_DATA_KEY(PrefsTabHelper); namespace { -// Registers prefs only used for migration. -void RegisterPrefsToMigrate(user_prefs::PrefRegistrySyncable* prefs) { - prefs->RegisterLocalizedStringPref( - prefs::kWebKitOldStandardFontFamily, - IDS_STANDARD_FONT_FAMILY, - user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); - prefs->RegisterLocalizedStringPref( - prefs::kWebKitOldFixedFontFamily, - IDS_FIXED_FONT_FAMILY, - user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); - prefs->RegisterLocalizedStringPref( - prefs::kWebKitOldSerifFontFamily, - IDS_SERIF_FONT_FAMILY, - user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); - prefs->RegisterLocalizedStringPref( - prefs::kWebKitOldSansSerifFontFamily, - IDS_SANS_SERIF_FONT_FAMILY, - user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); - prefs->RegisterLocalizedStringPref( - prefs::kWebKitOldCursiveFontFamily, - IDS_CURSIVE_FONT_FAMILY, - user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); - prefs->RegisterLocalizedStringPref( - prefs::kWebKitOldFantasyFontFamily, - IDS_FANTASY_FONT_FAMILY, - user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); - prefs->RegisterLocalizedStringPref( - prefs::kGlobalDefaultCharset, - IDS_DEFAULT_ENCODING, - user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); - prefs->RegisterLocalizedIntegerPref( - prefs::kWebKitGlobalDefaultFontSize, - IDS_DEFAULT_FONT_SIZE, - user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); - prefs->RegisterLocalizedIntegerPref( - prefs::kWebKitGlobalDefaultFixedFontSize, - IDS_DEFAULT_FIXED_FONT_SIZE, - user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); - prefs->RegisterLocalizedIntegerPref( - prefs::kWebKitGlobalMinimumFontSize, - IDS_MINIMUM_FONT_SIZE, - user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); - prefs->RegisterLocalizedIntegerPref( - prefs::kWebKitGlobalMinimumLogicalFontSize, - IDS_MINIMUM_LOGICAL_FONT_SIZE, - user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); - prefs->RegisterLocalizedStringPref( - prefs::kWebKitGlobalStandardFontFamily, - IDS_STANDARD_FONT_FAMILY, - user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); - prefs->RegisterLocalizedStringPref( - prefs::kWebKitGlobalFixedFontFamily, - IDS_FIXED_FONT_FAMILY, - user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); - prefs->RegisterLocalizedStringPref( - prefs::kWebKitGlobalSerifFontFamily, - IDS_SERIF_FONT_FAMILY, - user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); - prefs->RegisterLocalizedStringPref( - prefs::kWebKitGlobalSansSerifFontFamily, - IDS_SANS_SERIF_FONT_FAMILY, - user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); - prefs->RegisterLocalizedStringPref( - prefs::kWebKitGlobalCursiveFontFamily, - IDS_CURSIVE_FONT_FAMILY, - user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); - prefs->RegisterLocalizedStringPref( - prefs::kWebKitGlobalFantasyFontFamily, - IDS_FANTASY_FONT_FAMILY, - user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); -} - // The list of prefs we want to observe. const char* kPrefsToObserve[] = { prefs::kDefaultCharset, @@ -138,12 +66,16 @@ const char* kPrefsToObserve[] = { const int kPrefsToObserveLength = arraysize(kPrefsToObserve); +#if !defined(OS_ANDROID) // Registers a preference under the path |pref_name| for each script used for // per-script font prefs. // For example, for WEBKIT_WEBPREFS_FONTS_SERIF ("fonts.serif"): // "fonts.serif.Arab", "fonts.serif.Hang", etc. are registered. // |fonts_with_defaults| contains all |pref_names| already registered since they // have a specified default value. +// On Android there are no default values for these properties and there is no +// way to set them (because extensions are not supported so the Font Settings +// API cannot be used), so we can avoid registering them altogether. void RegisterFontFamilyPrefs(user_prefs::PrefRegistrySyncable* registry, const std::set<std::string>& fonts_with_defaults) { @@ -177,7 +109,6 @@ ALL_FONT_SCRIPTS(WEBKIT_WEBPREFS_FONTS_STANDARD) } } -#if !defined(OS_ANDROID) // Registers |obs| to observe per-script font prefs under the path |map_name|. // On android, there's no exposed way to change these prefs, so we can save // ~715KB of heap and some startup cycles by avoiding observing these prefs @@ -330,60 +261,6 @@ UScriptCode GetScriptOfBrowserLocale() { return GetScriptForFontPrefMatching(code); } -const struct { - const char* from; - const char* to; -} kPrefNamesToMigrate[] = { - // Migrate prefs like "webkit.webprefs.standard_font_family" to - // "webkit.webprefs.fonts.standard.Zyyy". This moves the formerly - // "non-per-script" font prefs into the per-script font pref maps, as the - // entry for the "Common" script (Zyyy is the ISO 15924 script code for the - // Common script). The |from| prefs will exist if the migration to global - // prefs (for the per-tab pref mechanism, which has since been removed) never - // occurred. - { prefs::kWebKitOldCursiveFontFamily, - prefs::kWebKitCursiveFontFamily }, - { prefs::kWebKitOldFantasyFontFamily, - prefs::kWebKitFantasyFontFamily }, - { prefs::kWebKitOldFixedFontFamily, - prefs::kWebKitFixedFontFamily }, - { prefs::kWebKitOldSansSerifFontFamily, - prefs::kWebKitSansSerifFontFamily }, - { prefs::kWebKitOldSerifFontFamily, - prefs::kWebKitSerifFontFamily }, - { prefs::kWebKitOldStandardFontFamily, - prefs::kWebKitStandardFontFamily }, - - // Migrate "global" prefs. These will exist if the migration to global prefs - // (for the per-tab pref mechanism, which has since been removed) occurred. - // In addition, this moves the formerly "non-per-script" font prefs into the - // per-script font pref maps, as above. - { prefs::kGlobalDefaultCharset, - prefs::kDefaultCharset }, - { prefs::kWebKitGlobalDefaultFixedFontSize, - prefs::kWebKitDefaultFixedFontSize }, - { prefs::kWebKitGlobalDefaultFontSize, - prefs::kWebKitDefaultFontSize }, - { prefs::kWebKitGlobalMinimumFontSize, - prefs::kWebKitMinimumFontSize }, - { prefs::kWebKitGlobalMinimumLogicalFontSize, - prefs::kWebKitMinimumLogicalFontSize }, - { prefs::kWebKitGlobalCursiveFontFamily, - prefs::kWebKitCursiveFontFamily }, - { prefs::kWebKitGlobalFantasyFontFamily, - prefs::kWebKitFantasyFontFamily }, - { prefs::kWebKitGlobalFixedFontFamily, - prefs::kWebKitFixedFontFamily }, - { prefs::kWebKitGlobalSansSerifFontFamily, - prefs::kWebKitSansSerifFontFamily }, - { prefs::kWebKitGlobalSerifFontFamily, - prefs::kWebKitSerifFontFamily }, - { prefs::kWebKitGlobalStandardFontFamily, - prefs::kWebKitStandardFontFamily } -}; - -const int kPrefsToMigrateLength = ARRAYSIZE_UNSAFE(kPrefNamesToMigrate); - // Sets a font family pref in |prefs| to |pref_value|. void OverrideFontFamily(WebPreferences* prefs, const std::string& generic_family, @@ -564,7 +441,11 @@ void PrefsTabHelper::RegisterProfilePrefs( #if defined(OS_ANDROID) registry->RegisterDoublePref( prefs::kWebKitFontScaleFactor, - pref_defaults.font_scale_factor, + 1.0, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterBooleanPref( + prefs::kWebKitFontScaleFactorQuirk, + true, user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); registry->RegisterBooleanPref( prefs::kWebKitForceEnableZoom, @@ -612,8 +493,10 @@ void PrefsTabHelper::RegisterProfilePrefs( } } - // Register font prefs that don't have defaults. + // Register per-script font prefs that don't have defaults. +#if !defined(OS_ANDROID) RegisterFontFamilyPrefs(registry, fonts_with_defaults); +#endif registry->RegisterLocalizedIntegerPref( prefs::kWebKitDefaultFontSize, @@ -643,19 +526,6 @@ void PrefsTabHelper::RegisterProfilePrefs( prefs::kRecentlySelectedEncoding, std::string(), user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); - - RegisterPrefsToMigrate(registry); -} - -void PrefsTabHelper::MigrateUserPrefs(PrefService* prefs) { - for (int i = 0; i < kPrefsToMigrateLength; ++i) { - const PrefService::Preference* pref = - prefs->FindPreference(kPrefNamesToMigrate[i].from); - if (pref && !pref->IsDefaultValue()) { - prefs->Set(kPrefNamesToMigrate[i].to, *pref->GetValue()); - prefs->ClearPref(kPrefNamesToMigrate[i].from); - } - } } void PrefsTabHelper::Observe(int type, diff --git a/chrome/browser/ui/prefs/prefs_tab_helper.h b/chrome/browser/ui/prefs/prefs_tab_helper.h index 42ffd36289..055a2f1056 100644 --- a/chrome/browser/ui/prefs/prefs_tab_helper.h +++ b/chrome/browser/ui/prefs/prefs_tab_helper.h @@ -33,7 +33,6 @@ class PrefsTabHelper : public content::NotificationObserver, static void InitIncognitoUserPrefStore(OverlayUserPrefStore* pref_store); static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); - static void MigrateUserPrefs(PrefService* prefs); protected: // Update the RenderView's WebPreferences. Exposed as protected for testing. diff --git a/chrome/browser/ui/prefs/prefs_tab_helper_browsertest.cc b/chrome/browser/ui/prefs/prefs_tab_helper_browsertest.cc index d6cd780695..bb82f51a3c 100644 --- a/chrome/browser/ui/prefs/prefs_tab_helper_browsertest.cc +++ b/chrome/browser/ui/prefs/prefs_tab_helper_browsertest.cc @@ -20,7 +20,7 @@ class PrefsTabHelperBrowserTest : public InProcessBrowserTest { PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory); return test_data_directory .AppendASCII("profiles") - .AppendASCII("webkit_global_migration") + .AppendASCII("web_prefs") .AppendASCII("Default") .Append(chrome::kPreferencesFilename); } @@ -34,15 +34,15 @@ class PrefsTabHelperBrowserTest : public InProcessBrowserTest { LOG(ERROR) << "Can't create " << default_profile.MaybeAsASCII(); return false; } - base::FilePath non_global_pref_file = GetPreferencesFilePath(); - if (!base::PathExists(non_global_pref_file)) { - LOG(ERROR) << "Doesn't exist " << non_global_pref_file.MaybeAsASCII(); + base::FilePath pref_file = GetPreferencesFilePath(); + if (!base::PathExists(pref_file)) { + LOG(ERROR) << "Doesn't exist " << pref_file.MaybeAsASCII(); return false; } base::FilePath default_pref_file = default_profile.Append(chrome::kPreferencesFilename); - if (!base::CopyFile(non_global_pref_file, default_pref_file)) { - LOG(ERROR) << "Copy error from " << non_global_pref_file.MaybeAsASCII() + if (!base::CopyFile(pref_file, default_pref_file)) { + LOG(ERROR) << "Copy error from " << pref_file.MaybeAsASCII() << " to " << default_pref_file.MaybeAsASCII(); return false; } @@ -57,132 +57,24 @@ class PrefsTabHelperBrowserTest : public InProcessBrowserTest { } }; -// This tests migration like: -// webkit.webprefs.standard_font_family -> webkit.webprefs.fonts.standard.Zyyy -// This migration moves the formerly "non-per-script" font prefs into the -// per-script font maps, as the entry for "Common" script (Zyyy is the ISO 15924 -// script code for the Common script). -// -// In addition, it tests that the former migration of -// webkit.webprefs.blahblah -> webkit.webprefs.global.blahblah -// no longer occurs. -IN_PROC_BROWSER_TEST_F(PrefsTabHelperBrowserTest, PrefsAreMigratedToFontMap) { +// Tests that a sampling of web prefs are registered and ones with values in the +// test user preferences file take on those values. +IN_PROC_BROWSER_TEST_F(PrefsTabHelperBrowserTest, WebPrefs) { PrefService* prefs = browser()->profile()->GetPrefs(); EXPECT_TRUE(prefs->FindPreference( - prefs::kGlobalDefaultCharset)->IsDefaultValue()); + prefs::kWebKitCursiveFontFamily)->IsDefaultValue()); EXPECT_TRUE(prefs->FindPreference( - prefs::kWebKitGlobalDefaultFontSize)->IsDefaultValue()); + prefs::kWebKitSerifFontFamily)->IsDefaultValue()); EXPECT_TRUE(prefs->FindPreference( - prefs::kWebKitGlobalDefaultFixedFontSize)->IsDefaultValue()); - EXPECT_TRUE(prefs->FindPreference( - prefs::kWebKitGlobalMinimumFontSize)->IsDefaultValue()); - EXPECT_TRUE(prefs->FindPreference( - prefs::kWebKitGlobalMinimumLogicalFontSize)->IsDefaultValue()); - EXPECT_TRUE(prefs->FindPreference( - prefs::kWebKitOldCursiveFontFamily)->IsDefaultValue()); - EXPECT_TRUE(prefs->FindPreference( - prefs::kWebKitOldFantasyFontFamily)->IsDefaultValue()); - EXPECT_TRUE(prefs->FindPreference( - prefs::kWebKitOldFixedFontFamily)->IsDefaultValue()); - EXPECT_TRUE(prefs->FindPreference( - prefs::kWebKitOldSansSerifFontFamily)->IsDefaultValue()); - EXPECT_TRUE(prefs->FindPreference( - prefs::kWebKitOldSerifFontFamily)->IsDefaultValue()); - EXPECT_TRUE(prefs->FindPreference( - prefs::kWebKitOldStandardFontFamily)->IsDefaultValue()); + prefs::kWebKitSerifFontFamilyJapanese)->IsDefaultValue()); EXPECT_EQ("ISO-8859-1", prefs->GetString(prefs::kDefaultCharset)); - EXPECT_EQ(42, prefs->GetInteger(prefs::kWebKitDefaultFontSize)); - EXPECT_EQ(42, prefs->GetInteger(prefs::kWebKitDefaultFixedFontSize)); - EXPECT_EQ(42, prefs->GetInteger(prefs::kWebKitMinimumFontSize)); - EXPECT_EQ(42, prefs->GetInteger(prefs::kWebKitMinimumLogicalFontSize)); - EXPECT_EQ("CursiveFontFamily", - prefs->GetString(prefs::kWebKitCursiveFontFamily)); - EXPECT_EQ("FantasyFontFamily", - prefs->GetString(prefs::kWebKitFantasyFontFamily)); - // PictographFontFamily was added after the migration, so it never exists - // in the old format (and consequently isn't in the test Preferences file). - // So it doesn't need to be tested here. - EXPECT_EQ("FixedFontFamily", - prefs->GetString(prefs::kWebKitFixedFontFamily)); - EXPECT_EQ("SansSerifFontFamily", - prefs->GetString(prefs::kWebKitSansSerifFontFamily)); - EXPECT_EQ("SerifFontFamily", - prefs->GetString(prefs::kWebKitSerifFontFamily)); - EXPECT_EQ("StandardFontFamily", - prefs->GetString(prefs::kWebKitStandardFontFamily)); + EXPECT_EQ(16, prefs->GetInteger(prefs::kWebKitDefaultFontSize)); + EXPECT_EQ("Nanum Gothic", + prefs->GetString(prefs::kWebKitStandardFontFamilyKorean)); + EXPECT_EQ("Tinos", prefs->GetString(prefs::kWebKitStandardFontFamily)); + EXPECT_EQ("DejaVu Sans", prefs->GetString(prefs::kWebKitSansSerifFontFamily)); }; -class PrefsTabHelperBrowserTest2 : public PrefsTabHelperBrowserTest { - protected: - virtual base::FilePath GetPreferencesFilePath() OVERRIDE { - base::FilePath test_data_directory; - PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory); - return test_data_directory - .AppendASCII("profiles") - .AppendASCII("webkit_global_reverse_migration") - .AppendASCII("Default") - .Append(chrome::kPreferencesFilename); - } -}; -// This tests migration like: -// webkit.webprefs.global.blahblah -> webkit.webprefs.blahblah -// This undoes the migration to "global" names (originally done for the per-tab -// pref mechanism, which has since been removed). -// -// In addition it tests the migration for font families: -// webkit.webprefs.global.standard_font_family -> -// webkit.webprefs.fonts.standard.Zyyy -// This moves the formerly "non-per-script" font prefs into the per-script font -// maps, as described in the comment for PrefsAreMigratedToFontMap. -IN_PROC_BROWSER_TEST_F(PrefsTabHelperBrowserTest2, GlobalPrefsAreMigrated) { - PrefService* prefs = browser()->profile()->GetPrefs(); - - EXPECT_TRUE(prefs->FindPreference( - prefs::kGlobalDefaultCharset)->IsDefaultValue()); - EXPECT_TRUE(prefs->FindPreference( - prefs::kWebKitGlobalDefaultFontSize)->IsDefaultValue()); - EXPECT_TRUE(prefs->FindPreference( - prefs::kWebKitGlobalDefaultFixedFontSize)->IsDefaultValue()); - EXPECT_TRUE(prefs->FindPreference( - prefs::kWebKitGlobalMinimumFontSize)->IsDefaultValue()); - EXPECT_TRUE(prefs->FindPreference( - prefs::kWebKitGlobalMinimumLogicalFontSize)->IsDefaultValue()); - EXPECT_TRUE(prefs->FindPreference( - prefs::kWebKitGlobalCursiveFontFamily)->IsDefaultValue()); - EXPECT_TRUE(prefs->FindPreference( - prefs::kWebKitGlobalFantasyFontFamily)->IsDefaultValue()); - EXPECT_TRUE(prefs->FindPreference( - prefs::kWebKitGlobalFixedFontFamily)->IsDefaultValue()); - EXPECT_TRUE(prefs->FindPreference( - prefs::kWebKitGlobalSansSerifFontFamily)->IsDefaultValue()); - EXPECT_TRUE(prefs->FindPreference( - prefs::kWebKitGlobalSerifFontFamily)->IsDefaultValue()); - EXPECT_TRUE(prefs->FindPreference( - prefs::kWebKitGlobalStandardFontFamily)->IsDefaultValue()); - - EXPECT_EQ("ISO-8859-1", prefs->GetString(prefs::kDefaultCharset)); - EXPECT_EQ(42, prefs->GetInteger(prefs::kWebKitDefaultFontSize)); - EXPECT_EQ(42, - prefs->GetInteger(prefs::kWebKitDefaultFixedFontSize)); - EXPECT_EQ(42, prefs->GetInteger(prefs::kWebKitMinimumFontSize)); - EXPECT_EQ(42, - prefs->GetInteger(prefs::kWebKitMinimumLogicalFontSize)); - EXPECT_EQ("CursiveFontFamily", - prefs->GetString(prefs::kWebKitCursiveFontFamily)); - EXPECT_EQ("FantasyFontFamily", - prefs->GetString(prefs::kWebKitFantasyFontFamily)); - // PictographFontFamily was added after the migration, so it never exists - // in the old format (and consequently isn't in the test Preferences file). - // So it doesn't need to be tested here. - EXPECT_EQ("FixedFontFamily", - prefs->GetString(prefs::kWebKitFixedFontFamily)); - EXPECT_EQ("SansSerifFontFamily", - prefs->GetString(prefs::kWebKitSansSerifFontFamily)); - EXPECT_EQ("SerifFontFamily", - prefs->GetString(prefs::kWebKitSerifFontFamily)); - EXPECT_EQ("StandardFontFamily", - prefs->GetString(prefs::kWebKitStandardFontFamily)); -}; diff --git a/chrome/browser/ui/search/instant_page.cc b/chrome/browser/ui/search/instant_page.cc index fb7422b8a8..1732a3c7ed 100644 --- a/chrome/browser/ui/search/instant_page.cc +++ b/chrome/browser/ui/search/instant_page.cc @@ -94,6 +94,7 @@ bool InstantPage::OnMessageReceived(const IPC::Message& message) { void InstantPage::DidCommitProvisionalLoadForFrame( int64 /* frame_id */, + const string16& frame_unique_name, bool is_main_frame, const GURL& url, content::PageTransition /* transition_type */, @@ -114,6 +115,7 @@ void InstantPage::DidNavigateMainFrame( void InstantPage::DidFailProvisionalLoad( int64 /* frame_id */, + const string16& frame_unique_name, bool is_main_frame, const GURL& /* validated_url */, int /* error_code */, diff --git a/chrome/browser/ui/search/instant_page.h b/chrome/browser/ui/search/instant_page.h index 47cea9f0d7..cb85102405 100644 --- a/chrome/browser/ui/search/instant_page.h +++ b/chrome/browser/ui/search/instant_page.h @@ -132,6 +132,7 @@ class InstantPage : public content::WebContentsObserver, virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; virtual void DidCommitProvisionalLoadForFrame( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& url, content::PageTransition transition_type, @@ -141,6 +142,7 @@ class InstantPage : public content::WebContentsObserver, const content::FrameNavigateParams& params) OVERRIDE; virtual void DidFailProvisionalLoad( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& validated_url, int error_code, diff --git a/chrome/browser/ui/search/search_ipc_router.cc b/chrome/browser/ui/search/search_ipc_router.cc index 666e3d467e..064fb27503 100644 --- a/chrome/browser/ui/search/search_ipc_router.cc +++ b/chrome/browser/ui/search/search_ipc_router.cc @@ -5,7 +5,6 @@ #include "chrome/browser/ui/search/search_ipc_router.h" #include "chrome/browser/search/search.h" -#include "chrome/browser/ui/search/search_tab_helper.h" #include "chrome/common/render_messages.h" #include "content/public/browser/web_contents.h" @@ -106,8 +105,11 @@ void SearchIPCRouter::OnInstantSupportDetermined(int page_id, void SearchIPCRouter::OnVoiceSearchSupportDetermined( int page_id, bool supports_voice_search) const { - if (!web_contents()->IsActiveEntry(page_id) || - !policy_->ShouldProcessSetVoiceSearchSupport()) + if (!web_contents()->IsActiveEntry(page_id)) + return; + + delegate_->OnInstantSupportDetermined(true); + if (!policy_->ShouldProcessSetVoiceSearchSupport()) return; delegate_->OnSetVoiceSearchSupport(supports_voice_search); @@ -118,7 +120,7 @@ void SearchIPCRouter::OnFocusOmnibox(int page_id, if (!web_contents()->IsActiveEntry(page_id)) return; - SearchTabHelper::FromWebContents(web_contents())->InstantSupportChanged(true); + delegate_->OnInstantSupportDetermined(true); if (!policy_->ShouldProcessFocusOmnibox()) return; @@ -130,7 +132,7 @@ void SearchIPCRouter::OnDeleteMostVisitedItem(int page_id, if (!web_contents()->IsActiveEntry(page_id)) return; - SearchTabHelper::FromWebContents(web_contents())->InstantSupportChanged(true); + delegate_->OnInstantSupportDetermined(true); if (!policy_->ShouldProcessDeleteMostVisitedItem()) return; @@ -142,7 +144,7 @@ void SearchIPCRouter::OnUndoMostVisitedDeletion(int page_id, if (!web_contents()->IsActiveEntry(page_id)) return; - SearchTabHelper::FromWebContents(web_contents())->InstantSupportChanged(true); + delegate_->OnInstantSupportDetermined(true); if (!policy_->ShouldProcessUndoMostVisitedDeletion()) return; @@ -153,7 +155,7 @@ void SearchIPCRouter::OnUndoAllMostVisitedDeletions(int page_id) const { if (!web_contents()->IsActiveEntry(page_id)) return; - SearchTabHelper::FromWebContents(web_contents())->InstantSupportChanged(true); + delegate_->OnInstantSupportDetermined(true); if (!policy_->ShouldProcessUndoAllMostVisitedDeletions()) return; @@ -161,8 +163,11 @@ void SearchIPCRouter::OnUndoAllMostVisitedDeletions(int page_id) const { } void SearchIPCRouter::OnLogEvent(int page_id, NTPLoggingEventType event) const { - if (!web_contents()->IsActiveEntry(page_id) || - !policy_->ShouldProcessLogEvent()) + if (!web_contents()->IsActiveEntry(page_id)) + return; + + delegate_->OnInstantSupportDetermined(true); + if (!policy_->ShouldProcessLogEvent()) return; delegate_->OnLogEvent(event); diff --git a/chrome/browser/ui/search/search_tab_helper.cc b/chrome/browser/ui/search/search_tab_helper.cc index 059090fcfc..495cb0667e 100644 --- a/chrome/browser/ui/search/search_tab_helper.cc +++ b/chrome/browser/ui/search/search_tab_helper.cc @@ -298,6 +298,7 @@ void SearchTabHelper::DidNavigateMainFrame( void SearchTabHelper::DidFailProvisionalLoad( int64 /* frame_id */, + const string16& frame_unique_name, bool is_main_frame, const GURL& /* validated_url */, int /* error_code */, diff --git a/chrome/browser/ui/search/search_tab_helper.h b/chrome/browser/ui/search/search_tab_helper.h index 876c032848..64e6921ef8 100644 --- a/chrome/browser/ui/search/search_tab_helper.h +++ b/chrome/browser/ui/search/search_tab_helper.h @@ -173,6 +173,7 @@ class SearchTabHelper : public content::NotificationObserver, const content::FrameNavigateParams& params) OVERRIDE; virtual void DidFailProvisionalLoad( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& validated_url, int error_code, diff --git a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc index d76ca8de49..60d73d755a 100644 --- a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc +++ b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc @@ -109,7 +109,7 @@ class StartupBrowserCreatorTest : public ExtensionBrowserTest { ExtensionService* service = extensions::ExtensionSystem::Get( browser()->profile())->extension_service(); *out_app_extension = service->GetExtensionById( - last_loaded_extension_id_, false); + last_loaded_extension_id(), false); ASSERT_TRUE(*out_app_extension); // Code that opens a new browser assumes we start with exactly one. @@ -960,16 +960,12 @@ class ManagedModeBrowserCreatorTest : public InProcessBrowserTest { virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { InProcessBrowserTest::SetUpCommandLine(command_line); command_line->AppendSwitch(switches::kEnableManagedUsers); + command_line->AppendSwitch(switches::kNewProfileIsSupervised); } }; IN_PROC_BROWSER_TEST_F(ManagedModeBrowserCreatorTest, StartupManagedModeProfile) { - // Make this a managed profile. - ManagedUserService* managed_user_service = - ManagedUserServiceFactory::GetForProfile(browser()->profile()); - managed_user_service->InitForTesting(); - StartupBrowserCreator browser_creator; // Do a simple non-process-startup browser launch. diff --git a/chrome/browser/ui/sync/one_click_signin_helper.cc b/chrome/browser/ui/sync/one_click_signin_helper.cc index 8ab55dc038..7bd2e7d0ef 100644 --- a/chrome/browser/ui/sync/one_click_signin_helper.cc +++ b/chrome/browser/ui/sync/one_click_signin_helper.cc @@ -536,6 +536,7 @@ class CurrentHistoryCleaner : public content::WebContentsObserver { virtual void WebContentsDestroyed(content::WebContents* contents) OVERRIDE; virtual void DidCommitProvisionalLoadForFrame( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& url, content::PageTransition transition_type, @@ -559,6 +560,7 @@ CurrentHistoryCleaner::~CurrentHistoryCleaner() { void CurrentHistoryCleaner::DidCommitProvisionalLoadForFrame( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& url, content::PageTransition transition_type, diff --git a/chrome/browser/ui/tab_contents/core_tab_helper.cc b/chrome/browser/ui/tab_contents/core_tab_helper.cc index 169e23a661..f5431398df 100644 --- a/chrome/browser/ui/tab_contents/core_tab_helper.cc +++ b/chrome/browser/ui/tab_contents/core_tab_helper.cc @@ -4,19 +4,36 @@ #include "chrome/browser/ui/tab_contents/core_tab_helper.h" +#include <string> +#include <vector> + #include "base/command_line.h" #include "base/metrics/histogram.h" +#include "base/strings/stringprintf.h" +#include "chrome/browser/profiles/profile.h" #include "chrome/browser/renderer_host/web_cache_manager.h" +#include "chrome/browser/search_engines/search_terms_data.h" +#include "chrome/browser/search_engines/template_url.h" +#include "chrome/browser/search_engines/template_url_service.h" +#include "chrome/browser/search_engines/template_url_service_factory.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_command_controller.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/render_messages.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" -#include "net/base/load_states.h" #include "grit/generated_resources.h" +#include "net/base/load_states.h" +#include "net/http/http_request_headers.h" +#include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/gfx/codec/jpeg_codec.h" + +#if defined(OS_WIN) +#include "base/win/win_util.h" +#endif using content::WebContents; @@ -165,7 +182,6 @@ void CoreTabHelper::WebContentsDestroyed(WebContents* web_contents) { UMA_HISTOGRAM_TIMES("Tab.Close.UnloadTime", now - unload_start_time); } } - } void CoreTabHelper::BeforeUnloadFired(const base::TimeTicks& proceed_time) { @@ -175,3 +191,83 @@ void CoreTabHelper::BeforeUnloadFired(const base::TimeTicks& proceed_time) { void CoreTabHelper::BeforeUnloadDialogCancelled() { OnCloseCanceled(); } + +bool CoreTabHelper::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(CoreTabHelper, message) + IPC_MESSAGE_HANDLER(ChromeViewHostMsg_FocusedNodeTouched, + OnFocusedNodeTouched) + IPC_MESSAGE_HANDLER(ChromeViewHostMsg_RequestThumbnailForContextNode_ACK, + OnRequestThumbnailForContextNodeACK) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void CoreTabHelper::OnFocusedNodeTouched(bool editable) { +#if defined(OS_WIN) && defined(USE_AURA) + if (editable) { + base::win::DisplayVirtualKeyboard(); + } else { + base::win::DismissVirtualKeyboard(); + } +#endif // OS_WIN && USE_AURA +} + +// Handles the image thumbnail for the context node, composes a image search +// request based on the received thumbnail and opens the request in a new tab. +void CoreTabHelper::OnRequestThumbnailForContextNodeACK( + const SkBitmap& bitmap, + const gfx::Size& original_size) { + if (bitmap.isNull()) + return; + Profile* profile = + Profile::FromBrowserContext(web_contents()->GetBrowserContext()); + + TemplateURLService* template_url_service = + TemplateURLServiceFactory::GetForProfile(profile); + if (!template_url_service) + return; + const TemplateURL* const default_provider = + template_url_service->GetDefaultSearchProvider(); + if (!default_provider) + return; + + const int kDefaultQualityForImageSearch = 90; + std::vector<unsigned char> data; + if (!gfx::JPEGCodec::Encode( + reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)), + gfx::JPEGCodec::FORMAT_SkBitmap, bitmap.width(), bitmap.height(), + static_cast<int>(bitmap.rowBytes()), kDefaultQualityForImageSearch, + &data)) + return; + + TemplateURLRef::SearchTermsArgs search_args = + TemplateURLRef::SearchTermsArgs(base::string16()); + search_args.image_thumbnail_content = std::string(data.begin(), data.end()); + // TODO(jnd): Add a method in WebContentsViewDelegate to get the image URL + // from the ContextMenuParams which creates current context menu. + search_args.image_url = GURL(); + search_args.image_original_size = original_size; + TemplateURLRef::PostContent post_content; + GURL result(default_provider->image_url_ref().ReplaceSearchTerms( + search_args, &post_content)); + if (!result.is_valid()) + return; + + content::OpenURLParams open_url_params( + result, content::Referrer(), NEW_FOREGROUND_TAB, + content::PAGE_TRANSITION_LINK, false); + const std::string& content_type = post_content.first; + std::string* post_data = &post_content.second; + if (!post_data->empty()) { + DCHECK(!content_type.empty()); + open_url_params.uses_post = true; + open_url_params.browser_initiated_post_data = + base::RefCountedString::TakeString(post_data); + open_url_params.extra_headers += base::StringPrintf( + "%s: %s\r\n", net::HttpRequestHeaders::kContentType, + content_type.c_str()); + } + web_contents()->OpenURL(open_url_params); +} diff --git a/chrome/browser/ui/tab_contents/core_tab_helper.h b/chrome/browser/ui/tab_contents/core_tab_helper.h index 7f0ffe368a..bb57f4ee53 100644 --- a/chrome/browser/ui/tab_contents/core_tab_helper.h +++ b/chrome/browser/ui/tab_contents/core_tab_helper.h @@ -63,6 +63,11 @@ class CoreTabHelper : public content::WebContentsObserver, content::WebContents* web_contents) OVERRIDE; virtual void BeforeUnloadFired(const base::TimeTicks& proceed_time) OVERRIDE; virtual void BeforeUnloadDialogCancelled() OVERRIDE; + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + + void OnFocusedNodeTouched(bool editable); + void OnRequestThumbnailForContextNodeACK(const SkBitmap& bitmap, + const gfx::Size& original_size); // Delegate for notifying our owner about stuff. Not owned by us. CoreTabHelperDelegate* delegate_; diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc index f590dad484..91042c7a0a 100644 --- a/chrome/browser/ui/tabs/tab_strip_model.cc +++ b/chrome/browser/ui/tabs/tab_strip_model.cc @@ -277,6 +277,9 @@ void TabStripModel::AppendWebContents(WebContents* contents, void TabStripModel::InsertWebContentsAt(int index, WebContents* contents, int add_types) { + // TODO(sky): nuke, used in isolating 297118. + CHECK(!contents->IsBeingDestroyed()); + delegate_->WillAddWebContents(contents); bool active = add_types & ADD_ACTIVE; diff --git a/chrome/browser/ui/toolbar/wrench_menu_model.cc b/chrome/browser/ui/toolbar/wrench_menu_model.cc index 19571ab8f7..c24ba59564 100644 --- a/chrome/browser/ui/toolbar/wrench_menu_model.cc +++ b/chrome/browser/ui/toolbar/wrench_menu_model.cc @@ -217,6 +217,7 @@ void ToolsMenuModel::Build(Browser* browser) { AddItemWithStringId(IDC_VIEW_SOURCE, IDS_VIEW_SOURCE); AddItemWithStringId(IDC_DEV_TOOLS, IDS_DEV_TOOLS); AddItemWithStringId(IDC_DEV_TOOLS_CONSOLE, IDS_DEV_TOOLS_CONSOLE); + AddItemWithStringId(IDC_DEV_TOOLS_DEVICES, IDS_DEV_TOOLS_DEVICES); #if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC) AddSeparator(ui::NORMAL_SEPARATOR); diff --git a/chrome/browser/ui/translate/language_combobox_model.cc b/chrome/browser/ui/translate/language_combobox_model.cc new file mode 100644 index 0000000000..031998addd --- /dev/null +++ b/chrome/browser/ui/translate/language_combobox_model.cc @@ -0,0 +1,33 @@ +// Copyright 2013 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/ui/translate/language_combobox_model.h" + +#include "chrome/browser/ui/translate/translate_bubble_model.h" + +LanguageComboboxModel::LanguageComboboxModel( + int default_index, + TranslateBubbleModel* model) + : default_index_(default_index), + model_(model) { +} + +LanguageComboboxModel::~LanguageComboboxModel() { +} + +int LanguageComboboxModel::GetItemCount() const { + return model_->GetNumberOfLanguages(); +} + +string16 LanguageComboboxModel::GetItemAt(int index) { + return model_->GetLanguageNameAt(index); +} + +bool LanguageComboboxModel::IsItemSeparatorAt(int index) { + return false; +} + +int LanguageComboboxModel::GetDefaultIndex() const { + return default_index_; +} diff --git a/chrome/browser/ui/translate/language_combobox_model.h b/chrome/browser/ui/translate/language_combobox_model.h new file mode 100644 index 0000000000..4af7a69dbf --- /dev/null +++ b/chrome/browser/ui/translate/language_combobox_model.h @@ -0,0 +1,37 @@ +// Copyright 2013 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_UI_TRANSLATE_LANGUAGE_COMBOBOX_MODEL_H_ +#define CHROME_BROWSER_UI_TRANSLATE_LANGUAGE_COMBOBOX_MODEL_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string16.h" +#include "ui/base/models/combobox_model.h" + +class TranslateBubbleModel; + +// The model for the combobox to select a language. This is used for Translate +// user interface to select language. +class LanguageComboboxModel : public ui::ComboboxModel { + public: + LanguageComboboxModel(int default_index, + TranslateBubbleModel* model); + virtual ~LanguageComboboxModel(); + + // Overridden from ui::ComboboxModel: + virtual int GetItemCount() const OVERRIDE; + virtual string16 GetItemAt(int index) OVERRIDE; + virtual bool IsItemSeparatorAt(int index) OVERRIDE; + virtual int GetDefaultIndex() const OVERRIDE; + + private: + const int default_index_; + TranslateBubbleModel* model_; + + DISALLOW_COPY_AND_ASSIGN(LanguageComboboxModel); +}; + +#endif // CHROME_BROWSER_UI_TRANSLATE_LANGUAGE_COMBOBOX_MODEL_H_ diff --git a/chrome/browser/ui/translate/translate_bubble_model.h b/chrome/browser/ui/translate/translate_bubble_model.h new file mode 100644 index 0000000000..af12a910b3 --- /dev/null +++ b/chrome/browser/ui/translate/translate_bubble_model.h @@ -0,0 +1,89 @@ +// Copyright 2013 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_UI_TRANSLATE_TRANSLATE_BUBBLE_MODEL_H_ +#define CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_BUBBLE_MODEL_H_ + +#include <string> + +#include "base/strings/string16.h" + +// The model for the Translate bubble UX. This manages the user's manipulation +// of the bubble and offers the data to show on the bubble. +class TranslateBubbleModel { + public: + enum ViewState { + // The view state before translating. + VIEW_STATE_BEFORE_TRANSLATE, + + // The view state during translating. + VIEW_STATE_TRANSLATING, + + // The view state after translating. + VIEW_STATE_AFTER_TRANSLATE, + + // The view state when an error of Translate happens. + VIEW_STATE_ERROR, + + // The view state when the detailed settings is shown. This view appears + // when the user click a link 'Advanced' on other views. + VIEW_STATE_ADVANCED, + }; + + virtual ~TranslateBubbleModel() {} + + // Returns the current view state. + virtual ViewState GetViewState() const = 0; + + // Transitions the view state. + virtual void SetViewState(ViewState view_state) = 0; + + // Goes back from the 'Advanced' view state. + virtual void GoBackFromAdvanced() = 0; + + // Returns the number of languages supported. + virtual int GetNumberOfLanguages() const = 0; + + // Returns the displayable name for the language at |index|. + virtual string16 GetLanguageNameAt(int index) const = 0; + + // Returns the original language index. + virtual int GetOriginalLanguageIndex() const = 0; + + // Sets the original language index. + virtual void SetOriginalLanguageIndex(int index) = 0; + + // Returns the target language index. + virtual int GetTargetLanguageIndex() const = 0; + + // Sets the target language index. + virtual void SetTargetLanguageIndex(int index) = 0; + + // Sets the value if the user doesn't want to have the page translated in the + // current page's language. + virtual void SetNeverTranslateLanguage(bool value) = 0; + + // Sets the value if the user doesn't want to have the page translated the + // current page's domain. + virtual void SetNeverTranslateSite(bool value) = 0; + + // Returns true if the webpage in the current original language should be + // translated into the current target language automatically. + virtual bool ShouldAlwaysTranslate() const = 0; + + // Sets the value if the webpage in the current original language should be + // translated into the current target language automatically. + virtual void SetAlwaysTranslate(bool value) = 0; + + // Starts translating the current page. + virtual void Translate() = 0; + + // Reverts translation. + virtual void RevertTranslation() = 0; + + // Processes when the user declines translation. + virtual void TranslationDeclined() = 0; +}; + +#endif // CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_BUBBLE_MODEL_H_ diff --git a/chrome/browser/ui/translate/translate_bubble_model_impl.cc b/chrome/browser/ui/translate/translate_bubble_model_impl.cc new file mode 100644 index 0000000000..7bf27172b3 --- /dev/null +++ b/chrome/browser/ui/translate/translate_bubble_model_impl.cc @@ -0,0 +1,82 @@ +// Copyright 2013 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/ui/translate/translate_bubble_model_impl.h" + +#include "chrome/browser/translate/translate_ui_delegate.h" + +TranslateBubbleModelImpl::TranslateBubbleModelImpl( + TranslateBubbleModel::ViewState view_type, + scoped_ptr<TranslateUIDelegate> ui_delegate) + : ui_delegate_(ui_delegate.Pass()), + view_state_transition_(view_type) { +} + +TranslateBubbleModelImpl::~TranslateBubbleModelImpl() { +} + +TranslateBubbleModel::ViewState TranslateBubbleModelImpl::GetViewState() const { + return view_state_transition_.view_state(); +} + +void TranslateBubbleModelImpl::SetViewState( + TranslateBubbleModel::ViewState view_state) { + view_state_transition_.SetViewState(view_state); +} + +void TranslateBubbleModelImpl::GoBackFromAdvanced() { + view_state_transition_.GoBackFromAdvanced(); +} + +int TranslateBubbleModelImpl::GetNumberOfLanguages() const { + return ui_delegate_->GetNumberOfLanguages(); +} + +string16 TranslateBubbleModelImpl::GetLanguageNameAt(int index) const { + return ui_delegate_->GetLanguageNameAt(index); +} + +int TranslateBubbleModelImpl::GetOriginalLanguageIndex() const { + return ui_delegate_->GetOriginalLanguageIndex(); +} + +void TranslateBubbleModelImpl::SetOriginalLanguageIndex(int index) { + ui_delegate_->SetOriginalLanguageIndex(index); +} + +int TranslateBubbleModelImpl::GetTargetLanguageIndex() const { + return ui_delegate_->GetTargetLanguageIndex(); +} + +void TranslateBubbleModelImpl::SetTargetLanguageIndex(int index) { + ui_delegate_->SetTargetLanguageIndex(index); +} + +void TranslateBubbleModelImpl::SetNeverTranslateLanguage(bool value) { + ui_delegate_->SetLanguageBlocked(value); +} + +void TranslateBubbleModelImpl::SetNeverTranslateSite(bool value) { + ui_delegate_->SetSiteBlacklist(value); +} + +bool TranslateBubbleModelImpl::ShouldAlwaysTranslate() const { + return ui_delegate_->ShouldAlwaysTranslate(); +} + +void TranslateBubbleModelImpl::SetAlwaysTranslate(bool value) { + ui_delegate_->SetAlwaysTranslate(value); +} + +void TranslateBubbleModelImpl::Translate() { + ui_delegate_->Translate(); +} + +void TranslateBubbleModelImpl::RevertTranslation() { + ui_delegate_->RevertTranslation(); +} + +void TranslateBubbleModelImpl::TranslationDeclined() { + ui_delegate_->TranslationDeclined(); +} diff --git a/chrome/browser/ui/translate/translate_bubble_model_impl.h b/chrome/browser/ui/translate/translate_bubble_model_impl.h new file mode 100644 index 0000000000..94c78ad73b --- /dev/null +++ b/chrome/browser/ui/translate/translate_bubble_model_impl.h @@ -0,0 +1,48 @@ +// Copyright 2013 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_UI_TRANSLATE_TRANSLATE_BUBBLE_MODEL_IMPL_H_ +#define CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_BUBBLE_MODEL_IMPL_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/ui/translate/translate_bubble_model.h" +#include "chrome/browser/ui/translate/translate_bubble_view_state_transition.h" + +class TranslateUIDelegate; + +// The standard implementation of TranslateBubbleModel. +class TranslateBubbleModelImpl : public TranslateBubbleModel { + public: + TranslateBubbleModelImpl(TranslateBubbleModel::ViewState view_type, + scoped_ptr<TranslateUIDelegate> ui_delegate); + virtual ~TranslateBubbleModelImpl(); + + // TranslateBubbleModel methods. + virtual TranslateBubbleModel::ViewState GetViewState() const OVERRIDE; + virtual void SetViewState(TranslateBubbleModel::ViewState view_state) + OVERRIDE; + virtual void GoBackFromAdvanced() OVERRIDE; + virtual int GetNumberOfLanguages() const OVERRIDE; + virtual string16 GetLanguageNameAt(int index) const OVERRIDE; + virtual int GetOriginalLanguageIndex() const OVERRIDE; + virtual void SetOriginalLanguageIndex(int index) OVERRIDE; + virtual int GetTargetLanguageIndex() const OVERRIDE; + virtual void SetTargetLanguageIndex(int index) OVERRIDE; + virtual void SetNeverTranslateLanguage(bool value) OVERRIDE; + virtual void SetNeverTranslateSite(bool value) OVERRIDE; + virtual bool ShouldAlwaysTranslate() const OVERRIDE; + virtual void SetAlwaysTranslate(bool value) OVERRIDE; + virtual void Translate() OVERRIDE; + virtual void RevertTranslation() OVERRIDE; + virtual void TranslationDeclined() OVERRIDE; + + private: + scoped_ptr<TranslateUIDelegate> ui_delegate_; + TranslateBubbleViewStateTransition view_state_transition_; + + DISALLOW_COPY_AND_ASSIGN(TranslateBubbleModelImpl); +}; + +#endif // CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_BUBBLE_MODEL_IMPL_H_ diff --git a/chrome/browser/ui/translate/translate_bubble_view_state_transition.cc b/chrome/browser/ui/translate/translate_bubble_view_state_transition.cc new file mode 100644 index 0000000000..9a7134b76c --- /dev/null +++ b/chrome/browser/ui/translate/translate_bubble_view_state_transition.cc @@ -0,0 +1,27 @@ +// Copyright 2013 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/ui/translate/translate_bubble_view_state_transition.h" + +#include "base/logging.h" + +TranslateBubbleViewStateTransition::TranslateBubbleViewStateTransition( + TranslateBubbleModel::ViewState view_state) + : view_state_(view_state), + view_state_before_advanced_view_(view_state) { + // The initial view type must not be 'Advanced'. + DCHECK_NE(TranslateBubbleModel::VIEW_STATE_ADVANCED, view_state_); +} + +void TranslateBubbleViewStateTransition::SetViewState( + TranslateBubbleModel::ViewState view_state) { + view_state_ = view_state; + if (view_state != TranslateBubbleModel::VIEW_STATE_ADVANCED) + view_state_before_advanced_view_ = view_state; +} + +void TranslateBubbleViewStateTransition::GoBackFromAdvanced() { + DCHECK(view_state_ == TranslateBubbleModel::VIEW_STATE_ADVANCED); + SetViewState(view_state_before_advanced_view_); +} diff --git a/chrome/browser/ui/translate/translate_bubble_view_state_transition.h b/chrome/browser/ui/translate/translate_bubble_view_state_transition.h new file mode 100644 index 0000000000..ad6c76838c --- /dev/null +++ b/chrome/browser/ui/translate/translate_bubble_view_state_transition.h @@ -0,0 +1,39 @@ +// Copyright 2013 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_UI_TRANSLATE_TRANSLATE_BUBBLE_VIEW_STATE_TRANSITION_H_ +#define CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_BUBBLE_VIEW_STATE_TRANSITION_H_ + +#include "base/basictypes.h" +#include "chrome/browser/ui/translate/translate_bubble_model.h" + +// The class which manages the transition of the view state of the Translate +// bubble. +class TranslateBubbleViewStateTransition { + public: + explicit TranslateBubbleViewStateTransition( + TranslateBubbleModel::ViewState view_state); + + TranslateBubbleModel::ViewState view_state() const { return view_state_; } + + // Transitions the view state. + void SetViewState(TranslateBubbleModel::ViewState view_state); + + // Goes back from the 'Advanced' view state. + void GoBackFromAdvanced(); + + private: + // The current view type. + TranslateBubbleModel::ViewState view_state_; + + // The view type. When the current view type is not 'Advanced' view, this is + // equivalent to |view_state_|. Otherwise, this is the previous view type + // before the user opens the 'Advanced' view. This is used to navigate when + // pressing 'Cancel' button on the 'Advanced' view. + TranslateBubbleModel::ViewState view_state_before_advanced_view_; + + DISALLOW_COPY_AND_ASSIGN(TranslateBubbleViewStateTransition); +}; + +#endif // CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_BUBBLE_VIEW_STATE_TRANSITION_H_ diff --git a/chrome/browser/ui/view_ids.h b/chrome/browser/ui/view_ids.h index 3d0fcb8869..81e035ec96 100644 --- a/chrome/browser/ui/view_ids.h +++ b/chrome/browser/ui/view_ids.h @@ -24,6 +24,7 @@ enum ViewID { VIEW_ID_WINDOW_TITLE, VIEW_ID_AVATAR_LABEL, VIEW_ID_AVATAR_BUTTON, + VIEW_ID_NEW_AVATAR_BUTTON, // Tabs within a window/tab strip, counting from the left. VIEW_ID_TAB_0, diff --git a/chrome/browser/ui/views/app_list/win/app_list_controller_delegate_win.cc b/chrome/browser/ui/views/app_list/win/app_list_controller_delegate_win.cc index 74ebecaf29..ca4458ea68 100644 --- a/chrome/browser/ui/views/app_list/win/app_list_controller_delegate_win.cc +++ b/chrome/browser/ui/views/app_list/win/app_list_controller_delegate_win.cc @@ -4,13 +4,11 @@ #include "chrome/browser/ui/views/app_list/win/app_list_controller_delegate_win.h" -#include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/ui/app_list/app_list_icon_win.h" #include "chrome/browser/ui/browser_commands.h" +#include "chrome/browser/ui/browser_dialogs.h" #include "chrome/browser/ui/extensions/application_launch.h" #include "chrome/browser/ui/views/app_list/win/app_list_service_win.h" -#include "chrome/browser/ui/views/browser_dialogs.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/manifest_handlers/app_launch_info.h" #include "net/base/url_util.h" @@ -60,31 +58,10 @@ void AppListControllerDelegateWin::OnCloseExtensionPrompt() { service_->set_can_close(true); } -bool AppListControllerDelegateWin::CanDoCreateShortcutsFlow( - bool is_platform_app) { +bool AppListControllerDelegateWin::CanDoCreateShortcutsFlow() { return true; } -void AppListControllerDelegateWin::DoCreateShortcutsFlow( - Profile* profile, - const std::string& extension_id) { - ExtensionService* service = - extensions::ExtensionSystem::Get(profile)->extension_service(); - DCHECK(service); - const extensions::Extension* extension = service->GetInstalledExtension( - extension_id); - DCHECK(extension); - - gfx::NativeWindow parent_hwnd = GetAppListWindow(); - if (!parent_hwnd) - return; - OnShowExtensionPrompt(); - chrome::ShowCreateChromeAppShortcutsDialog( - parent_hwnd, profile, extension, - base::Bind(&AppListControllerDelegateWin::OnCloseExtensionPrompt, - base::Unretained(this))); -} - void AppListControllerDelegateWin::CreateNewWindow(Profile* profile, bool incognito) { Profile* window_profile = incognito ? @@ -108,6 +85,7 @@ void AppListControllerDelegateWin::LaunchApp( AppListServiceImpl::RecordAppListAppLaunch(); AppLaunchParams params(profile, extension, NEW_FOREGROUND_TAB); + params.desktop_type = chrome::HOST_DESKTOP_TYPE_NATIVE; if (source != LAUNCH_FROM_UNKNOWN && extension->id() == extension_misc::kWebStoreAppId) { diff --git a/chrome/browser/ui/views/app_list/win/app_list_controller_delegate_win.h b/chrome/browser/ui/views/app_list/win/app_list_controller_delegate_win.h index 869ca282d4..9e3ffcca56 100644 --- a/chrome/browser/ui/views/app_list/win/app_list_controller_delegate_win.h +++ b/chrome/browser/ui/views/app_list/win/app_list_controller_delegate_win.h @@ -31,9 +31,7 @@ class AppListControllerDelegateWin : public AppListControllerDelegate { virtual Pinnable GetPinnable() OVERRIDE; virtual void OnShowExtensionPrompt() OVERRIDE; virtual void OnCloseExtensionPrompt() OVERRIDE; - virtual bool CanDoCreateShortcutsFlow(bool is_platform_app) OVERRIDE; - virtual void DoCreateShortcutsFlow(Profile* profile, - const std::string& extension_id) OVERRIDE; + virtual bool CanDoCreateShortcutsFlow() OVERRIDE; virtual void CreateNewWindow(Profile* profile, bool incognito) OVERRIDE; virtual void ActivateApp(Profile* profile, const extensions::Extension* extension, diff --git a/chrome/browser/ui/views/app_list/win/app_list_service_win.cc b/chrome/browser/ui/views/app_list/win/app_list_service_win.cc index d061902b2e..d02c6fa001 100644 --- a/chrome/browser/ui/views/app_list/win/app_list_service_win.cc +++ b/chrome/browser/ui/views/app_list/win/app_list_service_win.cc @@ -312,19 +312,22 @@ void SetWindowAttributes(HWND hwnd) { class AppListFactoryWin : public AppListFactory { public: - explicit AppListFactoryWin( - scoped_ptr<AppListControllerDelegate> delegate) - : delegate_(delegate.Pass()) {} - virtual ~AppListFactoryWin() {} + explicit AppListFactoryWin(AppListServiceWin* service) + : service_(service) { + } + + virtual ~AppListFactoryWin() { + } virtual AppList* CreateAppList( Profile* profile, const base::Closure& on_should_dismiss) OVERRIDE { // The controller will be owned by the view delegate, and the delegate is // owned by the app list view. The app list view manages it's own lifetime. - // TODO(koz): Make AppListViewDelegate take a scoped_ptr. + scoped_ptr<AppListControllerDelegate> controller_delegate( + new AppListControllerDelegateWin(service_)); AppListViewDelegate* view_delegate = new AppListViewDelegate( - delegate_.get(), profile); + controller_delegate.Pass(), profile); app_list::AppListView* view = new app_list::AppListView(view_delegate); gfx::Point cursor = gfx::Screen::GetNativeScreen()->GetCursorScreenPoint(); view->InitAsBubbleAtFixedLocation(NULL, @@ -339,8 +342,8 @@ class AppListFactoryWin : public AppListFactory { private: // PaginationModel that is shared across all views. app_list::PaginationModel pagination_model_; + AppListServiceWin* service_; - scoped_ptr<AppListControllerDelegate> delegate_; DISALLOW_COPY_AND_ASSIGN(AppListFactoryWin); }; @@ -355,9 +358,7 @@ AppListServiceWin* AppListServiceWin::GetInstance() { AppListServiceWin::AppListServiceWin() : enable_app_list_on_next_init_(false), shower_(new AppListShower( - scoped_ptr<AppListFactory>( - new AppListFactoryWin(scoped_ptr<AppListControllerDelegate>( - new AppListControllerDelegateWin(this)))), + scoped_ptr<AppListFactory>(new AppListFactoryWin(this)), scoped_ptr<KeepAliveService>(new KeepAliveServiceImpl))), weak_factory_(this) {} diff --git a/chrome/browser/ui/views/apps/native_app_window_views.cc b/chrome/browser/ui/views/apps/native_app_window_views.cc index c39d0424de..ff9bbd2428 100644 --- a/chrome/browser/ui/views/apps/native_app_window_views.cc +++ b/chrome/browser/ui/views/apps/native_app_window_views.cc @@ -131,8 +131,6 @@ NativeAppWindowViews::NativeAppWindowViews( is_fullscreen_(false), frameless_(create_params.frame == ShellWindow::FRAME_NONE), transparent_background_(create_params.transparent_background), - minimum_size_(create_params.minimum_size), - maximum_size_(create_params.maximum_size), resizable_(create_params.resizable), weak_ptr_factory_(this) { Observe(web_contents()); @@ -555,12 +553,11 @@ views::View* NativeAppWindowViews::GetInitiallyFocusedView() { } bool NativeAppWindowViews::CanResize() const { - return resizable_ && - (maximum_size_.IsEmpty() || minimum_size_ != maximum_size_); + return resizable_ && !shell_window_->size_constraints().HasFixedSize(); } bool NativeAppWindowViews::CanMaximize() const { - return resizable_ && maximum_size_.IsEmpty() && + return resizable_ && !shell_window_->size_constraints().HasMaximumSize() && !shell_window_->window_type_is_panel(); } @@ -629,9 +626,7 @@ views::NonClientFrameView* NativeAppWindowViews::CreateNonClientFrameView( return new ash::PanelFrameView(widget, frame_type); } if (!frameless_) { - ash::CustomFrameViewAsh* frame = new ash::CustomFrameViewAsh(); - frame->Init(widget); - return frame; + return new ash::CustomFrameViewAsh(widget); } } #endif @@ -724,11 +719,11 @@ gfx::Size NativeAppWindowViews::GetPreferredSize() { } gfx::Size NativeAppWindowViews::GetMinimumSize() { - return minimum_size_; + return shell_window_->size_constraints().GetMinimumSize(); } gfx::Size NativeAppWindowViews::GetMaximumSize() { - return maximum_size_; + return shell_window_->size_constraints().GetMaximumSize(); } void NativeAppWindowViews::OnFocus() { @@ -838,3 +833,4 @@ bool NativeAppWindowViews::IsVisible() const { void NativeAppWindowViews::HideWithApp() {} void NativeAppWindowViews::ShowWithApp() {} +void NativeAppWindowViews::UpdateWindowMinMaxSize() {} diff --git a/chrome/browser/ui/views/apps/native_app_window_views.h b/chrome/browser/ui/views/apps/native_app_window_views.h index caecca39cc..506138ce27 100644 --- a/chrome/browser/ui/views/apps/native_app_window_views.h +++ b/chrome/browser/ui/views/apps/native_app_window_views.h @@ -154,6 +154,7 @@ class NativeAppWindowViews : public apps::NativeAppWindow, virtual bool IsVisible() const OVERRIDE; virtual void HideWithApp() OVERRIDE; virtual void ShowWithApp() OVERRIDE; + virtual void UpdateWindowMinMaxSize() OVERRIDE; // web_modal::WebContentsModalDialogHost implementation. virtual gfx::NativeView GetHostView() const OVERRIDE; @@ -185,8 +186,6 @@ class NativeAppWindowViews : public apps::NativeAppWindow, const bool frameless_; const bool transparent_background_; - gfx::Size minimum_size_; - gfx::Size maximum_size_; gfx::Size preferred_size_; bool resizable_; diff --git a/chrome/browser/ui/views/autofill/autofill_dialog_views.cc b/chrome/browser/ui/views/autofill/autofill_dialog_views.cc index 3fba3171eb..b50c295d93 100644 --- a/chrome/browser/ui/views/autofill/autofill_dialog_views.cc +++ b/chrome/browser/ui/views/autofill/autofill_dialog_views.cc @@ -484,12 +484,25 @@ AutofillDialogViews::ErrorBubble::ErrorBubble(views::View* anchor, views::View* anchor_container, const base::string16& message) : anchor_(anchor), - anchor_container_(anchor_container) { + anchor_container_(anchor_container), + show_above_anchor_( + anchor->GetClassName() == views::Combobox::kViewClassName) { DCHECK(anchor_container_->Contains(anchor)); - SetAnchorView(anchor_); - set_arrow(ShouldArrowGoOnTheRight() ? views::BubbleBorder::TOP_RIGHT : - views::BubbleBorder::TOP_LEFT); + + // TODO(dbeam): currently we assume that combobox menus always show downward + // (which isn't true). If the invalid combobox is low enough on the screen, + // its menu will actually show upward and obscure the bubble. Figure out when + // this might happen and adjust |show_above_anchor_| accordingly. This is not + // that big of deal because it rarely happens in practice. + if (show_above_anchor_) { + set_arrow(ShouldArrowGoOnTheRight() ? views::BubbleBorder::BOTTOM_RIGHT : + views::BubbleBorder::BOTTOM_LEFT); + } else { + set_arrow(ShouldArrowGoOnTheRight() ? views::BubbleBorder::TOP_RIGHT : + views::BubbleBorder::TOP_LEFT); + } + set_margins(gfx::Insets(kErrorBubbleVerticalMargin, kErrorBubbleHorizontalMargin, kErrorBubbleVerticalMargin, @@ -523,7 +536,7 @@ void AutofillDialogViews::ErrorBubble::UpdatePosition() { if (!anchor_->GetVisibleBounds().IsEmpty()) { SizeToContents(); widget_->SetVisibilityChangedAnimationsEnabled(true); - widget_->Show(); + widget_->ShowInactive(); } else { widget_->SetVisibilityChangedAnimationsEnabled(false); widget_->Hide(); @@ -540,6 +553,10 @@ gfx::Size AutofillDialogViews::ErrorBubble::GetPreferredSize() { gfx::Rect AutofillDialogViews::ErrorBubble::GetBubbleBounds() { gfx::Rect bounds = views::BubbleDelegateView::GetBubbleBounds(); gfx::Rect anchor_bounds = anchor_->GetBoundsInScreen(); + + if (show_above_anchor_) + bounds.set_y(anchor_bounds.y() - GetBubbleFrameView()->height()); + anchor_bounds.Inset(-GetBubbleFrameView()->bubble_border()->GetInsets()); bounds.set_x(ShouldArrowGoOnTheRight() ? anchor_bounds.right() - bounds.width() - kBubbleBorderVisibleWidth : @@ -648,6 +665,46 @@ void AutofillDialogViews::AccountChooser::OnMenuButtonClicked( } } +void AutofillDialogViews::ShowDialogInMode(DialogMode dialog_mode) { + std::vector<views::View*> visible; + + if (dialog_mode == LOADING) { + visible.push_back(loading_shield_); + } else if (dialog_mode == SIGN_IN) { + visible.push_back(sign_in_web_view_); + } else { + DCHECK_EQ(DETAIL_INPUT, dialog_mode); + visible.push_back(notification_area_); + visible.push_back(scrollable_area_); + } + + for (size_t i = 0; i < visible.size(); ++i) { + DCHECK_GE(GetIndexOf(visible[i]), 0); + } + + for (int i = 0; i < child_count(); ++i) { + views::View* child = child_at(i); + child->SetVisible( + std::find(visible.begin(), visible.end(), child) != visible.end()); + } +} + +views::View* AutofillDialogViews::GetLoadingShieldForTesting() { + return loading_shield_; +} + +views::WebView* AutofillDialogViews::GetSignInWebViewForTesting() { + return sign_in_web_view_; +} + +views::View* AutofillDialogViews::GetNotificationAreaForTesting() { + return notification_area_; +} + +views::View* AutofillDialogViews::GetScrollableAreaForTesting() { + return scrollable_area_; +} + void AutofillDialogViews::AccountChooser::LinkClicked(views::Link* source, int event_flags) { delegate_->SignInLinkClicked(); @@ -877,9 +934,10 @@ void AutofillDialogViews::OnWidgetClosing(views::Widget* widget) { void AutofillDialogViews::OnWidgetBoundsChanged(views::Widget* widget, const gfx::Rect& new_bounds) { // Notify the web contents of its new auto-resize limits. - if (sign_in_delegate_ && sign_in_webview_->visible()) + if (sign_in_delegate_ && sign_in_web_view_->visible()) { sign_in_delegate_->UpdateLimitsAndEnableAutoResize( GetMinimumSignInViewSize(), GetMaximumSignInViewSize()); + } HideErrorBubble(); } @@ -1265,7 +1323,7 @@ AutofillDialogViews::AutofillDialogViews(AutofillDialogViewDelegate* delegate) window_(NULL), notification_area_(NULL), account_chooser_(NULL), - sign_in_webview_(NULL), + sign_in_web_view_(NULL), scrollable_area_(NULL), details_container_(NULL), loading_shield_(NULL), @@ -1358,9 +1416,11 @@ void AutofillDialogViews::UpdateAccountChooser() { if (show_loading) { loading_shield_height_ = std::max(kInitialLoadingShieldHeight, GetContentsBounds().height()); + ShowDialogInMode(LOADING); + } else { + ShowDialogInMode(DETAIL_INPUT); } - loading_shield_->SetVisible(show_loading); InvalidateLayout(); ContentsPreferredSizeChanged(); } @@ -1398,7 +1458,6 @@ void AutofillDialogViews::UpdateButtonStrip() { } void AutofillDialogViews::UpdateOverlay() { - DialogOverlayState overlay_state = delegate_->GetDialogOverlay(); overlay_view_->UpdateState(); } @@ -1438,7 +1497,7 @@ void AutofillDialogViews::FillSection(DialogSection section, // If the Autofill data comes from a credit card, make sure to overwrite the // CC comboboxes (even if they already have something in them). If the // Autofill data comes from an AutofillProfile, leave the comboboxes alone. - if ((section == SECTION_CC || section == SECTION_CC_BILLING) && + if (section == GetCreditCardSection() && AutofillType(originating_input.type).group() == CREDIT_CARD) { for (ComboboxMap::const_iterator it = group->comboboxes.begin(); it != group->comboboxes.end(); ++it) { @@ -1465,9 +1524,7 @@ void AutofillDialogViews::GetUserInput(DialogSection section, } base::string16 AutofillDialogViews::GetCvc() { - DialogSection billing_section = delegate_->SectionIsActive(SECTION_CC) ? - SECTION_CC : SECTION_CC_BILLING; - return GroupForSection(billing_section)->suggested_info-> + return GroupForSection(GetCreditCardSection())->suggested_info-> decorated_textfield()->text(); } @@ -1498,20 +1555,21 @@ const content::NavigationController* AutofillDialogViews::ShowSignIn() { sign_in_delegate_.reset( new AutofillDialogSignInDelegate( - this, sign_in_webview_->GetWebContents(), + this, sign_in_web_view_->GetWebContents(), delegate_->GetWebContents()->GetDelegate(), GetMinimumSignInViewSize(), GetMaximumSignInViewSize())); - sign_in_webview_->LoadInitialURL(wallet::GetSignInUrl()); + sign_in_web_view_->LoadInitialURL(wallet::GetSignInUrl()); + + ShowDialogInMode(SIGN_IN); + sign_in_web_view_->RequestFocus(); - sign_in_webview_->SetVisible(true); - sign_in_webview_->RequestFocus(); UpdateButtonStrip(); ContentsPreferredSizeChanged(); - return &sign_in_webview_->web_contents()->GetController(); + return &sign_in_web_view_->web_contents()->GetController(); } void AutofillDialogViews::HideSignIn() { - sign_in_webview_->SetVisible(false); + ShowDialogInMode(DETAIL_INPUT); UpdateButtonStrip(); ContentsPreferredSizeChanged(); } @@ -1530,7 +1588,7 @@ TestableAutofillDialogView* AutofillDialogViews::GetTestableView() { } void AutofillDialogViews::OnSignInResize(const gfx::Size& pref_size) { - sign_in_webview_->SetPreferredSize(pref_size); + sign_in_web_view_->SetPreferredSize(pref_size); ContentsPreferredSizeChanged(); } @@ -1609,8 +1667,8 @@ gfx::Size AutofillDialogViews::GetMinimumSize() { void AutofillDialogViews::Layout() { const gfx::Rect content_bounds = GetContentsBounds(); - if (sign_in_webview_->visible()) { - sign_in_webview_->SetBoundsRect(content_bounds); + if (sign_in_web_view_->visible()) { + sign_in_web_view_->SetBoundsRect(content_bounds); return; } @@ -1800,6 +1858,7 @@ void AutofillDialogViews::OnDidChangeFocus( void AutofillDialogViews::OnSelectedIndexChanged(views::Combobox* combobox) { DetailsGroup* group = GroupForView(combobox); ValidateGroup(*group, VALIDATE_EDIT); + SetEditabilityForSection(group->section); } void AutofillDialogViews::StyledLabelLinkClicked(const gfx::Range& range, @@ -1852,8 +1911,8 @@ gfx::Size AutofillDialogViews::CalculatePreferredSize(bool get_minimum_size) { // The width is always set by the scroll area. const int width = scroll_size.width(); - if (sign_in_webview_->visible()) { - const gfx::Size size = static_cast<views::View*>(sign_in_webview_)-> + if (sign_in_web_view_->visible()) { + const gfx::Size size = static_cast<views::View*>(sign_in_web_view_)-> GetPreferredSize(); return gfx::Size(width + insets.width(), size.height() + insets.height()); } @@ -1904,6 +1963,14 @@ gfx::Size AutofillDialogViews::GetMaximumSignInViewSize() const { return gfx::Size(width, height); } +DialogSection AutofillDialogViews::GetCreditCardSection() const { + if (delegate_->SectionIsActive(SECTION_CC)) + return SECTION_CC; + + DCHECK(delegate_->SectionIsActive(SECTION_CC_BILLING)); + return SECTION_CC_BILLING; +} + void AutofillDialogViews::InitChildViews() { button_strip_extra_view_ = new LayoutPropagationView(); button_strip_extra_view_->SetLayoutManager( @@ -1936,15 +2003,15 @@ void AutofillDialogViews::InitChildViews() { AddChildView(scrollable_area_); loading_shield_ = new LoadingAnimationView(delegate_->SpinnerText()); - loading_shield_->SetVisible(false); AddChildView(loading_shield_); - sign_in_webview_ = new views::WebView(delegate_->profile()); - sign_in_webview_->SetVisible(false); - AddChildView(sign_in_webview_); + sign_in_web_view_ = new views::WebView(delegate_->profile()); + AddChildView(sign_in_web_view_); overlay_view_ = new OverlayView(delegate_); overlay_view_->SetVisible(false); + + ShowDialogInMode(DETAIL_INPUT); } views::View* AutofillDialogViews::CreateDetailsContainer() { @@ -2099,7 +2166,6 @@ void AutofillDialogViews::UpdateSectionImpl( if (text_mapping != group->textfields.end()) { DecoratedTextfield* decorated = text_mapping->second; - decorated->SetEnabled(input.editable); if (decorated->text().empty() || clobber_inputs) decorated->SetText(iter->initial_value); } @@ -2107,7 +2173,6 @@ void AutofillDialogViews::UpdateSectionImpl( ComboboxMap::iterator combo_mapping = group->comboboxes.find(&input); if (combo_mapping != group->comboboxes.end()) { views::Combobox* combobox = combo_mapping->second; - combobox->SetEnabled(input.editable); if (combobox->selected_index() == combobox->model()->GetDefaultIndex() || clobber_inputs) { for (int i = 0; i < combobox->model()->GetItemCount(); ++i) { @@ -2121,6 +2186,7 @@ void AutofillDialogViews::UpdateSectionImpl( } SetIconsForSection(section); + SetEditabilityForSection(section); UpdateDetailsGroupState(*group); } @@ -2230,7 +2296,7 @@ void AutofillDialogViews::MarkInputsInvalid( ++it; } - if (section == SECTION_CC) { + if (section == GetCreditCardSection()) { // Special case CVC as it's not part of |group->manual_input|. const ValidityMessage& message = messages.GetMessageOrDefault(CREDIT_CARD_VERIFICATION_CODE); @@ -2252,14 +2318,14 @@ bool AutofillDialogViews::ValidateGroup(const DetailsGroup& group, if (group.manual_input->visible()) { for (TextfieldMap::const_iterator iter = group.textfields.begin(); iter != group.textfields.end(); ++iter) { - if (!iter->first->editable) + if (!iter->second->editable()) continue; detail_outputs[iter->first] = iter->second->text(); } for (ComboboxMap::const_iterator iter = group.comboboxes.begin(); iter != group.comboboxes.end(); ++iter) { - if (!iter->first->editable) + if (!iter->second->enabled()) continue; views::Combobox* combobox = iter->second; @@ -2267,12 +2333,14 @@ bool AutofillDialogViews::ValidateGroup(const DetailsGroup& group, combobox->model()->GetItemAt(combobox->selected_index()); detail_outputs[iter->first] = item; } - } else if (group.section == SECTION_CC) { + } else if (group.section == GetCreditCardSection()) { DecoratedTextfield* decorated_cvc = group.suggested_info->decorated_textfield(); - cvc_input.reset(new DetailInput); - cvc_input->type = CREDIT_CARD_VERIFICATION_CODE; - detail_outputs[cvc_input.get()] = decorated_cvc->text(); + if (decorated_cvc->visible()) { + cvc_input.reset(new DetailInput); + cvc_input->type = CREDIT_CARD_VERIFICATION_CODE; + detail_outputs[cvc_input.get()] = decorated_cvc->text(); + } } ValidityMessages validity = delegate_->InputsAreValid(group.section, @@ -2352,6 +2420,8 @@ void AutofillDialogViews::TextfieldEditedOrActivated( if (delegate_->FieldControlsIcons(type)) SetIconsForSection(group->section); + + SetEditabilityForSection(group->section); } void AutofillDialogViews::UpdateButtonStripExtraView() { @@ -2467,6 +2537,31 @@ void AutofillDialogViews::SetIconsForSection(DialogSection section) { } } +void AutofillDialogViews::SetEditabilityForSection(DialogSection section) { + const DetailInputs& inputs = + delegate_->RequestedFieldsForSection(section); + DetailsGroup* group = GroupForSection(section); + + for (DetailInputs::const_iterator iter = inputs.begin(); + iter != inputs.end(); ++iter) { + const DetailInput& input = *iter; + bool editable = delegate_->InputIsEditable(input, section); + + TextfieldMap::iterator text_mapping = group->textfields.find(&input); + if (text_mapping != group->textfields.end()) { + DecoratedTextfield* decorated = text_mapping->second; + decorated->SetEditable(editable); + continue; + } + + ComboboxMap::iterator combo_mapping = group->comboboxes.find(&input); + if (combo_mapping != group->comboboxes.end()) { + views::Combobox* combobox = combo_mapping->second; + combobox->SetEnabled(editable); + } + } +} + AutofillDialogViews::DetailsGroup::DetailsGroup(DialogSection section) : section(section), container(NULL), diff --git a/chrome/browser/ui/views/autofill/autofill_dialog_views.h b/chrome/browser/ui/views/autofill/autofill_dialog_views.h index ef59102563..4f57cf44f9 100644 --- a/chrome/browser/ui/views/autofill/autofill_dialog_views.h +++ b/chrome/browser/ui/views/autofill/autofill_dialog_views.h @@ -167,6 +167,26 @@ class AutofillDialogViews : public AutofillDialogView, virtual void OnMenuButtonClicked(views::View* source, const gfx::Point& point) OVERRIDE; + protected: + // What the entire dialog should be doing (e.g. gathering info from the user, + // asking the user to sign in, etc.). + enum DialogMode { + DETAIL_INPUT, + LOADING, + SIGN_IN, + }; + + // Changes the function of the whole dialog. Currently this can show a loading + // shield, an embedded sign in web view, or the more typical detail input mode + // (suggestions and form inputs). + void ShowDialogInMode(DialogMode dialog_mode); + + // Exposed for testing. + views::View* GetLoadingShieldForTesting(); + views::WebView* GetSignInWebViewForTesting(); + views::View* GetNotificationAreaForTesting(); + views::View* GetScrollableAreaForTesting(); + private: // A class that creates and manages a widget for error messages. class ErrorBubble : public views::BubbleDelegateView { @@ -207,6 +227,9 @@ class AutofillDialogViews : public AutofillDialogView, // right edge of |anchor_|. Must contain |anchor_|. views::View* const anchor_container_; // Weak. + // Whether the bubble should be shown above the anchor (default is below). + const bool show_above_anchor_; + DISALLOW_COPY_AND_ASSIGN(ErrorBubble); }; @@ -504,6 +527,9 @@ class AutofillDialogViews : public AutofillDialogView, // Returns the maximum size of the sign in view for this dialog. gfx::Size GetMaximumSignInViewSize() const; + // Returns which section should currently be used for credit card info. + DialogSection GetCreditCardSection() const; + void InitChildViews(); // Creates and returns a view that holds all detail sections. @@ -519,10 +545,6 @@ class AutofillDialogViews : public AutofillDialogView, // inputs View, and suggestion model. Relevant pointers are stored in |group|. void CreateDetailsSection(DialogSection section); - // Like CreateDetailsSection, but creates the combined billing/cc section, - // which is somewhat more complicated than the others. - void CreateBillingSection(); - // Creates the view that holds controls for inputing or selecting data for // a given section. views::View* CreateInputsContainer(DialogSection section); @@ -603,6 +625,10 @@ class AutofillDialogViews : public AutofillDialogView, // sets the credit card and CVC icons according to the credit card number. void SetIconsForSection(DialogSection section); + // Iterates over all the inputs in |section| and sets their enabled/disabled + // state. + void SetEditabilityForSection(DialogSection section); + // The delegate that drives this view. Weak pointer, always non-NULL. AutofillDialogViewDelegate* const delegate_; @@ -634,7 +660,7 @@ class AutofillDialogViews : public AutofillDialogView, // A WebView to that navigates to a Google sign-in page to allow the user to // sign-in. - views::WebView* sign_in_webview_; + views::WebView* sign_in_web_view_; // View that wraps |details_container_| and makes it scroll vertically. views::ScrollView* scrollable_area_; diff --git a/chrome/browser/ui/views/autofill/autofill_dialog_views_unittest.cc b/chrome/browser/ui/views/autofill/autofill_dialog_views_unittest.cc new file mode 100644 index 0000000000..04087d0fd7 --- /dev/null +++ b/chrome/browser/ui/views/autofill/autofill_dialog_views_unittest.cc @@ -0,0 +1,186 @@ +// Copyright 2013 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/ui/views/autofill/autofill_dialog_views.h" + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/ui/autofill/mock_autofill_dialog_view_delegate.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/views/frame/browser_view.h" +#include "chrome/browser/ui/views/frame/test_with_browser_view.h" +#include "components/web_modal/test_web_contents_modal_dialog_host.h" +#include "components/web_modal/test_web_contents_modal_dialog_manager_delegate.h" +#include "components/web_modal/web_contents_modal_dialog_manager.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/views/controls/webview/webview.h" +#include "ui/views/widget/widget.h" + +namespace autofill { + +namespace { + +using web_modal::WebContentsModalDialogManager; + +// A views implementation of the Autofill dialog with slightly more testability. +class TestAutofillDialogViews : public AutofillDialogViews { + public: + explicit TestAutofillDialogViews(AutofillDialogViewDelegate* delegate) + : AutofillDialogViews(delegate) {} + virtual ~TestAutofillDialogViews() {} + + void ShowLoadingMode() { ShowDialogInMode(LOADING); } + void ShowSignInMode() { ShowDialogInMode(SIGN_IN); } + void ShowDetailInputMode() { ShowDialogInMode(DETAIL_INPUT); } + + using AutofillDialogViews::GetLoadingShieldForTesting; + using AutofillDialogViews::GetSignInWebViewForTesting; + using AutofillDialogViews::GetNotificationAreaForTesting; + using AutofillDialogViews::GetScrollableAreaForTesting; + + private: + DISALLOW_COPY_AND_ASSIGN(TestAutofillDialogViews); +}; + +} // namespace + +class AutofillDialogViewsTest : public TestWithBrowserView { + public: + AutofillDialogViewsTest() {} + virtual ~AutofillDialogViewsTest() {} + + // TestWithBrowserView: + virtual void SetUp() OVERRIDE { + TestWithBrowserView::SetUp(); + + AddTab(browser(), GURL()); + TabStripModel* tab_strip_model = browser()->tab_strip_model(); + content::WebContents* contents = tab_strip_model->GetWebContentsAt(0); + ASSERT_TRUE(contents); + view_delegate_.SetWebContents(contents); + + BrowserView* browser_view = + BrowserView::GetBrowserViewForBrowser(browser()); + dialog_host_.reset(new web_modal::TestWebContentsModalDialogHost( + browser_view->GetWidget()->GetNativeView())); + dialog_delegate_.set_web_contents_modal_dialog_host(dialog_host_.get()); + + WebContentsModalDialogManager* dialog_manager = + WebContentsModalDialogManager::FromWebContents(contents); + ASSERT_TRUE(dialog_manager); + dialog_manager->SetDelegate(&dialog_delegate_); + + dialog_.reset(new TestAutofillDialogViews(&view_delegate_)); + dialog_->Show(); + } + + virtual void TearDown() OVERRIDE { + dialog_->GetWidget()->CloseNow(); + + TestWithBrowserView::TearDown(); + } + + TestAutofillDialogViews* dialog() { return dialog_.get(); } + + protected: + // Allows each main section (e.g. loading shield, sign in web view, + // notification area, and scrollable area) to get focus. Used in focus-related + // tests. + void SetSectionsFocusable() { + dialog()->GetLoadingShieldForTesting()->set_focusable(true); + // The sign in web view has a different implementation of IsFocusable(). + dialog()->GetNotificationAreaForTesting()->set_focusable(true); + dialog()->GetScrollableAreaForTesting()->set_focusable(true); + } + + private: + // Fake dialog delegate and host to isolate test behavior. + web_modal::TestWebContentsModalDialogManagerDelegate dialog_delegate_; + scoped_ptr<web_modal::TestWebContentsModalDialogHost> dialog_host_; + + // Mock view delegate as this file only tests the view. + testing::NiceMock<MockAutofillDialogViewDelegate> view_delegate_; + + scoped_ptr<TestAutofillDialogViews> dialog_; +}; + +TEST_F(AutofillDialogViewsTest, ShowLoadingMode) { + SetSectionsFocusable(); + + dialog()->ShowLoadingMode(); + + views::View* loading_shield = dialog()->GetLoadingShieldForTesting(); + EXPECT_TRUE(loading_shield->visible()); + EXPECT_TRUE(loading_shield->IsFocusable()); + + loading_shield->RequestFocus(); + EXPECT_TRUE(loading_shield->HasFocus()); + + views::View* sign_in_web_view = dialog()->GetSignInWebViewForTesting(); + EXPECT_FALSE(sign_in_web_view->visible()); + EXPECT_FALSE(sign_in_web_view->IsFocusable()); + + views::View* notification_area = dialog()->GetNotificationAreaForTesting(); + EXPECT_FALSE(notification_area->visible()); + EXPECT_FALSE(notification_area->IsFocusable()); + + views::View* scrollable_area = dialog()->GetScrollableAreaForTesting(); + EXPECT_FALSE(scrollable_area->visible()); + EXPECT_FALSE(scrollable_area->IsFocusable()); +} + +TEST_F(AutofillDialogViewsTest, ShowSignInMode) { + SetSectionsFocusable(); + + dialog()->ShowSignInMode(); + + views::View* loading_shield = dialog()->GetLoadingShieldForTesting(); + EXPECT_FALSE(loading_shield->visible()); + EXPECT_FALSE(loading_shield->IsFocusable()); + + views::View* sign_in_web_view = dialog()->GetSignInWebViewForTesting(); + EXPECT_TRUE(sign_in_web_view->visible()); + // NOTE: |sign_in_web_view| is not focusable until a web contents is created. + // TODO(dbeam): figure out how to create a web contents on the right thread. + + views::View* notification_area = dialog()->GetNotificationAreaForTesting(); + EXPECT_FALSE(notification_area->visible()); + EXPECT_FALSE(notification_area->IsFocusable()); + + views::View* scrollable_area = dialog()->GetScrollableAreaForTesting(); + EXPECT_FALSE(scrollable_area->visible()); + EXPECT_FALSE(scrollable_area->IsFocusable()); +} + +TEST_F(AutofillDialogViewsTest, ShowDetailInputMode) { + SetSectionsFocusable(); + + dialog()->ShowDetailInputMode(); + + views::View* loading_shield = dialog()->GetLoadingShieldForTesting(); + EXPECT_FALSE(loading_shield->visible()); + EXPECT_FALSE(loading_shield->IsFocusable()); + + views::View* sign_in_web_view = dialog()->GetSignInWebViewForTesting(); + EXPECT_FALSE(sign_in_web_view->visible()); + EXPECT_FALSE(sign_in_web_view->IsFocusable()); + + views::View* notification_area = dialog()->GetNotificationAreaForTesting(); + EXPECT_TRUE(notification_area->visible()); + EXPECT_TRUE(notification_area->IsFocusable()); + + views::View* scrollable_area = dialog()->GetScrollableAreaForTesting(); + EXPECT_TRUE(scrollable_area->visible()); + EXPECT_TRUE(scrollable_area->IsFocusable()); + + notification_area->RequestFocus(); + EXPECT_TRUE(notification_area->HasFocus()); + + scrollable_area->RequestFocus(); + EXPECT_TRUE(scrollable_area->HasFocus()); +} + +} // namespace autofill diff --git a/chrome/browser/ui/views/autofill/decorated_textfield.cc b/chrome/browser/ui/views/autofill/decorated_textfield.cc index 02bf92d9ef..d4c0fa34f9 100644 --- a/chrome/browser/ui/views/autofill/decorated_textfield.cc +++ b/chrome/browser/ui/views/autofill/decorated_textfield.cc @@ -31,9 +31,9 @@ DecoratedTextfield::DecoratedTextfield( const base::string16& placeholder, views::TextfieldController* controller) : border_(new views::FocusableBorder()), - invalid_(false) { - set_background( - views::Background::CreateSolidBackground(GetBackgroundColor())); + invalid_(false), + editable_(true) { + UpdateBackground(); set_border(border_); // Removes the border from |native_wrapper_|. @@ -49,6 +49,9 @@ DecoratedTextfield::~DecoratedTextfield() {} void DecoratedTextfield::SetInvalid(bool invalid) { invalid_ = invalid; + if (!editable_) + return; + if (invalid) border_->SetColor(kWarningColor); else @@ -56,6 +59,24 @@ void DecoratedTextfield::SetInvalid(bool invalid) { SchedulePaint(); } +void DecoratedTextfield::SetEditable(bool editable) { + if (editable_ == editable) + return; + + editable_ = editable; + if (editable) { + SetInvalid(invalid_); + UseDefaultBackgroundColor(); + } else { + border_->SetColor(SK_ColorTRANSPARENT); + SetBackgroundColor(SK_ColorTRANSPARENT); + } + + UpdateBackground(); + SetEnabled(editable); + IconChanged(); +} + void DecoratedTextfield::SetIcon(const gfx::Image& icon) { if (!icon_view_ && icon.IsEmpty()) return; @@ -88,6 +109,13 @@ void DecoratedTextfield::SetTooltipIcon(const base::string16& text) { IconChanged(); } +base::string16 DecoratedTextfield::GetPlaceholderText() const { + if (!editable_) + return base::string16(); + + return views::Textfield::GetPlaceholderText(); +} + const char* DecoratedTextfield::GetClassName() const { return kViewClassName; } @@ -113,7 +141,7 @@ gfx::Size DecoratedTextfield::GetPreferredSize() { void DecoratedTextfield::Layout() { views::Textfield::Layout(); - if (icon_view_) { + if (icon_view_ && icon_view_->visible()) { gfx::Rect bounds = GetContentsBounds(); gfx::Size icon_size = icon_view_->GetPreferredSize(); int x = base::i18n::IsRTL() ? @@ -128,7 +156,15 @@ void DecoratedTextfield::Layout() { } } +void DecoratedTextfield::UpdateBackground() { + set_background( + views::Background::CreateSolidBackground(GetBackgroundColor())); +} + void DecoratedTextfield::IconChanged() { + // Don't show the icon if nothing else is showing. + icon_view_->SetVisible(editable_ || !text().empty()); + int icon_space = icon_view_ ? icon_view_->GetPreferredSize().width() + 2 * kTextfieldIconPadding : 0; diff --git a/chrome/browser/ui/views/autofill/decorated_textfield.h b/chrome/browser/ui/views/autofill/decorated_textfield.h index 77195af78a..9acf971ced 100644 --- a/chrome/browser/ui/views/autofill/decorated_textfield.h +++ b/chrome/browser/ui/views/autofill/decorated_textfield.h @@ -33,6 +33,10 @@ class DecoratedTextfield : public views::Textfield { void SetInvalid(bool invalid); bool invalid() const { return invalid_; } + // See docs for |editable_|. + void SetEditable(bool editable); + bool editable() const { return editable_; } + // Sets the icon to be displayed inside the textfield at the end of the // text. void SetIcon(const gfx::Image& icon); @@ -41,6 +45,9 @@ class DecoratedTextfield : public views::Textfield { // SetIcon(), if any, and will be overridden by future calls to SetIcon(). void SetTooltipIcon(const base::string16& text); + // views::Textfield implementation. + virtual base::string16 GetPlaceholderText() const OVERRIDE; + // views::View implementation. virtual const char* GetClassName() const OVERRIDE; virtual gfx::Size GetPreferredSize() OVERRIDE; @@ -51,6 +58,10 @@ class DecoratedTextfield : public views::Textfield { private: FRIEND_TEST_ALL_PREFIXES(DecoratedTextfieldTest, HeightMatchesButton); + // Updates the background of |this| after it may have changed. This is + // necessary for the sake of the padding around the native textfield. + void UpdateBackground(); + // Called to update the layout after SetIcon or SetTooltipIcon has been // called. void IconChanged(); @@ -71,6 +82,11 @@ class DecoratedTextfield : public views::Textfield { // shown to indicate invalidness). bool invalid_; + // Whether the user can edit the field. When not editable, many of the + // pieces of the textfield disappear (border, background, icon, placeholder + // text) and it can't receive focus. + bool editable_; + DISALLOW_COPY_AND_ASSIGN(DecoratedTextfield); }; diff --git a/chrome/browser/ui/views/avatar_menu_bubble_view.h b/chrome/browser/ui/views/avatar_menu_bubble_view.h index 781e6bee97..e9e722553b 100644 --- a/chrome/browser/ui/views/avatar_menu_bubble_view.h +++ b/chrome/browser/ui/views/avatar_menu_bubble_view.h @@ -131,7 +131,6 @@ class AvatarMenuBubbleView : public views::BubbleDelegateView, bool expanded_; DISALLOW_COPY_AND_ASSIGN(AvatarMenuBubbleView); - FRIEND_TEST_ALL_PREFIXES(AvatarMenuButtonTest, SignOut); }; #endif // CHROME_BROWSER_UI_VIEWS_AVATAR_MENU_BUBBLE_VIEW_H_ diff --git a/chrome/browser/ui/views/avatar_menu_button_browsertest.cc b/chrome/browser/ui/views/avatar_menu_button_browsertest.cc index a3c01250ca..02f5cb33e8 100644 --- a/chrome/browser/ui/views/avatar_menu_button_browsertest.cc +++ b/chrome/browser/ui/views/avatar_menu_button_browsertest.cc @@ -7,35 +7,24 @@ #include "base/command_line.h" #include "base/path_service.h" #include "base/strings/utf_string_conversions.h" -#include "chrome/browser/chrome_notification_types.h" -#include "chrome/browser/profiles/avatar_menu.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profiles_state.h" #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/views/avatar_menu_bubble_view.h" #include "chrome/browser/ui/views/frame/browser_view.h" -#include "chrome/browser/ui/views/profile_chooser_view.h" -#include "chrome/browser/ui/views/user_manager_view.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/test_switches.h" #include "chrome/test/base/testing_browser_process.h" -#include "content/public/test/test_utils.h" #include "grit/generated_resources.h" -#include "ui/base/l10n/l10n_util.h" -#include "ui/views/controls/button/label_button.h" -class AvatarMenuButtonTest : public InProcessBrowserTest, - public testing::WithParamInterface<bool> { +class AvatarMenuButtonTest : public InProcessBrowserTest { public: AvatarMenuButtonTest(); virtual ~AvatarMenuButtonTest(); protected: - virtual void SetUp() OVERRIDE; - - bool UsingNewProfileChooser(); void CreateTestingProfile(); AvatarMenuButton* GetAvatarMenuButton(); void StartAvatarMenu(); @@ -50,33 +39,10 @@ AvatarMenuButtonTest::AvatarMenuButtonTest() { AvatarMenuButtonTest::~AvatarMenuButtonTest() { } -void AvatarMenuButtonTest::SetUp() { - if (GetParam()) { - if (!UsingNewProfileChooser()) { - CommandLine::ForCurrentProcess()->AppendSwitch( - switches::kNewProfileManagement); - } - DCHECK(UsingNewProfileChooser()); - } else { - DCHECK(!UsingNewProfileChooser()); - } - - InProcessBrowserTest::SetUp(); -} - -bool AvatarMenuButtonTest::UsingNewProfileChooser() { - return CommandLine::ForCurrentProcess()->HasSwitch( - switches::kNewProfileManagement); -} - void AvatarMenuButtonTest::CreateTestingProfile() { ProfileManager* profile_manager = g_browser_process->profile_manager(); EXPECT_EQ(1u, profile_manager->GetNumberOfProfiles()); - // Sign in the default profile - ProfileInfoCache& cache = profile_manager->GetProfileInfoCache(); - cache.SetUserNameOfProfileAtIndex(0, UTF8ToUTF16("user_name")); - base::FilePath path; PathService::Get(chrome::DIR_USER_DATA, &path); path = path.AppendASCII("test_profile"); @@ -99,28 +65,25 @@ void AvatarMenuButtonTest::StartAvatarMenu() { ASSERT_TRUE(button); AvatarMenuBubbleView::set_close_on_deactivate(false); - ProfileChooserView::set_close_on_deactivate(false); static_cast<views::MenuButtonListener*>(button)->OnMenuButtonClicked( NULL, gfx::Point()); base::MessageLoop::current()->RunUntilIdle(); - EXPECT_NE(AvatarMenuBubbleView::IsShowing(), - ProfileChooserView::IsShowing()); + EXPECT_TRUE(AvatarMenuBubbleView::IsShowing()); } -IN_PROC_BROWSER_TEST_P(AvatarMenuButtonTest, HideOnSecondClick) { +IN_PROC_BROWSER_TEST_F(AvatarMenuButtonTest, HideOnSecondClick) { #if defined(OS_WIN) && defined(USE_ASH) // Disable this test in Metro+Ash for now (http://crbug.com/262796). if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests)) return; #endif - if (!profiles::IsMultipleProfilesEnabled() || - UsingNewProfileChooser()) { + // If multiprofile mode is not enabled, you can't switch between profiles. + if (!profiles::IsMultipleProfilesEnabled()) return; - } CreateTestingProfile(); - StartAvatarMenu(); + ASSERT_NO_FATAL_FAILURE(StartAvatarMenu()); // Verify that clicking again doesn't reshow it. AvatarMenuButton* button = GetAvatarMenuButton(); @@ -131,44 +94,4 @@ IN_PROC_BROWSER_TEST_P(AvatarMenuButtonTest, HideOnSecondClick) { AvatarMenuBubbleView::Hide(); base::MessageLoop::current()->RunUntilIdle(); EXPECT_FALSE(AvatarMenuBubbleView::IsShowing()); - EXPECT_FALSE(ProfileChooserView::IsShowing()); -} - -IN_PROC_BROWSER_TEST_P(AvatarMenuButtonTest, NewSignOut) { - if (!profiles::IsMultipleProfilesEnabled() || - !UsingNewProfileChooser()) { - return; - } - - CreateTestingProfile(); - StartAvatarMenu(); - - BrowserList* browser_list = - BrowserList::GetInstance(chrome::GetActiveDesktop()); - EXPECT_EQ(1U, browser_list->size()); - content::WindowedNotificationObserver window_close_observer( - chrome::NOTIFICATION_BROWSER_CLOSED, - content::Source<Browser>(browser())); - - AvatarMenu* menu = - ProfileChooserView::profile_bubble_->avatar_menu_.get(); - const AvatarMenu::Item& menu_item_before = - menu->GetItemAt(menu->GetActiveProfileIndex()); - EXPECT_FALSE(menu_item_before.signin_required); - - ui::MouseEvent mouse_ev(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(), 0); - menu->SetLogoutURL("about:blank"); - - ProfileChooserView::profile_bubble_->LinkClicked( - static_cast<views::Link*>( - ProfileChooserView::profile_bubble_->signout_current_profile_link_), - 0); - - EXPECT_TRUE(menu->GetItemAt(menu->GetActiveProfileIndex()).signin_required); - - window_close_observer.Wait(); // Rely on test timeout for failure indication. - EXPECT_TRUE(browser_list->empty()); } - -INSTANTIATE_TEST_CASE_P(Old, AvatarMenuButtonTest, testing::Values(false)); -INSTANTIATE_TEST_CASE_P(New, AvatarMenuButtonTest, testing::Values(true)); diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc index 89b63d256c..67f415f546 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc @@ -110,10 +110,6 @@ static gfx::ImageSkia* kDefaultFavicon = NULL; // Icon used for folders. static gfx::ImageSkia* kFolderIcon = NULL; -// Offset for where the menu is shown relative to the bottom of the -// BookmarkBarView. -static const int kMenuOffset = 3; - // Color of the drop indicator. static const SkColor kDropIndicatorColor = SK_ColorBLACK; diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc index c8a29788f6..5cce19047f 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc @@ -998,7 +998,7 @@ class BookmarkBarViewTest10 : public BookmarkBarViewEventTestBase { } }; -VIEW_TEST(BookmarkBarViewTest10, DISABLED_KeyEvents) +VIEW_TEST(BookmarkBarViewTest10, KeyEvents) // Make sure the menu closes with the following sequence: show menu, show // context menu, close context menu (via escape), then click else where. This diff --git a/chrome/browser/ui/views/browser_actions_container.cc b/chrome/browser/ui/views/browser_actions_container.cc index 850189762b..5e525e39ee 100644 --- a/chrome/browser/ui/views/browser_actions_container.cc +++ b/chrome/browser/ui/views/browser_actions_container.cc @@ -9,10 +9,12 @@ #include "base/stl_util.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/tab_helper.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/sessions/session_tab_helper.h" #include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/view_ids.h" #include "chrome/browser/ui/views/browser_action_view.h" @@ -459,7 +461,7 @@ void BrowserActionsContainer::OnWidgetDestroying(views::Widget* widget) { void BrowserActionsContainer::InspectPopup(ExtensionAction* action) { BrowserActionView* view = GetBrowserActionView(action); - ShowPopup(view->button(), ExtensionPopup::SHOW_AND_INSPECT); + ShowPopup(view->button(), ExtensionPopup::SHOW_AND_INSPECT, true); } int BrowserActionsContainer::GetCurrentTabId() const { @@ -473,7 +475,7 @@ int BrowserActionsContainer::GetCurrentTabId() const { void BrowserActionsContainer::OnBrowserActionExecuted( BrowserActionButton* button) { - ShowPopup(button, ExtensionPopup::SHOW); + ShowPopup(button, ExtensionPopup::SHOW, true); } void BrowserActionsContainer::OnBrowserActionVisibilityChanged() { @@ -686,6 +688,27 @@ void BrowserActionsContainer::BrowserActionMoved(const Extension* extension, SchedulePaint(); } +bool BrowserActionsContainer::BrowserActionShowPopup( + const extensions::Extension* extension) { + // Do not override other popups and only show in active window. The window + // must also have a toolbar, otherwise it should not be showing popups. + // TODO(justinlin): Remove toolbar check when http://crbug.com/308645 is + // fixed. + if (popup_ || + !browser_->window()->IsActive() || + !browser_->window()->IsToolbarVisible()) { + return false; + } + + for (BrowserActionViews::iterator it = browser_action_views_.begin(); + it != browser_action_views_.end(); ++it) { + BrowserActionButton* button = (*it)->button(); + if (button && button->extension() == extension) + return ShowPopup(button, ExtensionPopup::SHOW, false); + } + return false; +} + void BrowserActionsContainer::ModelLoaded() { SetContainerWidth(); } @@ -808,18 +831,21 @@ bool BrowserActionsContainer::ShouldDisplayBrowserAction( // Only display incognito-enabled extensions while in incognito mode. return (!profile_->IsOffTheRecord() || - extensions::ExtensionSystem::Get(profile_)->extension_service()-> - IsIncognitoEnabled(extension->id())); + extension_util::IsIncognitoEnabled( + extension->id(), + extensions::ExtensionSystem::Get(profile_)->extension_service())); } -void BrowserActionsContainer::ShowPopup( +bool BrowserActionsContainer::ShowPopup( BrowserActionButton* button, - ExtensionPopup::ShowAction show_action) { + ExtensionPopup::ShowAction show_action, + bool should_grant) { const Extension* extension = button->extension(); GURL popup_url; - if (model_->ExecuteBrowserAction(extension, browser_, &popup_url) != + if (model_->ExecuteBrowserAction( + extension, browser_, &popup_url, should_grant) != ExtensionToolbarModel::ACTION_SHOW_POPUP) { - return; + return false; } // If we're showing the same popup, just hide it and return. @@ -830,7 +856,7 @@ void BrowserActionsContainer::ShowPopup( HidePopup(); if (same_showing) - return; + return false; // We can get the execute event for browser actions that are not visible, // since buttons can be activated from the overflow menu (chevron). In that @@ -845,5 +871,9 @@ void BrowserActionsContainer::ShowPopup( show_action); popup_->GetWidget()->AddObserver(this); popup_button_ = button; - popup_button_->SetButtonPushed(); + + // Only set button as pushed if it was triggered by a user click. + if (should_grant) + popup_button_->SetButtonPushed(); + return true; } diff --git a/chrome/browser/ui/views/browser_actions_container.h b/chrome/browser/ui/views/browser_actions_container.h index 9e3fc949c5..70b42812b6 100644 --- a/chrome/browser/ui/views/browser_actions_container.h +++ b/chrome/browser/ui/views/browser_actions_container.h @@ -259,6 +259,8 @@ class BrowserActionsContainer const extensions::Extension* extension) OVERRIDE; virtual void BrowserActionMoved(const extensions::Extension* extension, int index) OVERRIDE; + virtual bool BrowserActionShowPopup( + const extensions::Extension* extension) OVERRIDE; virtual void ModelLoaded() OVERRIDE; void LoadImages(); @@ -309,9 +311,12 @@ class BrowserActionsContainer // for incognito. bool ShouldDisplayBrowserAction(const extensions::Extension* extension); - // Show a popup. - void ShowPopup(BrowserActionButton* button, - ExtensionPopup::ShowAction show_action); + // Show a popup. Returns true if a new popup was shown. Showing the popup will + // grant tab permissions if |should_grant| is true. Popup's shown via an API + // should not grant permissions. + bool ShowPopup(BrowserActionButton* button, + ExtensionPopup::ShowAction show_action, + bool should_grant); // The vector of browser actions (icons/image buttons for each action). Note // that not every BrowserAction in the ToolbarModel will necessarily be in diff --git a/chrome/browser/ui/views/browser_dialogs.h b/chrome/browser/ui/views/browser_dialogs.h index b7d684f8a6..d1a941f192 100644 --- a/chrome/browser/ui/views/browser_dialogs.h +++ b/chrome/browser/ui/views/browser_dialogs.h @@ -21,10 +21,6 @@ class FindBar; class Profile; class TemplateURL; -namespace extensions { -class Extension; -} - namespace chrome { // Creates and returns a find bar for the given browser window. See FindBarWin. @@ -40,12 +36,6 @@ void EditSearchEngine(gfx::NativeWindow parent, EditSearchEngineControllerDelegate* delegate, Profile* profile); -// Shows the create chrome app shortcut dialog box. -void ShowCreateChromeAppShortcutsDialog(gfx::NativeWindow parent_window, - Profile* profile, - const extensions::Extension* app, - const base::Closure& close_callback); - } // namespace chrome #endif // CHROME_BROWSER_UI_VIEWS_BROWSER_DIALOGS_H_ diff --git a/chrome/browser/ui/views/constrained_window_views.cc b/chrome/browser/ui/views/constrained_window_views.cc index 81fb7db462..1309cc221b 100644 --- a/chrome/browser/ui/views/constrained_window_views.cc +++ b/chrome/browser/ui/views/constrained_window_views.cc @@ -101,6 +101,10 @@ void UpdateModalDialogPosition( views::Widget* widget, web_modal::ModalDialogHost* dialog_host, const gfx::Size& size) { + // Do not forcibly update the dialog widget position if it is being dragged. + if (widget->HasCapture()) + return; + gfx::Point position = dialog_host->GetDialogPosition(size); views::Border* border = widget->non_client_view()->frame_view()->border(); @@ -319,9 +323,6 @@ namespace { // The frame border is only visible in restored mode and is hardcoded to 4 px on // each side regardless of the system window border size. const int kFrameBorderThickness = 4; -// Various edges of the frame border have a 1 px shadow along their edges; in a -// few cases we shift elements based on this amount for visual appeal. -const int kFrameShadowThickness = 1; // In the window corners, the resize areas don't actually expand bigger, but the // 16 px at the end of each edge triggers diagonal resizing. const int kResizeAreaCornerSize = 16; @@ -647,8 +648,7 @@ views::NonClientFrameView* CreateConstrainedStyleNonClientFrameView( force_opaque_border); } #if defined(USE_ASH) - ash::CustomFrameViewAsh* frame = new ash::CustomFrameViewAsh; - frame->Init(widget); + ash::CustomFrameViewAsh* frame = new ash::CustomFrameViewAsh(widget); // Always use "active" look. frame->SetInactiveRenderingDisabled(true); return frame; diff --git a/chrome/browser/ui/views/constrained_window_views_unittest.cc b/chrome/browser/ui/views/constrained_window_views_unittest.cc index 16f6d45812..bba0585569 100644 --- a/chrome/browser/ui/views/constrained_window_views_unittest.cc +++ b/chrome/browser/ui/views/constrained_window_views_unittest.cc @@ -4,7 +4,7 @@ #include "chrome/browser/ui/views/constrained_window_views.h" -#include "components/web_modal/web_contents_modal_dialog_host.h" +#include "components/web_modal/test_web_contents_modal_dialog_host.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/point.h" #include "ui/gfx/rect.h" @@ -13,10 +13,6 @@ #include "ui/views/widget/widget.h" #include "ui/views/window/dialog_delegate.h" -namespace web_modal { -class WebContentsModalDialogHostObserver; -} - namespace views { class DialogContents : public DialogDelegateView { @@ -39,37 +35,6 @@ class DialogContents : public DialogDelegateView { DISALLOW_COPY_AND_ASSIGN(DialogContents); }; -class DialogHost : public web_modal::WebContentsModalDialogHost { - public: - explicit DialogHost(gfx::NativeView host_view) - : host_view_(host_view), - max_dialog_size_(5000, 5000) { - } - - virtual ~DialogHost() {} - - void set_max_dialog_size(const gfx::Size& max_dialog_size) { - max_dialog_size_ = max_dialog_size; - } - - // Overridden from WebContentsModalDialogHost: - virtual gfx::NativeView GetHostView() const OVERRIDE { return host_view_; } - virtual gfx::Point GetDialogPosition(const gfx::Size& size) OVERRIDE { - return gfx::Point(); - } - virtual gfx::Size GetMaximumDialogSize() OVERRIDE { return max_dialog_size_; } - virtual void AddObserver( - web_modal::ModalDialogHostObserver* observer) OVERRIDE {}; - virtual void RemoveObserver( - web_modal::ModalDialogHostObserver* observer) OVERRIDE {}; - - private: - gfx::NativeView host_view_; - gfx::Size max_dialog_size_; - - DISALLOW_COPY_AND_ASSIGN(DialogHost); -}; - class ConstrainedWindowViewsTest : public ViewsTestBase { public: ConstrainedWindowViewsTest() : contents_(NULL) {} @@ -83,7 +48,9 @@ class ConstrainedWindowViewsTest : public ViewsTestBase { params.delegate = contents_; dialog_.reset(new Widget); dialog_->Init(params); - dialog_host_.reset(new DialogHost(dialog_->GetNativeView())); + dialog_host_.reset(new web_modal::TestWebContentsModalDialogHost( + dialog_->GetNativeView())); + dialog_host_->set_max_dialog_size(gfx::Size(5000, 5000)); // Make sure the dialog size is dominated by the preferred size of the // contents. @@ -104,12 +71,14 @@ class ConstrainedWindowViewsTest : public ViewsTestBase { } DialogContents* contents() { return contents_; } - DialogHost* dialog_host() { return dialog_host_.get(); } + web_modal::TestWebContentsModalDialogHost* dialog_host() { + return dialog_host_.get(); + } Widget* dialog() { return dialog_.get(); } private: DialogContents* contents_; - scoped_ptr<DialogHost> dialog_host_; + scoped_ptr<web_modal::TestWebContentsModalDialogHost> dialog_host_; scoped_ptr<Widget> dialog_; DISALLOW_COPY_AND_ASSIGN(ConstrainedWindowViewsTest); diff --git a/chrome/browser/ui/views/desktop_media_picker_views.cc b/chrome/browser/ui/views/desktop_media_picker_views.cc index 782b736ea2..4155e5bac5 100644 --- a/chrome/browser/ui/views/desktop_media_picker_views.cc +++ b/chrome/browser/ui/views/desktop_media_picker_views.cc @@ -113,6 +113,9 @@ class DesktopMediaListView : public views::View, // Called by DesktopMediaSourceView when selection has changed. void OnSelectionChanged(); + // Called by DesktopMediaSourceView when a source has been double-clicked. + void OnDoubleClick(); + // Returns currently selected source. DesktopMediaSourceView* GetSelection(); @@ -149,6 +152,7 @@ class DesktopMediaPickerDialogView : public views::DialogDelegateView { // Called by DesktopMediaListView. void OnSelectionChanged(); + void OnDoubleClick(); // views::View overrides. virtual gfx::Size GetPreferredSize() OVERRIDE; @@ -292,7 +296,12 @@ void DesktopMediaSourceView::OnFocus() { } bool DesktopMediaSourceView::OnMousePressed(const ui::MouseEvent& event) { - RequestFocus(); + if (event.GetClickCount() == 1) { + RequestFocus(); + } else if (event.GetClickCount() == 2) { + RequestFocus(); + parent_->OnDoubleClick(); + } return true; } @@ -316,6 +325,10 @@ void DesktopMediaListView::OnSelectionChanged() { parent_->OnSelectionChanged(); } +void DesktopMediaListView::OnDoubleClick() { + parent_->OnDoubleClick(); +} + DesktopMediaSourceView* DesktopMediaListView::GetSelection() { for (int i = 0; i < child_count(); ++i) { DesktopMediaSourceView* source_view = @@ -550,6 +563,11 @@ void DesktopMediaPickerDialogView::OnSelectionChanged() { GetDialogClientView()->UpdateDialogButtons(); } +void DesktopMediaPickerDialogView::OnDoubleClick() { + // This will call Accept() and close the dialog. + GetDialogClientView()->AcceptWindow(); +} + DesktopMediaPickerViews::DesktopMediaPickerViews() : dialog_(NULL) { } diff --git a/chrome/browser/ui/views/download/download_item_view.cc b/chrome/browser/ui/views/download/download_item_view.cc index a9d7832f31..3ff7201080 100644 --- a/chrome/browser/ui/views/download/download_item_view.cc +++ b/chrome/browser/ui/views/download/download_item_view.cc @@ -51,9 +51,7 @@ // different screen resolutions. static const int kTextWidth = 140; // Pixels static const int kDangerousTextWidth = 200; // Pixels -static const int kHorizontalTextPadding = 2; // Pixels static const int kVerticalPadding = 3; // Pixels -static const int kVerticalTextSpacer = 2; // Pixels static const int kVerticalTextPadding = 2; // Pixels static const int kTooltipMaxWidth = 800; // Pixels @@ -1074,7 +1072,7 @@ void DownloadItemView::ClearWarningDialog() { void DownloadItemView::ShowWarningDialog() { DCHECK(mode_ != DANGEROUS_MODE && mode_ != MALICIOUS_MODE); - mode_ = ((model_.IsMalicious()) ? MALICIOUS_MODE : DANGEROUS_MODE); + mode_ = model_.MightBeMalicious() ? MALICIOUS_MODE : DANGEROUS_MODE; body_state_ = NORMAL; drop_down_state_ = NORMAL; diff --git a/chrome/browser/ui/views/download/download_started_animation_views.cc b/chrome/browser/ui/views/download/download_started_animation_views.cc index 31d6879962..82f5b58727 100644 --- a/chrome/browser/ui/views/download/download_started_animation_views.cc +++ b/chrome/browser/ui/views/download/download_started_animation_views.cc @@ -21,15 +21,10 @@ using content::WebContents; // How long to spend moving downwards and fading out after waiting. -static const int kMoveTimeMs = 600; +const int kMoveTimeMs = 600; // The animation framerate. -static const int kFrameRateHz = 60; - -// What fraction of the frame height to move downward from the frame center. -// Note that setting this greater than 0.5 will mean moving past the bottom of -// the frame. -static const double kMoveFraction = 1.0 / 3.0; +const int kFrameRateHz = 60; namespace { diff --git a/chrome/browser/ui/views/external_tab_container_win.cc b/chrome/browser/ui/views/external_tab_container_win.cc index 5fc81f09c3..de2ba67c5e 100644 --- a/chrome/browser/ui/views/external_tab_container_win.cc +++ b/chrome/browser/ui/views/external_tab_container_win.cc @@ -1065,6 +1065,7 @@ bool ExternalTabContainerWin::OnMessageReceived(const IPC::Message& message) { void ExternalTabContainerWin::DidFailProvisionalLoad( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& validated_url, int error_code, diff --git a/chrome/browser/ui/views/external_tab_container_win.h b/chrome/browser/ui/views/external_tab_container_win.h index 495d579618..91c3dfe3d9 100644 --- a/chrome/browser/ui/views/external_tab_container_win.h +++ b/chrome/browser/ui/views/external_tab_container_win.h @@ -183,6 +183,7 @@ class ExternalTabContainerWin : public ExternalTabContainer, virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; virtual void DidFailProvisionalLoad( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& validated_url, int error_code, diff --git a/chrome/browser/ui/views/frame/browser_frame.cc b/chrome/browser/ui/views/frame/browser_frame.cc index 3c5fb8b6ce..e45d211c9b 100644 --- a/chrome/browser/ui/views/frame/browser_frame.cc +++ b/chrome/browser/ui/views/frame/browser_frame.cc @@ -45,6 +45,10 @@ #include "chrome/browser/ui/ash/ash_init.h" #endif +#if defined(OS_CHROMEOS) +#include "ash/session_state_delegate.h" +#endif + //////////////////////////////////////////////////////////////////////////////// // BrowserFrame, public: @@ -240,6 +244,17 @@ void BrowserFrame::ShowContextMenuForView(views::View* source, } ui::MenuModel* BrowserFrame::GetSystemMenuModel() { +#if defined(OS_CHROMEOS) + ash::SessionStateDelegate* delegate = + ash::Shell::GetInstance()->session_state_delegate(); + if (delegate && delegate->NumberOfLoggedInUsers() > 1) { + // In Multi user mode, the number of users as well as the order of users + // can change. Coming here we have more then one user and since the menu + // model contains the user information, it must get updated to show any + // changes happened since the last invocation. + menu_model_builder_.reset(); + } +#endif if (!menu_model_builder_.get()) { menu_model_builder_.reset( new SystemMenuModelBuilder(browser_view_, browser_view_->browser())); @@ -252,6 +267,10 @@ AvatarMenuButton* BrowserFrame::GetAvatarMenuButton() { return browser_frame_view_->avatar_button(); } +NewAvatarButton* BrowserFrame::GetNewAvatarMenuButton() { + return browser_frame_view_->new_avatar_button(); +} + #if !defined(OS_WIN) || defined(USE_AURA) bool BrowserFrame::ShouldLeaveOffsetNearTopBorder() { return !IsMaximized(); diff --git a/chrome/browser/ui/views/frame/browser_frame.h b/chrome/browser/ui/views/frame/browser_frame.h index 023e143a2d..5c130affab 100644 --- a/chrome/browser/ui/views/frame/browser_frame.h +++ b/chrome/browser/ui/views/frame/browser_frame.h @@ -16,6 +16,7 @@ class AvatarMenuButton; class BrowserRootView; class BrowserView; class NativeBrowserFrame; +class NewAvatarButton; class NonClientFrameView; class SystemMenuModelBuilder; @@ -98,7 +99,10 @@ class BrowserFrame AvatarMenuButton* GetAvatarMenuButton(); + NewAvatarButton* GetNewAvatarMenuButton(); + // Returns the menu model. BrowserFrame owns the returned model. + // Note that in multi user mode this will upon each call create a new model. ui::MenuModel* GetSystemMenuModel(); private: diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc index 367aae1466..dd5e2f6704 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc @@ -9,11 +9,14 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_info_cache.h" #include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/profiles/profiles_state.h" #include "chrome/browser/ui/view_ids.h" #include "chrome/browser/ui/views/avatar_label.h" #include "chrome/browser/ui/views/avatar_menu_button.h" #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/frame/taskbar_decorator.h" +#include "chrome/browser/ui/views/new_avatar_button.h" +#include "chrome/browser/ui/views/profile_chooser_view.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" #include "third_party/skia/include/core/SkColor.h" @@ -28,7 +31,8 @@ BrowserNonClientFrameView::BrowserNonClientFrameView(BrowserFrame* frame, : frame_(frame), browser_view_(browser_view), avatar_button_(NULL), - avatar_label_(NULL) { + avatar_label_(NULL), + new_avatar_button_(NULL) { } BrowserNonClientFrameView::~BrowserNonClientFrameView() { @@ -41,7 +45,9 @@ void BrowserNonClientFrameView::VisibilityChanged(views::View* starting_from, // The first time UpdateAvatarInfo() is called the window is not visible so // DrawTaskBarDecoration() has no effect. Therefore we need to call it again // once the window is visible. - UpdateAvatarInfo(); + if (!browser_view_->IsRegularOrGuestSession() || + !profiles::IsNewProfileManagementEnabled()) + UpdateAvatarInfo(); } void BrowserNonClientFrameView::OnThemeChanged() { @@ -59,8 +65,7 @@ void BrowserNonClientFrameView::UpdateAvatarInfo() { AddChildView(avatar_label_); } avatar_button_ = new AvatarMenuButton( - browser_view_->browser(), - browser_view_->IsOffTheRecord() && !browser_view_->IsGuestSession()); + browser_view_->browser(), !browser_view_->IsRegularOrGuestSession()); avatar_button_->set_id(VIEW_ID_AVATAR_BUTTON); AddChildView(avatar_button_); frame_->GetRootView()->Layout(); @@ -111,3 +116,38 @@ void BrowserNonClientFrameView::UpdateAvatarInfo() { frame_->GetNativeWindow(), AvatarMenu::ShouldShowAvatarMenu() ? &avatar : NULL); } + +void BrowserNonClientFrameView::UpdateNewStyleAvatarInfo( + views::ButtonListener* listener, + const NewAvatarButton::AvatarButtonStyle style) { + DCHECK(profiles::IsNewProfileManagementEnabled()); + // This should never be called in incognito mode. + DCHECK(browser_view_->IsRegularOrGuestSession()); + + if (browser_view_->ShouldShowAvatar()) { + if (!new_avatar_button_) { + string16 profile_name = + profiles::GetActiveProfileDisplayName(browser_view_->browser()); + new_avatar_button_ = new NewAvatarButton( + listener, profile_name, style, browser_view_->browser()); + new_avatar_button_->set_id(VIEW_ID_NEW_AVATAR_BUTTON); + AddChildView(new_avatar_button_); + frame_->GetRootView()->Layout(); + } + } else if (new_avatar_button_) { + delete new_avatar_button_; + new_avatar_button_ = NULL; + frame_->GetRootView()->Layout(); + } +} + +void BrowserNonClientFrameView::ShowProfileChooserViewBubble() { + gfx::Point origin; + views::View::ConvertPointToScreen(new_avatar_button(), &origin); + gfx::Rect bounds(origin, size()); + + ProfileChooserView::ShowBubble( + new_avatar_button(), views::BubbleBorder::TOP_RIGHT, + views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE, bounds, + browser_view_->browser()); +} diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view.h index 91a9b0dc95..681a06e7a4 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.h +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.h @@ -5,12 +5,14 @@ #ifndef CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_NON_CLIENT_FRAME_VIEW_H_ #define CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_NON_CLIENT_FRAME_VIEW_H_ +#include "chrome/browser/ui/views/new_avatar_button.h" #include "ui/views/window/non_client_view.h" class AvatarLabel; class AvatarMenuButton; class BrowserFrame; class BrowserView; +class NewAvatarButton; // A specialization of the NonClientFrameView object that provides additional // Browser-specific methods. @@ -34,6 +36,8 @@ class BrowserNonClientFrameView : public views::NonClientFrameView { AvatarMenuButton* avatar_button() const { return avatar_button_; } + NewAvatarButton* new_avatar_button() const { return new_avatar_button_; } + AvatarLabel* avatar_label() const { return avatar_label_; } // Returns the bounds within which the TabStrip should be laid out. @@ -62,6 +66,16 @@ class BrowserNonClientFrameView : public views::NonClientFrameView { // Updates the title and icon of the avatar button. void UpdateAvatarInfo(); + // Updates the title of the avatar button displayed in the caption area. + // The button uses |style| to match the browser window style and notifies + // |listener| when it is clicked. + void UpdateNewStyleAvatarInfo(views::ButtonListener* listener, + const NewAvatarButton::AvatarButtonStyle style); + + // Anchor and show the ProfileChooser bubble under the avatar button in + // the caption area. + void ShowProfileChooserViewBubble(); + private: // The frame that hosts this view. BrowserFrame* frame_; @@ -75,6 +89,10 @@ class BrowserNonClientFrameView : public views::NonClientFrameView { // Avatar label that is used for a managed user. AvatarLabel* avatar_label_; + + // Menu button that displays the name of the active or guest profile. + // May be NULL and will not be displayed for off the record profiles. + NewAvatarButton* new_avatar_button_; }; namespace chrome { diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc index 22ef7d3c05..df32b2d005 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc @@ -6,7 +6,8 @@ #include "ash/ash_switches.h" #include "ash/wm/caption_buttons/frame_caption_button_container_view.h" -#include "ash/wm/frame_painter.h" +#include "ash/wm/frame_border_hit_test_controller.h" +#include "ash/wm/header_painter.h" #include "chrome/browser/themes/theme_properties.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/immersive_fullscreen_configuration.h" @@ -83,7 +84,9 @@ BrowserNonClientFrameViewAsh::BrowserNonClientFrameViewAsh( : BrowserNonClientFrameView(frame, browser_view), caption_button_container_(NULL), window_icon_(NULL), - frame_painter_(new ash::FramePainter) { + header_painter_(new ash::HeaderPainter), + frame_border_hit_test_controller_( + new ash::FrameBorderHitTestController(frame)) { } BrowserNonClientFrameViewAsh::~BrowserNonClientFrameViewAsh() { @@ -106,7 +109,7 @@ void BrowserNonClientFrameViewAsh::Init() { UpdateAvatarInfo(); // Frame painter handles layout. - frame_painter_->Init(frame(), window_icon_, caption_button_container_); + header_painter_->Init(frame(), this, window_icon_, caption_button_container_); } /////////////////////////////////////////////////////////////////////////////// @@ -136,7 +139,7 @@ BrowserNonClientFrameViewAsh::GetTabStripInsets(bool force_restored) const { int extra_right = ash::switches::UseAlternateFrameCaptionButtonStyle() ? kTabstripRightSpacingAlternateCaptionButtonStyle : kTabstripRightSpacing; - int right = frame_painter_->GetRightInset() + extra_right; + int right = header_painter_->GetRightInset() + extra_right; int top = NonClientTopBorderHeight(); if (force_restored) @@ -159,7 +162,7 @@ BrowserNonClientFrameViewAsh::GetTabStripInsets(bool force_restored) const { } int BrowserNonClientFrameViewAsh::GetThemeBackgroundXInset() const { - return frame_painter_->GetThemeBackgroundXInset(); + return header_painter_->GetThemeBackgroundXInset(); } void BrowserNonClientFrameViewAsh::UpdateThrobber(bool running) { @@ -172,18 +175,19 @@ void BrowserNonClientFrameViewAsh::UpdateThrobber(bool running) { gfx::Rect BrowserNonClientFrameViewAsh::GetBoundsForClientView() const { int top_height = NonClientTopBorderHeight(); - return frame_painter_->GetBoundsForClientView(top_height, bounds()); + return ash::HeaderPainter::GetBoundsForClientView(top_height, bounds()); } gfx::Rect BrowserNonClientFrameViewAsh::GetWindowBoundsForClientBounds( const gfx::Rect& client_bounds) const { int top_height = NonClientTopBorderHeight(); - return frame_painter_->GetWindowBoundsForClientBounds(top_height, - client_bounds); + return ash::HeaderPainter::GetWindowBoundsForClientBounds(top_height, + client_bounds); } int BrowserNonClientFrameViewAsh::NonClientHitTest(const gfx::Point& point) { - int hit_test = frame_painter_->NonClientHitTest(this, point); + int hit_test = ash::FrameBorderHitTestController::NonClientHitTest(this, + header_painter_.get(), point); // See if the point is actually within the avatar menu button or within // the avatar label. @@ -229,7 +233,7 @@ void BrowserNonClientFrameViewAsh::UpdateWindowIcon() { void BrowserNonClientFrameViewAsh::UpdateWindowTitle() { if (!frame()->IsFullscreen()) - frame_painter_->SchedulePaintForTitle(BrowserFrame::GetTitleFont()); + header_painter_->SchedulePaintForTitle(BrowserFrame::GetTitleFont()); } /////////////////////////////////////////////////////////////////////////////// @@ -250,25 +254,24 @@ void BrowserNonClientFrameViewAsh::OnPaint(gfx::Canvas* canvas) { int theme_frame_overlay_image_id = GetThemeFrameOverlayImageId(); ui::ThemeProvider* theme_provider = GetThemeProvider(); - ash::FramePainter::Themed header_themed = ash::FramePainter::THEMED_NO; + ash::HeaderPainter::Themed header_themed = ash::HeaderPainter::THEMED_NO; if (theme_provider->HasCustomImage(theme_frame_image_id) || (theme_frame_overlay_image_id != 0 && theme_provider->HasCustomImage(theme_frame_overlay_image_id))) { - header_themed = ash::FramePainter::THEMED_YES; + header_themed = ash::HeaderPainter::THEMED_YES; } - if (frame_painter_->ShouldUseMinimalHeaderStyle(header_themed)) + if (header_painter_->ShouldUseMinimalHeaderStyle(header_themed)) theme_frame_image_id = IDR_AURA_WINDOW_HEADER_BASE_MINIMAL; - frame_painter_->PaintHeader( - this, + header_painter_->PaintHeader( canvas, ShouldPaintAsActive() ? - ash::FramePainter::ACTIVE : ash::FramePainter::INACTIVE, + ash::HeaderPainter::ACTIVE : ash::HeaderPainter::INACTIVE, theme_frame_image_id, theme_frame_overlay_image_id); if (browser_view()->ShouldShowWindowTitle()) - frame_painter_->PaintTitleBar(this, canvas, BrowserFrame::GetTitleFont()); + header_painter_->PaintTitleBar(canvas, BrowserFrame::GetTitleFont()); if (browser_view()->IsToolbarVisible()) PaintToolbarBackground(canvas); else @@ -276,7 +279,15 @@ void BrowserNonClientFrameViewAsh::OnPaint(gfx::Canvas* canvas) { } void BrowserNonClientFrameViewAsh::Layout() { - frame_painter_->LayoutHeader(this, UseShortHeader()); + header_painter_->LayoutHeader(UseShortHeader()); + int header_height = 0; + if (browser_view()->IsTabStripVisible()) { + header_height = GetTabStripInsets(false).top + + browser_view()->GetTabStripHeight(); + } else { + header_height = NonClientTopBorderHeight(); + } + header_painter_->set_header_height(header_height); if (avatar_button()) LayoutAvatar(); BrowserNonClientFrameView::Layout(); @@ -339,12 +350,16 @@ void BrowserNonClientFrameViewAsh::GetAccessibleState( } gfx::Size BrowserNonClientFrameViewAsh::GetMinimumSize() { - return frame_painter_->GetMinimumSize(this); + gfx::Size min_client_view_size(frame()->client_view()->GetMinimumSize()); + return gfx::Size( + std::max(header_painter_->GetMinimumHeaderWidth(), + min_client_view_size.width()), + NonClientTopBorderHeight() + min_client_view_size.height()); } void BrowserNonClientFrameViewAsh::OnThemeChanged() { BrowserNonClientFrameView::OnThemeChanged(); - frame_painter_->OnThemeChanged(); + header_painter_->OnThemeChanged(); } /////////////////////////////////////////////////////////////////////////////// @@ -531,8 +546,7 @@ void BrowserNonClientFrameViewAsh::PaintContentEdge(gfx::Canvas* canvas) { } int BrowserNonClientFrameViewAsh::GetThemeFrameImageId() const { - bool is_incognito = browser_view()->IsOffTheRecord() && - !browser_view()->IsGuestSession(); + bool is_incognito = !browser_view()->IsRegularOrGuestSession(); if (browser_view()->IsBrowserTypeNormal()) { // Use the standard resource ids to allow users to theme the frames. if (ShouldPaintAsActive()) { diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h index 99530ecdd5..121bfd044a 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h @@ -13,8 +13,9 @@ class TabIconView; namespace ash { +class FrameBorderHitTestController; class FrameCaptionButtonContainerView; -class FramePainter; +class HeaderPainter; } namespace views { class ImageButton; @@ -112,8 +113,12 @@ class BrowserNonClientFrameViewAsh // For popups, the window icon. TabIconView* window_icon_; - // Painter for the frame header. - scoped_ptr<ash::FramePainter> frame_painter_; + // Helper class for painting the header. + scoped_ptr<ash::HeaderPainter> header_painter_; + + // Updates the hittest bounds overrides based on the window show type. + scoped_ptr<ash::FrameBorderHitTestController> + frame_border_hit_test_controller_; DISALLOW_COPY_AND_ASSIGN(BrowserNonClientFrameViewAsh); }; diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc index b12feeab8d..c4fec8a22b 100644 --- a/chrome/browser/ui/views/frame/browser_view.cc +++ b/chrome/browser/ui/views/frame/browser_view.cc @@ -165,19 +165,12 @@ using views::GridLayout; using web_modal::WebContentsModalDialogHost; namespace { -// The height of the status bubble. -const int kStatusBubbleHeight = 20; // The name of a key to store on the window handle so that other code can // locate this object using just the handle. const char* const kBrowserViewKey = "__BROWSER_VIEW__"; // The number of milliseconds between loading animation frames. const int kLoadingAnimationFrameTimeMs = 30; -// The amount of space we expect the window border to take up. -const int kWindowBorderWidth = 5; - -// How round the 'new tab' style bookmarks bar is. -const int kNewtabBarRoundness = 5; // TODO(kuan): These functions are temporarily for the bookmark bar while its // detached state is at the top of the page; it'll be moved to float on the @@ -548,6 +541,15 @@ bool BrowserView::IsOffTheRecord() const { return browser_->profile()->IsOffTheRecord(); } +bool BrowserView::IsGuestSession() const { + return browser_->profile()->IsGuestSession(); +} + +bool BrowserView::IsRegularOrGuestSession() const { + Profile* profile = browser_->profile(); + return (profile->IsGuestSession() || !profile->IsOffTheRecord()); +} + int BrowserView::GetOTRIconResourceID() const { int otr_resource_id = IDR_OTR_ICON; if (ui::GetDisplayLayout() == ui::LAYOUT_TOUCH) { @@ -562,10 +564,6 @@ int BrowserView::GetOTRIconResourceID() const { return otr_resource_id; } -bool BrowserView::IsGuestSession() const { - return browser_->profile()->IsGuestSession(); -} - int BrowserView::GetGuestIconResourceID() const { return IDR_GUEST_ICON; } @@ -1218,13 +1216,6 @@ void BrowserView::ConfirmBrowserCloseWithPendingDownloads( GetNativeWindow(), download_count, dialog_type, app_modal, callback); } -void BrowserView::ShowCreateChromeAppShortcutsDialog( - Profile* profile, - const extensions::Extension* app) { - chrome::ShowCreateChromeAppShortcutsDialog( - GetNativeWindow(), profile, app, base::Closure()); -} - void BrowserView::UserChangedTheme() { frame_->FrameTypeChanged(); } diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h index 5e589b8014..82bc6b3a95 100644 --- a/chrome/browser/ui/views/frame/browser_view.h +++ b/chrome/browser/ui/views/frame/browser_view.h @@ -192,14 +192,18 @@ class BrowserView : public BrowserWindow, // incognito. bool IsOffTheRecord() const; - // Returns the resource ID to use for the OTR icon, which depends on - // which layout is being shown and whether we are full-screen. - int GetOTRIconResourceID() const; - // Returns true if the profile associated with this Browser window is // a guest session. bool IsGuestSession() const; + // Returns true if the profile associated with this Browser window is + // not off the record or a guest session. + bool IsRegularOrGuestSession() const; + + // Returns the resource ID to use for the OTR icon, which depends on + // which layout is being shown and whether we are full-screen. + int GetOTRIconResourceID() const; + // Returns the resource ID to use for the Guest icon, which may depend on // which layout is being shown and whether we are full-screen. int GetGuestIconResourceID() const; @@ -350,8 +354,6 @@ class BrowserView : public BrowserWindow, bool* is_keyboard_shortcut) OVERRIDE; virtual void HandleKeyboardEvent( const content::NativeWebKeyboardEvent& event) OVERRIDE; - virtual void ShowCreateChromeAppShortcutsDialog( - Profile*, const extensions::Extension* app) OVERRIDE; virtual void Cut() OVERRIDE; virtual void Copy() OVERRIDE; virtual void Paste() OVERRIDE; diff --git a/chrome/browser/ui/views/frame/browser_view_layout.cc b/chrome/browser/ui/views/frame/browser_view_layout.cc index 0b251a7172..6ba0387288 100644 --- a/chrome/browser/ui/views/frame/browser_view_layout.cc +++ b/chrome/browser/ui/views/frame/browser_view_layout.cc @@ -39,9 +39,6 @@ namespace { // The visible height of the shadow above the tabs. Clicks in this area are // treated as clicks to the frame, rather than clicks to the tab. const int kTabShadowSize = 2; -// The number of pixels the bookmark bar should overlap the spacer by if the -// spacer is visible. -const int kSpacerBookmarkBarOverlap = 1; // The number of pixels the metro switcher is offset from the right edge. const int kWindowSwitcherOffsetX = 7; // The number of pixels the constrained window should overlap the bottom diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc index b482292827..1d191ad57c 100644 --- a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc +++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc @@ -11,9 +11,11 @@ #include "chrome/app/chrome_dll_resource.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profiles_state.h" #include "chrome/browser/themes/theme_properties.h" #include "chrome/browser/ui/views/avatar_menu_button.h" #include "chrome/browser/ui/views/frame/browser_view.h" +#include "chrome/browser/ui/views/new_avatar_button.h" #include "chrome/browser/ui/views/tabs/tab.h" #include "chrome/browser/ui/views/tabs/tab_strip.h" #include "chrome/browser/ui/views/toolbar_view.h" @@ -55,6 +57,8 @@ const int kAvatarBottomSpacing = 2; const int kAvatarLeftSpacing = 2; // Space between the right edge of the avatar and the tabstrip. const int kAvatarRightSpacing = -2; +// How far the new avatar button is from the left of the minimize button. +const int kNewAvatarButtonOffset = 5; // The content left/right images have a shadow built into them. const int kContentEdgeShadowThickness = 2; // The top 3 px of the tabstrip is shadow; in maximized mode we push this off @@ -85,7 +89,12 @@ GlassBrowserFrameView::GlassBrowserFrameView(BrowserFrame* frame, if (browser_view->ShouldShowWindowIcon()) InitThrobberIcons(); - UpdateAvatarInfo(); + if (browser_view->IsRegularOrGuestSession() && + profiles::IsNewProfileManagementEnabled()) + UpdateNewStyleAvatarInfo(this, NewAvatarButton::NATIVE_BUTTON); + else + UpdateAvatarInfo(); + if (!browser_view->IsOffTheRecord()) { registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, content::NotificationService::AllSources()); @@ -102,6 +111,12 @@ gfx::Rect GlassBrowserFrameView::GetBoundsForTabStrip( views::View* tabstrip) const { int minimize_button_offset = std::min(frame()->GetMinimizeButtonOffset(), width()); + + // The new avatar button is optionally displayed to the left of the + // minimize button. + if (browser_view()->ShouldShowAvatar() && new_avatar_button()) + minimize_button_offset -= new_avatar_button()->width(); + int tabstrip_x = browser_view()->ShouldShowAvatar() ? (avatar_bounds_.right() + kAvatarRightSpacing) : NonClientBorderThickness() + kTabStripIndent; @@ -210,6 +225,10 @@ int GlassBrowserFrameView::NonClientHitTest(const gfx::Point& point) { if (avatar_button() && avatar_button()->GetMirroredBounds().Contains(point)) return HTCLIENT; + if (new_avatar_button() && + new_avatar_button()->GetMirroredBounds().Contains(point)) + return HTCLIENT; + int frame_component = frame()->client_view()->NonClientHitTest(point); // See if we're in the sysmenu region. We still have to check the tabstrip @@ -244,14 +263,32 @@ void GlassBrowserFrameView::OnPaint(gfx::Canvas* canvas) { } void GlassBrowserFrameView::Layout() { - LayoutAvatar(); + if (browser_view()->ShouldShowAvatar()) { + if (browser_view()->IsRegularOrGuestSession() && + profiles::IsNewProfileManagementEnabled()) + LayoutNewStyleAvatar(); + else + LayoutAvatar(); + } + LayoutClientView(); } bool GlassBrowserFrameView::HitTestRect(const gfx::Rect& rect) const { - return (avatar_button() && - avatar_button()->GetMirroredBounds().Intersects(rect)) || - !frame()->client_view()->bounds().Intersects(rect); + bool hit_avatar_button = avatar_button() && + avatar_button()->GetMirroredBounds().Intersects(rect); + bool hit_new_avatar_button = new_avatar_button() && + new_avatar_button()->GetMirroredBounds().Intersects(rect); + return hit_avatar_button || hit_new_avatar_button || + !frame()->client_view()->bounds().Intersects(rect); +} + +/////////////////////////////////////////////////////////////////////////////// +// GlassBrowserFrameView, views::ButtonListener overrides: +void GlassBrowserFrameView::ButtonPressed(views::Button* sender, + const ui::Event& event) { + if (sender == new_avatar_button()) + ShowProfileChooserViewBubble(); } /////////////////////////////////////////////////////////////////////////////// @@ -403,6 +440,28 @@ void GlassBrowserFrameView::PaintRestoredClientEdge(gfx::Canvas* canvas) { toolbar_color); } +void GlassBrowserFrameView::LayoutNewStyleAvatar() { + if (!new_avatar_button()) + return; + + gfx::Size label_size = new_avatar_button()->GetPreferredSize(); + int button_size_with_offset = kNewAvatarButtonOffset + label_size.width(); + + int button_x = frame()->GetMinimizeButtonOffset() - + kNewAvatarButtonOffset - label_size.width(); + + if (base::i18n::IsRTL()) + button_x = width() - frame()->GetMinimizeButtonOffset() + + kNewAvatarButtonOffset; + + int button_y = frame()->IsMaximized() ? NonClientTopBorderHeight(false) : 1; + new_avatar_button()->SetBounds( + button_x, + button_y, + label_size.width(), + button_y + gfx::win::GetSystemMetricsInDIP(SM_CXMENUSIZE)); +} + void GlassBrowserFrameView::LayoutAvatar() { // Even though the avatar is used for both incognito and profiles we always // use the incognito icon to layout the avatar button. The profile icon @@ -504,7 +563,11 @@ void GlassBrowserFrameView::Observe( const content::NotificationDetails& details) { switch (type) { case chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED: - UpdateAvatarInfo(); + if (browser_view()->IsRegularOrGuestSession() && + profiles::IsNewProfileManagementEnabled()) + UpdateNewStyleAvatarInfo(this, NewAvatarButton::NATIVE_BUTTON); + else + UpdateAvatarInfo(); break; default: NOTREACHED() << "Got a notification we didn't register for!"; diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.h b/chrome/browser/ui/views/frame/glass_browser_frame_view.h index d872738c1c..c9b2701c3b 100644 --- a/chrome/browser/ui/views/frame/glass_browser_frame_view.h +++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.h @@ -16,6 +16,7 @@ class BrowserView; class GlassBrowserFrameView : public BrowserNonClientFrameView, + public views::ButtonListener, public content::NotificationObserver { public: // Constructs a non-client view for an BrowserFrame. @@ -46,6 +47,10 @@ class GlassBrowserFrameView : public BrowserNonClientFrameView, virtual void Layout() OVERRIDE; virtual bool HitTestRect(const gfx::Rect& rect) const OVERRIDE; + // Overidden from views::ButtonListener: + virtual void ButtonPressed(views::Button* sender, + const ui::Event& event) OVERRIDE; + private: // Returns the thickness of the border that makes up the window frame edges. // This does not include any client edge. @@ -66,6 +71,7 @@ class GlassBrowserFrameView : public BrowserNonClientFrameView, // Layout various sub-components of this view. void LayoutAvatar(); + void LayoutNewStyleAvatar(); void LayoutClientView(); // Returns the insets of the client area. diff --git a/chrome/browser/ui/views/frame/global_menu_bar_x11.cc b/chrome/browser/ui/views/frame/global_menu_bar_x11.cc index 77db18956f..748fb43333 100644 --- a/chrome/browser/ui/views/frame/global_menu_bar_x11.cc +++ b/chrome/browser/ui/views/frame/global_menu_bar_x11.cc @@ -127,8 +127,7 @@ const int MENU_SEPARATOR =-1; const int MENU_END = -2; const int MENU_DISABLED_ID = -3; -// These tag values are used to refer to menu itesm. -const int TAG_NORMAL = 0; +// These tag values are used to refer to menu items. const int TAG_MOST_VISITED = 1; const int TAG_RECENTLY_CLOSED = 2; const int TAG_MOST_VISITED_HEADER = 3; @@ -228,6 +227,7 @@ GlobalMenuBarCommand tools_menu[] = { { IDS_VIEW_SOURCE, IDC_VIEW_SOURCE }, { IDS_DEV_TOOLS, IDC_DEV_TOOLS }, { IDS_DEV_TOOLS_CONSOLE, IDC_DEV_TOOLS_CONSOLE }, + { IDS_DEV_TOOLS_DEVICES, IDC_DEV_TOOLS_DEVICES }, { MENU_END, MENU_END } }; diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash_unittest.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_ash_unittest.cc index c09c9e1347..f24f95d724 100644 --- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash_unittest.cc +++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash_unittest.cc @@ -4,7 +4,7 @@ #include "chrome/browser/ui/views/frame/immersive_mode_controller_ash.h" -#include "ash/display/display_controller.h" +#include "ash/display/display_manager.h" #include "ash/shell.h" #include "ash/test/ash_test_base.h" #include "chrome/app/chrome_command_ids.h" @@ -357,7 +357,7 @@ TEST_F(ImmersiveModeControllerAshTest, MouseEventsVerticalDisplayLayout) { // Set up initial state. UpdateDisplay("800x600,800x600"); ash::DisplayLayout display_layout(ash::DisplayLayout::TOP, 0); - ash::Shell::GetInstance()->display_controller()->SetLayoutForCurrentDisplays( + ash::Shell::GetInstance()->display_manager()->SetLayoutForCurrentDisplays( display_layout); controller()->SetEnabled(true); diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc index fdfe4c7bb2..4de7694c3d 100644 --- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc +++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc @@ -12,6 +12,7 @@ #include "base/prefs/pref_service.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/profiles/profiles_state.h" #include "chrome/browser/themes/theme_properties.h" #include "chrome/browser/ui/views/avatar_label.h" #include "chrome/browser/ui/views/avatar_menu_button.h" @@ -19,6 +20,7 @@ #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h" #include "chrome/browser/ui/views/frame/opaque_browser_frame_view_platform_specific.h" +#include "chrome/browser/ui/views/new_avatar_button.h" #include "chrome/browser/ui/views/tab_icon_view.h" #include "chrome/browser/ui/views/tabs/tab_strip.h" #include "chrome/browser/ui/views/toolbar_view.h" @@ -150,7 +152,12 @@ OpaqueBrowserFrameView::OpaqueBrowserFrameView(BrowserFrame* frame, window_title_->set_id(VIEW_ID_WINDOW_TITLE); AddChildView(window_title_); - UpdateAvatarInfo(); + if (browser_view->IsRegularOrGuestSession() && + profiles::IsNewProfileManagementEnabled()) + UpdateNewStyleAvatarInfo(this, NewAvatarButton::THEMED_BUTTON); + else + UpdateAvatarInfo(); + if (!browser_view->IsOffTheRecord()) { registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, content::NotificationService::AllSources()); @@ -215,7 +222,9 @@ int OpaqueBrowserFrameView::NonClientHitTest(const gfx::Point& point) { // label. if ((avatar_button() && avatar_button()->GetMirroredBounds().Contains(point)) || - (avatar_label() && avatar_label()->GetMirroredBounds().Contains(point))) + (avatar_label() && avatar_label()->GetMirroredBounds().Contains(point)) || + (new_avatar_button() && + new_avatar_button()->GetMirroredBounds().Contains(point))) return HTCLIENT; int frame_component = frame()->client_view()->NonClientHitTest(point); @@ -396,6 +405,8 @@ void OpaqueBrowserFrameView::ButtonPressed(views::Button* sender, frame()->Restore(); else if (sender == close_button_) frame()->Close(); + else if (sender == new_avatar_button()) + ShowProfileChooserViewBubble(); } /////////////////////////////////////////////////////////////////////////////// @@ -427,7 +438,11 @@ void OpaqueBrowserFrameView::Observe( const content::NotificationDetails& details) { switch (type) { case chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED: - UpdateAvatarInfo(); + if (browser_view() ->IsRegularOrGuestSession() && + profiles::IsNewProfileManagementEnabled()) + UpdateNewStyleAvatarInfo(this, NewAvatarButton::THEMED_BUTTON); + else + UpdateAvatarInfo(); break; default: NOTREACHED() << "Got a notification we didn't register for!"; @@ -477,6 +492,10 @@ bool OpaqueBrowserFrameView::ShouldShowAvatar() const { return browser_view()->ShouldShowAvatar(); } +bool OpaqueBrowserFrameView::IsRegularOrGuestSession() const { + return browser_view()->IsRegularOrGuestSession(); +} + gfx::ImageSkia OpaqueBrowserFrameView::GetOTRAvatarIcon() const { return browser_view()->GetOTRAvatarIcon(); } diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.h b/chrome/browser/ui/views/frame/opaque_browser_frame_view.h index e732d019bb..1e9b018daf 100644 --- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.h +++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.h @@ -20,6 +20,7 @@ class BrowserView; class OpaqueBrowserFrameViewLayout; class OpaqueBrowserFrameViewPlatformSpecific; class TabIconView; +class NewAvatarButton; namespace views { class ImageButton; @@ -87,6 +88,7 @@ class OpaqueBrowserFrameView : public BrowserNonClientFrameView, virtual bool ShouldLeaveOffsetNearTopBorder() const OVERRIDE; virtual gfx::Size GetBrowserViewMinimumSize() const OVERRIDE; virtual bool ShouldShowAvatar() const OVERRIDE; + virtual bool IsRegularOrGuestSession() const OVERRIDE; virtual gfx::ImageSkia GetOTRAvatarIcon() const OVERRIDE; virtual bool IsMaximized() const OVERRIDE; virtual bool IsMinimized() const OVERRIDE; diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.cc index c0848d869f..071e3921fd 100644 --- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.cc +++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.cc @@ -4,6 +4,8 @@ #include "chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h" +#include "chrome/browser/profiles/profiles_state.h" +#include "chrome/browser/ui/views/new_avatar_button.h" #include "ui/gfx/font.h" #include "ui/views/controls/button/image_button.h" #include "ui/views/controls/label.h" @@ -49,6 +51,9 @@ const int kAvatarLeftSpacing = 2; // Space between the right edge of the avatar and the tabstrip. const int kAvatarRightSpacing = -4; +// How far the new avatar button is from the closest caption button. +const int kNewAvatarButtonOffset = 5; + // In restored mode, the New Tab button isn't at the same height as the caption // buttons, but the space will look cluttered if it actually slides under them, // so we stop it when the gap between the two is down to 5 px. @@ -101,7 +106,8 @@ OpaqueBrowserFrameViewLayout::OpaqueBrowserFrameViewLayout( window_icon_(NULL), window_title_(NULL), avatar_label_(NULL), - avatar_button_(NULL) { + avatar_button_(NULL), + new_avatar_button_(NULL) { trailing_buttons_.push_back(views::FRAME_BUTTON_MINIMIZE); trailing_buttons_.push_back(views::FRAME_BUTTON_MAXIMIZE); trailing_buttons_.push_back(views::FRAME_BUTTON_CLOSE); @@ -355,6 +361,24 @@ void OpaqueBrowserFrameViewLayout::LayoutTitleBar(views::View* host) { } } +void OpaqueBrowserFrameViewLayout::LayoutNewStyleAvatar(views::View* host) { + gfx::Size label_size = new_avatar_button_->GetPreferredSize(); + int button_size_with_offset = kNewAvatarButtonOffset + label_size.width(); + + int button_x = host->width() - trailing_button_start_ - + button_size_with_offset; + int button_y = CaptionButtonY(false); + + trailing_button_start_ += button_size_with_offset; + minimum_size_for_buttons_ += button_size_with_offset; + + new_avatar_button_->SetBounds( + button_x, + button_y, + label_size.width(), + button_y + kCaptionButtonHeightWithPadding); +} + void OpaqueBrowserFrameViewLayout::LayoutAvatar() { // Even though the avatar is used for both incognito and profiles we always // use the incognito icon to layout the avatar button. The profile icon @@ -567,6 +591,9 @@ void OpaqueBrowserFrameViewLayout::SetView(int id, views::View* view) { case VIEW_ID_AVATAR_BUTTON: avatar_button_ = view; break; + case VIEW_ID_NEW_AVATAR_BUTTON: + new_avatar_button_ = static_cast<NewAvatarButton*>(view); + break; default: NOTIMPLEMENTED() << "Unknown view id " << id; break; @@ -593,7 +620,11 @@ void OpaqueBrowserFrameViewLayout::Layout(views::View* host) { // on the trailing side. leading_button_start_++; - LayoutAvatar(); + if (delegate_->IsRegularOrGuestSession() && + profiles::IsNewProfileManagementEnabled()) + LayoutNewStyleAvatar(host); + else + LayoutAvatar(); client_view_bounds_ = CalculateClientAreaBounds( host->width(), host->height()); diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h index 12f6dea270..b37bd51436 100644 --- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h +++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h @@ -9,6 +9,7 @@ #include "ui/views/layout/layout_manager.h" #include "ui/views/window/frame_buttons.h" +class NewAvatarButton; class OpaqueBrowserFrameViewLayoutDelegate; namespace views { @@ -100,6 +101,7 @@ class OpaqueBrowserFrameViewLayout : public views::LayoutManager { void LayoutWindowControls(views::View* host); void LayoutTitleBar(views::View* host); void LayoutAvatar(); + void LayoutNewStyleAvatar(views::View* host); void ConfigureButton(views::View* host, views::FrameButton button_id, @@ -167,6 +169,7 @@ class OpaqueBrowserFrameViewLayout : public views::LayoutManager { views::View* avatar_label_; views::View* avatar_button_; + NewAvatarButton* new_avatar_button_; std::vector<views::FrameButton> leading_buttons_; std::vector<views::FrameButton> trailing_buttons_; diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_delegate.h b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_delegate.h index 7858ed1f68..58709c8cbd 100644 --- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_delegate.h +++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_delegate.h @@ -35,6 +35,9 @@ class OpaqueBrowserFrameViewLayoutDelegate { // Controls the visualization of the avatar virtual bool ShouldShowAvatar() const = 0; + // Returns true if in guest mode or a non off the record session. + virtual bool IsRegularOrGuestSession() const = 0; + // We don't have a ThemeProvider in the layout manager, so plumb in the icon // source here. virtual gfx::ImageSkia GetOTRAvatarIcon() const = 0; diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_unittest.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_unittest.cc index 5962fb0328..c75f09a1b0 100644 --- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_unittest.cc +++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_unittest.cc @@ -5,9 +5,11 @@ #include "chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h" #include "base/basictypes.h" +#include "base/command_line.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/ui/views/tab_icon_view.h" #include "chrome/browser/ui/views/tabs/tab.h" +#include "chrome/common/chrome_switches.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_skia_rep.h" #include "ui/gfx/text_constants.h" @@ -80,6 +82,10 @@ class TestLayoutDelegate : public OpaqueBrowserFrameViewLayoutDelegate { return show_avatar_; } + virtual bool IsRegularOrGuestSession() const OVERRIDE { + return true; + } + virtual gfx::ImageSkia GetOTRAvatarIcon() const OVERRIDE { // The calculations depend on the size of the OTR resource, and chromeos // uses a different sized image, so hard code the size of the current @@ -213,6 +219,12 @@ class OpaqueBrowserFrameViewLayoutTest : public views::ViewsTestBase { AddAvatarButton(); } + void AddNewAvatarButton() { + new_avatar_button_ = new views::MenuButton(NULL, string16(), NULL, false); + new_avatar_button_->set_id(VIEW_ID_NEW_AVATAR_BUTTON); + root_view_->AddChildView(new_avatar_button_); + } + void ExpectBasicWindowBounds() { EXPECT_EQ("428,1 25x18", maximize_button_->bounds().ToString()); EXPECT_EQ("402,1 26x18", minimize_button_->bounds().ToString()); @@ -238,6 +250,7 @@ class OpaqueBrowserFrameViewLayoutTest : public views::ViewsTestBase { views::MenuButton* menu_button_; views::MenuButton* avatar_label_; + views::MenuButton* new_avatar_button_; DISALLOW_COPY_AND_ASSIGN(OpaqueBrowserFrameViewLayoutTest); }; @@ -314,6 +327,28 @@ TEST_F(OpaqueBrowserFrameViewLayoutTest, WindowWithAvatar) { EXPECT_EQ("261x73", layout_manager_->GetMinimumSize(kWidth).ToString()); } + +TEST_F(OpaqueBrowserFrameViewLayoutTest, WindowWithNewAvatar) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kNewProfileManagement); + + // Tests a normal tabstrip window with the new style avatar icon. + AddNewAvatarButton(); + root_view_->Layout(); + + ExpectBasicWindowBounds(); + + // Check the location of the caption button + EXPECT_EQ("385,1 12x20", new_avatar_button_->bounds().ToString()); + // The basic window bounds are (-1, 13 398x29). There should not be an icon + // avatar in the left, and the new avatar button has an offset of 5 to its + // next control. + EXPECT_EQ("-1,13 381x29", + layout_manager_->GetBoundsForTabStrip( + delegate_->GetTabstripPreferredSize(), kWidth).ToString()); + EXPECT_EQ("261x73", layout_manager_->GetMinimumSize(kWidth).ToString()); +} + TEST_F(OpaqueBrowserFrameViewLayoutTest, WindowWithAvatarLabelAndButton) { AddAvatarLabel(); root_view_->Layout(); diff --git a/chrome/browser/ui/views/frame/system_menu_model_builder.cc b/chrome/browser/ui/views/frame/system_menu_model_builder.cc index f07118174c..f6ade584e4 100644 --- a/chrome/browser/ui/views/frame/system_menu_model_builder.cc +++ b/chrome/browser/ui/views/frame/system_menu_model_builder.cc @@ -15,6 +15,14 @@ #include "ui/base/accelerators/accelerator.h" #include "ui/base/models/simple_menu_model.h" +#if defined(OS_CHROMEOS) +#include "ash/session_state_delegate.h" +#include "ash/shell.h" +#include "chrome/browser/ui/ash/multi_user_window_manager.h" +#include "chrome/browser/ui/browser_window.h" +#include "ui/base/l10n/l10n_util.h" +#endif + SystemMenuModelBuilder::SystemMenuModelBuilder( ui::AcceleratorProvider* provider, Browser* browser) @@ -55,6 +63,7 @@ void SystemMenuModelBuilder::BuildSystemMenuForBrowserWindow( model->AddSeparator(ui::NORMAL_SEPARATOR); model->AddItemWithStringId(IDC_TASK_MANAGER, IDS_TASK_MANAGER); } + AppendTeleportMenu(model); // If it's a regular browser window with tabs, we don't add any more items, // since it already has menus (Page, Chrome). } @@ -87,6 +96,8 @@ void SystemMenuModelBuilder::BuildSystemMenuForAppOrPopupWindow( model->AddSeparator(ui::NORMAL_SEPARATOR); model->AddItemWithStringId(IDC_TASK_MANAGER, IDS_TASK_MANAGER); } + + AppendTeleportMenu(model); } void SystemMenuModelBuilder::AddFrameToggleItems(ui::SimpleMenuModel* model) { @@ -97,3 +108,38 @@ void SystemMenuModelBuilder::AddFrameToggleItems(ui::SimpleMenuModel* model) { } } +void SystemMenuModelBuilder::AppendTeleportMenu(ui::SimpleMenuModel* model) { +#if defined(OS_CHROMEOS) + DCHECK(browser()->window()); + chrome::MultiUserWindowManager* manager = + chrome::MultiUserWindowManager::GetInstance(); + // If there is no manager, we are not in the proper multi user mode. + if (!manager) + return; + + // To show the menu we need at least two logged in users. + ash::SessionStateDelegate* delegate = + ash::Shell::GetInstance()->session_state_delegate(); + int logged_in_users = delegate->NumberOfLoggedInUsers(); + if (logged_in_users <= 1) + return; + + // If this does not belong to a profile or there is no window, or the window + // is not owned by anyone, we don't show the menu addition. + const std::string user_id = + manager->GetUserIDFromProfile(browser()->profile()); + aura::Window* window = browser()->window()->GetNativeWindow(); + if (user_id.empty() || !window || manager->GetWindowOwner(window).empty()) + return; + + model->AddSeparator(ui::NORMAL_SEPARATOR); + DCHECK(logged_in_users <= 3); + for (int user_index = 1; user_index < logged_in_users; ++user_index) { + model->AddItem( + user_index == 1 ? IDC_VISIT_DESKTOP_OF_LRU_USER_2 : + IDC_VISIT_DESKTOP_OF_LRU_USER_3, + l10n_util::GetStringFUTF16(IDC_VISIT_DESKTOP_OF_LRU_USER, + delegate->GetUserDisplayName(user_index))); + } +#endif +} diff --git a/chrome/browser/ui/views/frame/system_menu_model_builder.h b/chrome/browser/ui/views/frame/system_menu_model_builder.h index 280d5de44e..d12dc336cc 100644 --- a/chrome/browser/ui/views/frame/system_menu_model_builder.h +++ b/chrome/browser/ui/views/frame/system_menu_model_builder.h @@ -42,6 +42,9 @@ class SystemMenuModelBuilder { // Adds items for toggling the frame type (if necessary). void AddFrameToggleItems(ui::SimpleMenuModel* model); + // Add the items to allow the window to visit the desktop of another user. + void AppendTeleportMenu(ui::SimpleMenuModel* model); + SystemMenuModelDelegate menu_delegate_; scoped_ptr<ui::MenuModel> menu_model_; scoped_ptr<ZoomMenuModel> zoom_menu_contents_; diff --git a/chrome/browser/ui/views/importer/import_lock_dialog_view.cc b/chrome/browser/ui/views/importer/import_lock_dialog_view.cc index b1e41ba6d0..dcc97208e4 100644 --- a/chrome/browser/ui/views/importer/import_lock_dialog_view.cc +++ b/chrome/browser/ui/views/importer/import_lock_dialog_view.cc @@ -19,10 +19,6 @@ using content::UserMetricsAction; -// Default size of the dialog window. -static const int kDefaultWindowWidth = 320; -static const int kDefaultWindowHeight = 100; - namespace importer { void ShowImportLockDialog(gfx::NativeWindow parent, diff --git a/chrome/browser/ui/views/login_prompt_views.cc b/chrome/browser/ui/views/login_prompt_views.cc index 7994e7a96a..42b99e431d 100644 --- a/chrome/browser/ui/views/login_prompt_views.cc +++ b/chrome/browser/ui/views/login_prompt_views.cc @@ -157,7 +157,7 @@ class LoginHandlerViews : public LoginHandler, WebContentsModalDialogManager::FromWebContents(requesting_contents); WebContentsModalDialogManagerDelegate* modal_delegate = web_contents_modal_dialog_manager->delegate(); - DCHECK(modal_delegate); + CHECK(modal_delegate); dialog_ = views::Widget::CreateWindowAsFramelessChild( this, requesting_contents->GetView()->GetNativeView(), diff --git a/chrome/browser/ui/views/message_center/message_center_frame_view.cc b/chrome/browser/ui/views/message_center/message_center_frame_view.cc index 0efd863462..095a3b85de 100644 --- a/chrome/browser/ui/views/message_center/message_center_frame_view.cc +++ b/chrome/browser/ui/views/message_center/message_center_frame_view.cc @@ -9,20 +9,15 @@ #include "ui/views/shadow_border.h" #include "ui/views/widget/widget.h" -namespace { - -const int kBorderWidth = 1; -const int kShadowBlur = 8; - -} // namepspace - namespace message_center { MessageCenterFrameView::MessageCenterFrameView() { #if defined(OS_LINUX) && !defined(OS_CHROMEOS) + const int kBorderWidth = 1; set_border(views::Border::CreateSolidBorder( kBorderWidth, message_center::kMessageCenterBorderColor)); #else + const int kShadowBlur = 8; set_border(new views::ShadowBorder(kShadowBlur, message_center::kMessageCenterShadowColor, 0, // Vertical offset diff --git a/chrome/browser/ui/views/message_center/web_notification_tray.cc b/chrome/browser/ui/views/message_center/web_notification_tray.cc index f58482a058..6c6675f6d4 100644 --- a/chrome/browser/ui/views/message_center/web_notification_tray.cc +++ b/chrome/browser/ui/views/message_center/web_notification_tray.cc @@ -32,10 +32,6 @@ namespace { // Tray constants const int kScreenEdgePadding = 2; -const int kSystemTrayWidth = 16; -const int kSystemTrayHeight = 16; -const int kNumberOfSystemTraySprites = 10; - // Number of pixels the message center is offset from the mouse. const int kMouseOffset = 5; diff --git a/chrome/browser/ui/views/new_avatar_button.cc b/chrome/browser/ui/views/new_avatar_button.cc new file mode 100644 index 0000000000..049afb3902 --- /dev/null +++ b/chrome/browser/ui/views/new_avatar_button.cc @@ -0,0 +1,128 @@ +// Copyright 2013 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/ui/views/new_avatar_button.h" + +#include "base/win/windows_version.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/profiles/profiles_state.h" +#include "grit/generated_resources.h" +#include "grit/theme_resources.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/color_utils.h" +#include "ui/views/border.h" +#include "ui/views/painter.h" + +namespace { + +// Text padding within the button border. +const int kInset = 10; + +views::TextButtonDefaultBorder* CreateBorder(const int normal_image_set[], + const int hot_image_set[], + const int pushed_image_set[]) { + views::TextButtonDefaultBorder* border = new views::TextButtonDefaultBorder(); + + border->SetInsets(gfx::Insets(kInset, kInset, kInset, kInset)); + border->set_normal_painter( + views::Painter::CreateImageGridPainter(normal_image_set)); + border->set_hot_painter( + views::Painter::CreateImageGridPainter(hot_image_set)); + border->set_pushed_painter( + views::Painter::CreateImageGridPainter(pushed_image_set)); + + return border; +} + +} // namespace + +NewAvatarButton::NewAvatarButton( + views::ButtonListener* listener, + const string16& profile_name, + AvatarButtonStyle button_style, + Browser* browser) + : MenuButton(listener, profile_name, NULL, true), + browser_(browser) { + set_animate_on_state_change(false); + + ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); + SetFont(rb->GetFont(ui::ResourceBundle::BaseFont)); + + bool is_win8 = false; +#if defined(OS_WIN) + is_win8 = base::win::GetVersion() >= base::win::VERSION_WIN8; +#endif + + if (button_style == THEMED_BUTTON) { + const int kNormalImageSet[] = IMAGE_GRID(IDR_AVATAR_THEMED_BUTTON_NORMAL); + const int kHotImageSet[] = IMAGE_GRID(IDR_AVATAR_THEMED_BUTTON_HOVER); + const int kPushedImageSet[] = IMAGE_GRID(IDR_AVATAR_THEMED_BUTTON_PRESSED); + + set_border(CreateBorder(kNormalImageSet, kHotImageSet, kPushedImageSet)); + set_menu_marker( + rb->GetImageNamed(IDR_AVATAR_THEMED_BUTTON_DROPARROW).ToImageSkia()); + } else if (is_win8) { + const int kNormalImageSet[] = IMAGE_GRID(IDR_AVATAR_METRO_BUTTON_NORMAL); + const int kHotImageSet[] = IMAGE_GRID(IDR_AVATAR_METRO_BUTTON_HOVER); + const int kPushedImageSet[] = IMAGE_GRID(IDR_AVATAR_METRO_BUTTON_PRESSED); + + set_border(CreateBorder(kNormalImageSet, kHotImageSet, kPushedImageSet)); + set_menu_marker( + rb->GetImageNamed(IDR_AVATAR_METRO_BUTTON_DROPARROW).ToImageSkia()); + } else { + const int kNormalImageSet[] = IMAGE_GRID(IDR_AVATAR_GLASS_BUTTON_NORMAL); + const int kHotImageSet[] = IMAGE_GRID(IDR_AVATAR_GLASS_BUTTON_HOVER); + const int kPushedImageSet[] = IMAGE_GRID(IDR_AVATAR_GLASS_BUTTON_PRESSED); + + set_border(CreateBorder(kNormalImageSet, kHotImageSet, kPushedImageSet)); + set_menu_marker( + rb->GetImageNamed(IDR_AVATAR_GLASS_BUTTON_DROPARROW).ToImageSkia()); + } + + avatar_menu_.reset(new AvatarMenu( + &g_browser_process->profile_manager()->GetProfileInfoCache(), + this, + browser_)); + avatar_menu_->RebuildMenu(); + + SchedulePaint(); +} + +NewAvatarButton::~NewAvatarButton() { +} + +void NewAvatarButton::OnPaint(gfx::Canvas* canvas) { + // From TextButton::PaintButton, draw everything but the text. + OnPaintBackground(canvas); + OnPaintBorder(canvas); + OnPaintFocusBorder(canvas); + + gfx::Rect rect; + // In RTL languages the marker gets drawn leftmost, so account for its offset. + if (base::i18n::IsRTL()) + rect = gfx::Rect(-kInset, 0, size().width(), size().height()); + else + rect = gfx::Rect(kInset, 0, size().width(), size().height()); + // TODO(noms): This should be DrawStringRectWithHalo but that function + // has a bug at the moment and incorrectly draws the background. + canvas->DrawStringRectWithFlags( + text(), + gfx::FontList(ui::ResourceBundle::GetSharedInstance().GetFont( + ui::ResourceBundle::BaseFont)), + SK_ColorBLACK, + rect, + gfx::Canvas::NO_SUBPIXEL_RENDERING); + + // From MenuButton::PaintButton, paint the marker + PaintMenuMarker(canvas); +} + +void NewAvatarButton::OnAvatarMenuChanged(AvatarMenu* avatar_menu) { + SetText(profiles::GetActiveProfileDisplayName(browser_)); + // We need to redraw the entire button because the width might have changed. + SchedulePaint(); +} diff --git a/chrome/browser/ui/views/new_avatar_button.h b/chrome/browser/ui/views/new_avatar_button.h new file mode 100644 index 0000000000..076d822f94 --- /dev/null +++ b/chrome/browser/ui/views/new_avatar_button.h @@ -0,0 +1,44 @@ +// Copyright 2013 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_UI_VIEWS_NEW_AVATAR_BUTTON_H_ +#define CHROME_BROWSER_UI_VIEWS_NEW_AVATAR_BUTTON_H_ + +#include "chrome/browser/profiles/avatar_menu.h" +#include "chrome/browser/profiles/avatar_menu_observer.h" +#include "ui/views/controls/button/menu_button.h" + +// Avatar button that displays the active profile's name in the caption area. +class NewAvatarButton : public views::MenuButton, + public AvatarMenuObserver { + public: + // Different button styles that can be applied. + enum AvatarButtonStyle { + THEMED_BUTTON, // Used in a themed browser window. + NATIVE_BUTTON, // Used in a native aero or metro window. + }; + + NewAvatarButton(views::ButtonListener* listener, + const string16& profile_name, + AvatarButtonStyle button_style, + Browser* browser); + virtual ~NewAvatarButton(); + + // views::View: + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; + + private: + friend class NewAvatarMenuButtonTest; + FRIEND_TEST_ALL_PREFIXES(NewAvatarMenuButtonTest, SignOut); + + // AvatarMenuObserver: + virtual void OnAvatarMenuChanged(AvatarMenu* avatar_menu) OVERRIDE; + + scoped_ptr<AvatarMenu> avatar_menu_; + Browser* browser_; + + DISALLOW_COPY_AND_ASSIGN(NewAvatarButton); +}; + +#endif // CHROME_BROWSER_UI_VIEWS_NEW_AVATAR_BUTTON_H_ diff --git a/chrome/browser/ui/views/new_avatar_menu_button_browsertest.cc b/chrome/browser/ui/views/new_avatar_menu_button_browsertest.cc new file mode 100644 index 0000000000..c4b21df85e --- /dev/null +++ b/chrome/browser/ui/views/new_avatar_menu_button_browsertest.cc @@ -0,0 +1,123 @@ +// Copyright 2013 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/command_line.h" +#include "base/path_service.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/profiles/profiles_state.h" +#include "chrome/browser/ui/browser_list.h" +#include "chrome/browser/ui/views/avatar_menu_button.h" +#include "chrome/browser/ui/views/frame/browser_view.h" +#include "chrome/browser/ui/views/new_avatar_button.h" +#include "chrome/browser/ui/views/profile_chooser_view.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/test_switches.h" +#include "chrome/test/base/testing_browser_process.h" +#include "content/public/test/test_utils.h" +#include "grit/generated_resources.h" + +class NewAvatarMenuButtonTest : public InProcessBrowserTest { + public: + NewAvatarMenuButtonTest(); + virtual ~NewAvatarMenuButtonTest(); + + protected: + virtual void SetUp() OVERRIDE; + virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE; + void CreateTestingProfile(); + void StartAvatarMenu(); + + private: + DISALLOW_COPY_AND_ASSIGN(NewAvatarMenuButtonTest); +}; + +NewAvatarMenuButtonTest::NewAvatarMenuButtonTest() { +} + +NewAvatarMenuButtonTest::~NewAvatarMenuButtonTest() { +} + +void NewAvatarMenuButtonTest::SetUp() { + InProcessBrowserTest::SetUp(); + DCHECK(CommandLine::ForCurrentProcess()->HasSwitch( + switches::kNewProfileManagement)); +} + +void NewAvatarMenuButtonTest::SetUpCommandLine(CommandLine* command_line) { + command_line->AppendSwitch(switches::kNewProfileManagement); +} + +void NewAvatarMenuButtonTest::CreateTestingProfile() { + ProfileManager* profile_manager = g_browser_process->profile_manager(); + EXPECT_EQ(1u, profile_manager->GetNumberOfProfiles()); + + // Sign in the default profile + ProfileInfoCache& cache = profile_manager->GetProfileInfoCache(); + cache.SetUserNameOfProfileAtIndex(0, UTF8ToUTF16("user_name")); + + base::FilePath path; + PathService::Get(chrome::DIR_USER_DATA, &path); + path = path.AppendASCII("test_profile"); + if (!base::PathExists(path)) + ASSERT_TRUE(file_util::CreateDirectory(path)); + Profile* profile = + Profile::CreateProfile(path, NULL, Profile::CREATE_MODE_SYNCHRONOUS); + profile_manager->RegisterTestingProfile(profile, true, false); + EXPECT_EQ(2u, profile_manager->GetNumberOfProfiles()); +} + +void NewAvatarMenuButtonTest::StartAvatarMenu() { + BrowserView* browser_view = reinterpret_cast<BrowserView*>( + browser()->window()); + + // Ensure that the avatar icon button is not also showing. + NewAvatarButton* button = browser_view->frame()->GetNewAvatarMenuButton(); + ASSERT_TRUE(button); + ASSERT_FALSE(browser_view->frame()->GetAvatarMenuButton()); + + ProfileChooserView::set_close_on_deactivate(false); + ui::MouseEvent mouse_ev(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(), 0); + button->NotifyClick(mouse_ev); + base::MessageLoop::current()->RunUntilIdle(); + EXPECT_TRUE(ProfileChooserView::IsShowing()); +} + +IN_PROC_BROWSER_TEST_F(NewAvatarMenuButtonTest, SignOut) { + // If multiprofile mode is not enabled, you can't switch between profiles. + if (!profiles::IsMultipleProfilesEnabled()) + return; + + CreateTestingProfile(); + ASSERT_NO_FATAL_FAILURE(StartAvatarMenu()); + + BrowserList* browser_list = + BrowserList::GetInstance(chrome::GetActiveDesktop()); + EXPECT_EQ(1U, browser_list->size()); + content::WindowedNotificationObserver window_close_observer( + chrome::NOTIFICATION_BROWSER_CLOSED, + content::Source<Browser>(browser())); + + AvatarMenu* menu = + ProfileChooserView::profile_bubble_->avatar_menu_.get(); + const AvatarMenu::Item& menu_item_before = + menu->GetItemAt(menu->GetActiveProfileIndex()); + EXPECT_FALSE(menu_item_before.signin_required); + + ui::MouseEvent mouse_ev(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(), 0); + menu->SetLogoutURL("about:blank"); + + ProfileChooserView::profile_bubble_->LinkClicked( + static_cast<views::Link*>( + ProfileChooserView::profile_bubble_->signout_current_profile_link_), + 0); + + EXPECT_TRUE(menu->GetItemAt(menu->GetActiveProfileIndex()).signin_required); + + window_close_observer.Wait(); // Rely on test timeout for failure indication. + EXPECT_TRUE(browser_list->empty()); +} diff --git a/chrome/browser/ui/views/notifications/balloon_view_views.cc b/chrome/browser/ui/views/notifications/balloon_view_views.cc index 2680f78eb3..a288d01040 100644 --- a/chrome/browser/ui/views/notifications/balloon_view_views.cc +++ b/chrome/browser/ui/views/notifications/balloon_view_views.cc @@ -74,9 +74,6 @@ const int kBottomShadowWidth = 6; // Optional animation. const bool kAnimateEnabled = true; -// Menu commands -const int kRevokePermissionCommand = 0; - // Colors const SkColor kControlBarBackgroundColor = SkColorSetRGB(245, 245, 245); const SkColor kControlBarTextColor = SkColorSetRGB(125, 125, 125); diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc index 12193bb594..d52726fd99 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc +++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc @@ -23,20 +23,10 @@ #include "ui/views/corewm/window_animations.h" #endif -namespace { - -// This is the number of pixels in the border image used to draw the bottom -// border + drop shadow interior to the "visual" border. We lay out assuming -// that this many pixels inside the border is "in the popup." -const SkAlpha kGlassPopupAlpha = 240; -const SkAlpha kOpaquePopupAlpha = 255; - // This is the number of pixels in the border image interior to the actual // border. const int kBorderInterior = 6; -} // namespace - class OmniboxPopupContentsView::AutocompletePopupWidget : public views::Widget, public base::SupportsWeakPtr<AutocompletePopupWidget> { diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc index 1248754004..b87a6af911 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc +++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc @@ -55,7 +55,7 @@ #endif #if defined(USE_AURA) -#include "ui/aura/focus_manager.h" +#include "ui/aura/client/focus_client.h" #include "ui/aura/root_window.h" #include "ui/compositor/layer.h" #endif diff --git a/chrome/browser/ui/views/panels/panel_frame_view.cc b/chrome/browser/ui/views/panels/panel_frame_view.cc index 7b9ed293bb..4b81d3db4d 100644 --- a/chrome/browser/ui/views/panels/panel_frame_view.cc +++ b/chrome/browser/ui/views/panels/panel_frame_view.cc @@ -58,7 +58,6 @@ const SkColor kAttentionBackgroundDefaultColor = // Color used to draw the minimized panel. const SkColor kMinimizeBackgroundDefaultColor = SkColorSetRGB(0xf5, 0xf4, 0xf0); -const SkColor kMinimizeBorderDefaultColor = SkColorSetRGB(0xc9, 0xc9, 0xc9); // Color used to draw the title text under default theme. const SkColor kTitleTextDefaultColor = SkColorSetRGB(0xf9, 0xf9, 0xf9); diff --git a/chrome/browser/ui/views/panels/panel_view.cc b/chrome/browser/ui/views/panels/panel_view.cc index aef7111c7e..97aa507eeb 100644 --- a/chrome/browser/ui/views/panels/panel_view.cc +++ b/chrome/browser/ui/views/panels/panel_view.cc @@ -38,10 +38,12 @@ namespace { +#if defined(OS_WIN) // If the height of a stacked panel shrinks below this threshold during the // user resizing, it will be treated as minimized. const int kStackedPanelHeightShrinkThresholdToBecomeMinimized = panel::kTitlebarHeight + 20; +#endif // Supported accelerators. // Note: We can't use the acclerator table defined in chrome/browser/ui/views diff --git a/chrome/browser/ui/views/profile_chooser_view.cc b/chrome/browser/ui/views/profile_chooser_view.cc index 8518378724..3c53784f51 100644 --- a/chrome/browser/ui/views/profile_chooser_view.cc +++ b/chrome/browser/ui/views/profile_chooser_view.cc @@ -48,6 +48,7 @@ const int kLargeImageSide = 64; const int kSmallImageSide = 32; const int kMinMenuWidth = 250; const int kButtonHeight = 29; +const int kArrowHeight = 10; // Current profile avatar image. views::View* CreateProfileImageView(const gfx::Image& icon) { @@ -55,8 +56,8 @@ views::View* CreateProfileImageView(const gfx::Image& icon) { gfx::Image image = profiles::GetSizedAvatarIconWithBorder( icon, true, - kLargeImageSide + profiles::kAvatarIconBorder, - kLargeImageSide + profiles::kAvatarIconBorder); + kLargeImageSide + profiles::kAvatarIconPadding, + kLargeImageSide + profiles::kAvatarIconPadding); view->SetImage(image.ToImageSkia()); return view; @@ -199,6 +200,7 @@ void ProfileChooserView::ShowBubble( profile_bubble_->set_close_on_deactivate(close_on_deactivate_); profile_bubble_->SetAlignment(border_alignment); profile_bubble_->GetWidget()->Show(); + profile_bubble_->SetArrowPaintType(views::BubbleBorder::PAINT_NONE); } // static @@ -220,6 +222,8 @@ ProfileChooserView::ProfileChooserView(views::View* anchor_view, browser_(browser) { // Reset the default margins inherited from the BubbleDelegateView. set_margins(gfx::Insets()); + // Compensate for built-in vertical padding in the anchor view's image. + set_anchor_view_insets(gfx::Insets(kArrowHeight, 0, kArrowHeight, 0)); ResetLinksAndButtons(); @@ -274,12 +278,18 @@ void ProfileChooserView::ShowView(BubbleViewMode view_to_display, layout->set_minimum_size(gfx::Size(kMinMenuWidth, 0)); if (view_to_display == GAIA_SIGNIN_VIEW) { + const int kMinGaiaViewWidth = 280; + const int kMinGaiaViewHeight = 300; Profile* profile = browser_->profile(); views::WebView* web_view = new views::WebView(profile); web_view->LoadInitialURL(GURL(chrome::kChromeUIInlineLoginURL)); layout->StartRow(1, 0); layout->AddView(web_view); + layout->set_minimum_size( + gfx::Size(kMinGaiaViewWidth, kMinGaiaViewHeight)); Layout(); + if (GetBubbleFrameView()) + SizeToContents(); return; } @@ -329,6 +339,8 @@ void ProfileChooserView::ShowView(BubbleViewMode view_to_display, layout->AddView(option_buttons_view); Layout(); + if (GetBubbleFrameView()) + SizeToContents(); } void ProfileChooserView::WindowClosing() { @@ -367,7 +379,6 @@ void ProfileChooserView::LinkClicked(views::Link* sender, int event_flags) { if (sender == manage_accounts_link_) { // ShowView() will DCHECK if this view is displayed for non signed-in users. ShowView(ACCOUNT_MANAGEMENT_VIEW, avatar_menu_.get()); - SizeToContents(); // The account list changes the height of the bubble. } else if (sender == signout_current_profile_link_) { avatar_menu_->BeginSignOut(); } else if (sender == signin_current_profile_link_) { @@ -505,8 +516,8 @@ views::View* ProfileChooserView::CreateOtherProfilesView( gfx::Image image = profiles::GetSizedAvatarIconWithBorder( item.icon, true, - kSmallImageSide + profiles::kAvatarIconBorder, - kSmallImageSide + profiles::kAvatarIconBorder); + kSmallImageSide + profiles::kAvatarIconPadding, + kSmallImageSide + profiles::kAvatarIconPadding); views::TextButton* button = new views::TextButton(this, item.name); open_other_profile_indexes_map_[button] = index; diff --git a/chrome/browser/ui/views/profile_chooser_view.h b/chrome/browser/ui/views/profile_chooser_view.h index eec7f2edcb..8affea1ae6 100644 --- a/chrome/browser/ui/views/profile_chooser_view.h +++ b/chrome/browser/ui/views/profile_chooser_view.h @@ -55,9 +55,8 @@ class ProfileChooserView : public views::BubbleDelegateView, } private: - friend class AvatarMenuButtonTest; - FRIEND_TEST_ALL_PREFIXES(AvatarMenuButtonTest, NewSignOut); - FRIEND_TEST_ALL_PREFIXES(AvatarMenuButtonTest, LaunchUserManagerScreen); + friend class NewAvatarMenuButtonTest; + FRIEND_TEST_ALL_PREFIXES(NewAvatarMenuButtonTest, SignOut); typedef std::vector<size_t> Indexes; typedef std::map<views::Button*, int> ButtonIndexes; diff --git a/chrome/browser/ui/views/speech_recognition_bubble_views.cc b/chrome/browser/ui/views/speech_recognition_bubble_views.cc index 2434c1d06f..71b3919327 100644 --- a/chrome/browser/ui/views/speech_recognition_bubble_views.cc +++ b/chrome/browser/ui/views/speech_recognition_bubble_views.cc @@ -35,8 +35,6 @@ namespace { const int kBubbleHorizMargin = 6; const int kBubbleVertMargin = 4; const int kBubbleHeadingVertMargin = 6; -const int kIconHorizontalOffset = 27; -const int kIconVerticalOffset = -7; // This is the SpeechRecognitionBubble content and views bubble delegate. class SpeechRecognitionBubbleView : public views::BubbleDelegateView, diff --git a/chrome/browser/ui/views/stubs_aura.cc b/chrome/browser/ui/views/stubs_aura.cc index 5a77b51ead..bb90ffde01 100644 --- a/chrome/browser/ui/views/stubs_aura.cc +++ b/chrome/browser/ui/views/stubs_aura.cc @@ -2,34 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/callback.h" #include "base/logging.h" #include "chrome/browser/external_protocol/external_protocol_handler.h" -#include "ui/gfx/native_widget_types.h" - -#if !defined(OS_WIN) -#include "chrome/browser/ui/certificate_dialogs.h" -#endif - -#if defined(USE_NSS) -#include "chrome/browser/ui/crypto_module_password_dialog.h" -#endif - -class SSLClientAuthHandler; - -namespace content { -class WebContents; -} - -namespace net { -class HttpNetworkSession; -class SSLCertRequestInfo; -class X509Certificate; -} - -namespace views { -class Widget; -} +#include "chrome/browser/ui/browser_dialogs.h" namespace chrome { @@ -43,6 +18,8 @@ void ShowAboutIPCDialog() { #if !defined(OS_CHROMEOS) && !defined(OS_WIN) // static void ExternalProtocolHandler::RunExternalProtocolDialog( - const GURL& url, int render_process_host_id, int routing_id) { + const GURL& url, + int render_process_host_id, + int routing_id) { } #endif diff --git a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc index 1c21c890de..804be07503 100644 --- a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc +++ b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc @@ -388,7 +388,7 @@ void BrowserTabStripController::TabInsertedAt(WebContents* contents, int model_index, bool is_active) { DCHECK(contents); - CHECK(model_->ContainsIndex(model_index)); + DCHECK(model_->ContainsIndex(model_index)); AddTab(contents, model_index, is_active); } diff --git a/chrome/browser/ui/views/tabs/dragged_tab_view.cc b/chrome/browser/ui/views/tabs/dragged_tab_view.cc index d7b8cf81ca..b62e1cf304 100644 --- a/chrome/browser/ui/views/tabs/dragged_tab_view.cc +++ b/chrome/browser/ui/views/tabs/dragged_tab_view.cc @@ -18,7 +18,6 @@ #endif static const int kTransparentAlpha = 200; -static const int kOpaqueAlpha = 255; static const int kDragFrameBorderSize = 2; static const int kTwiceDragFrameBorderSize = 2 * kDragFrameBorderSize; static const float kScalingFactor = 0.5; diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/tab_drag_controller.cc index 6f13d81163..ba0564b2e5 100644 --- a/chrome/browser/ui/views/tabs/tab_drag_controller.cc +++ b/chrome/browser/ui/views/tabs/tab_drag_controller.cc @@ -59,6 +59,11 @@ #include "ui/events/gestures/gesture_recognizer.h" #endif +#if defined(OS_WIN) && defined(USE_AURA) +#include "ui/aura/window.h" +#include "ui/events/gestures/gesture_recognizer.h" +#endif + using content::OpenURLParams; using content::UserMetricsAction; using content::WebContents; @@ -897,6 +902,18 @@ TabDragController::DragBrowserToNewTabStrip( target_tabstrip->GetWidget()->SetCapture(attached_tabstrip_); else browser_widget->ReleaseCapture(); +#if defined(OS_WIN) && defined(USE_AURA) + // The Gesture recognizer does not work well currently when capture changes + // while a touch gesture is in progress. So we need to manually transfer + // gesture sequence and the GR's touch events queue to the new window. This + // should really be done somewhere in capture change code and or inside the + // GR. But we currently do not have a consistent way for doing it that would + // work in all cases. Hence this hack. + ui::GestureRecognizer::Get()->TransferEventsTo( + browser_widget->GetNativeView(), + target_tabstrip->GetWidget()->GetNativeView()); +#endif + // The window is going away. Since the drag is still on going we don't want // that to effect the position of any windows. SetWindowPositionManaged(browser_widget->GetNativeView(), false); @@ -2227,9 +2244,8 @@ gfx::Point TabDragController::GetCursorScreenPoint() { aura::Window* widget_window = widget->GetNativeWindow(); DCHECK(widget_window->GetRootWindow()); gfx::Point touch_point; - bool got_touch_point = widget_window->GetRootWindow()-> - gesture_recognizer()->GetLastTouchPointForTarget(widget_window, - &touch_point); + bool got_touch_point = ui::GestureRecognizer::Get()-> + GetLastTouchPointForTarget(widget_window, &touch_point); DCHECK(got_touch_point); ash::wm::ConvertPointToScreen(widget_window->GetRootWindow(), &touch_point); return touch_point; diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc index bd7183a62b..54b91d531d 100644 --- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc +++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc @@ -875,8 +875,14 @@ void DragAllStep2(DetachToBrowserTabDragControllerTest* test, } // namespace +// Flaky on Windows Aura. crbug.com/309054 +#if defined(OS_WIN) && defined(USE_AURA) +#define MAYBE_DragAll DISABLED_DragAll +#else +#define MAYBE_DragAll DragAll +#endif // Selects multiple tabs and starts dragging the window. -IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest, DragAll) { +IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest, MAYBE_DragAll) { // Add another tab. AddTabAndResetBrowser(browser()); TabStrip* tab_strip = GetTabStripForBrowser(browser()); diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc index 12ec61cfdb..0e3ac65f49 100644 --- a/chrome/browser/ui/views/tabs/tab_strip.cc +++ b/chrome/browser/ui/views/tabs/tab_strip.cc @@ -96,8 +96,10 @@ static const int kMaxStackedCount = 4; static const int kStackedPadding = 6; // See UpdateLayoutTypeFromMouseEvent() for a description of these. +#if !defined(USE_ASH) const int kMouseMoveTimeMS = 200; const int kMouseMoveCountBeforeConsiderReal = 3; +#endif // Amount of time we delay before resizing after a close from a touch. const int kTouchResizeLayoutTimeMS = 2000; @@ -1639,7 +1641,6 @@ Tab* TabStrip::CreateTab() { } void TabStrip::StartInsertTabAnimation(int model_index) { - CHECK_LT(model_index, tabs_.view_size()); PrepareForAnimation(); // The TabStrip can now use its entire width to lay out Tabs. @@ -1647,7 +1648,7 @@ void TabStrip::StartInsertTabAnimation(int model_index) { available_width_for_tabs_ = -1; GenerateIdealBounds(); - CHECK_LT(model_index, tabs_.view_size()); + Tab* tab = tab_at(model_index); if (model_index == 0) { tab->SetBounds(0, ideal_bounds(model_index).y(), 0, diff --git a/chrome/browser/ui/views/toolbar_view.cc b/chrome/browser/ui/views/toolbar_view.cc index 8ac94c0af4..b9f31afc1c 100644 --- a/chrome/browser/ui/views/toolbar_view.cc +++ b/chrome/browser/ui/views/toolbar_view.cc @@ -93,10 +93,6 @@ const int kContentShadowHeightAsh = 2; // Non-ash uses a rounded content area with no shadow in the assets. const int kContentShadowHeight = 0; -// Top margin for the wrench menu badges (badge is placed in the upper right -// corner of the wrench menu). -const int kBadgeTopMargin = 2; - int GetButtonSpacing() { return (ui::GetDisplayLayout() == ui::LAYOUT_TOUCH) ? ToolbarView::kStandardSpacing : 0; @@ -575,9 +571,10 @@ void ToolbarView::Layout() { } bool ToolbarView::HitTestRect(const gfx::Rect& rect) const { - // Don't take hits in our top shadow edge. Let them fall through to the - // tab strip above us. - if (rect.y() < content_shadow_height()) + // Fall through to the tab strip above us if none of |rect| intersects + // with this view (intersection with the top shadow edge does not + // count as intersection with this view). + if (rect.bottom() < content_shadow_height()) return false; // Otherwise let our superclass take care of it. return AccessiblePaneView::HitTestRect(rect); diff --git a/chrome/browser/ui/views/translate/translate_bubble_view.cc b/chrome/browser/ui/views/translate/translate_bubble_view.cc new file mode 100644 index 0000000000..f24b936003 --- /dev/null +++ b/chrome/browser/ui/views/translate/translate_bubble_view.cc @@ -0,0 +1,753 @@ +// Copyright 2013 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/ui/views/translate/translate_bubble_view.h" + +#include <algorithm> +#include <string> +#include <vector> + +#include "base/i18n/string_compare.h" +#include "base/memory/singleton.h" +#include "base/metrics/histogram.h" +#include "base/prefs/pref_service.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/translate/translate_manager.h" +#include "chrome/browser/translate/translate_tab_helper.h" +#include "chrome/browser/translate/translate_ui_delegate.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/translate/translate_bubble_model_impl.h" +#include "chrome/common/url_constants.h" +#include "content/public/browser/web_contents.h" +#include "grit/generated_resources.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/models/combobox_model.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/views/controls/button/checkbox.h" +#include "ui/views/controls/button/label_button.h" +#include "ui/views/controls/combobox/combobox.h" +#include "ui/views/controls/label.h" +#include "ui/views/controls/link.h" +#include "ui/views/layout/box_layout.h" +#include "ui/views/layout/grid_layout.h" +#include "ui/views/layout/layout_constants.h" +#include "ui/views/widget/widget.h" + +namespace { + +views::LabelButton* CreateLabelButton(views::ButtonListener* listener, + const string16& label, int id) { + views::LabelButton* button = new views::LabelButton(listener, label); + button->set_id(id); + button->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON); + return button; +} + +views::Link* CreateLink(views::LinkListener* listener, + int resource_id, + int id) { + views::Link* link = new views::Link( + l10n_util::GetStringUTF16(resource_id)); + link->set_listener(listener); + link->set_id(id); + return link; +} + +void GetTranslateLanguages(content::WebContents* web_contents, + std::string* source, + std::string* target) { + DCHECK(source != NULL); + DCHECK(target != NULL); + + TranslateTabHelper* translate_tab_helper = + TranslateTabHelper::FromWebContents(web_contents); + *source = translate_tab_helper->language_state().original_language(); + *target = TranslateManager::GetLanguageCode( + g_browser_process->GetApplicationLocale()); +} + +// TODO(hajimehoshi): The interface to offer denial choices should be another +// control instead of Combobox. See crbug/305494. +class TranslateDenialComboboxModel : public ui::ComboboxModel { + public: + enum { + INDEX_NOPE = 0, + INDEX_NEVER_TRANSLATE_LANGUAGE = 2, + INDEX_NEVER_TRANSLATE_SITE = 4, + }; + + explicit TranslateDenialComboboxModel( + const string16& original_language_name) { + items_.push_back(l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_DENY)); + items_.push_back(string16()); + items_.push_back(l10n_util::GetStringFUTF16( + IDS_TRANSLATE_BUBBLE_NEVER_TRANSLATE_LANG, + original_language_name)); + items_.push_back(string16()); + items_.push_back(l10n_util::GetStringUTF16( + IDS_TRANSLATE_BUBBLE_NEVER_TRANSLATE_SITE)); + } + virtual ~TranslateDenialComboboxModel() {} + + private: + // Overridden from ui::ComboboxModel: + virtual int GetItemCount() const OVERRIDE { + return items_.size(); + } + virtual string16 GetItemAt(int index) OVERRIDE { + return items_[index]; + } + virtual bool IsItemSeparatorAt(int index) OVERRIDE { + return items_[index].empty(); + } + virtual int GetDefaultIndex() const OVERRIDE { + return 0; + } + + std::vector<string16> items_; + + DISALLOW_COPY_AND_ASSIGN(TranslateDenialComboboxModel); +}; + +const char* kUMATranslateModifyOriginalLang = "Translate.ModifyOriginalLang"; +const char* kUMATranslateModifyTargetLang = "Translate.ModifyTargetLang"; + +} // namespace + +// static +TranslateBubbleView* TranslateBubbleView::translate_bubble_view_ = NULL; + +TranslateBubbleView::~TranslateBubbleView() { + // A child view could refer to a model which is owned by this class when + // the child view is destructed. For example, |source_language_combobx_model_| + // is referred by Combobox's destructor. Before destroying the models, + // removing the child views is needed. + RemoveAllChildViews(true); +} + +// static +void TranslateBubbleView::ShowBubble(views::View* anchor_view, + content::WebContents* web_contents, + TranslateBubbleModel::ViewState type, + Browser* browser) { + if (IsShowing()) { + translate_bubble_view_->SwitchView(type); + return; + } + + std::string source_language; + std::string target_language; + GetTranslateLanguages(web_contents, &source_language, &target_language); + + scoped_ptr<TranslateUIDelegate> ui_delegate( + new TranslateUIDelegate(web_contents, source_language, target_language)); + scoped_ptr<TranslateBubbleModel> model( + new TranslateBubbleModelImpl(type, ui_delegate.Pass())); + bool is_in_incognito_window = + web_contents->GetBrowserContext()->IsOffTheRecord(); + TranslateBubbleView* view = new TranslateBubbleView(anchor_view, + model.Pass(), + is_in_incognito_window, + browser); + views::BubbleDelegateView::CreateBubble(view)->Show(); +} + +// static +bool TranslateBubbleView::IsShowing() { + return translate_bubble_view_ != NULL; +} + +void TranslateBubbleView::Init() { + SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, + 0, 0, 0)); + + before_translate_view_ = CreateViewBeforeTranslate(); + translating_view_ = CreateViewTranslating(); + after_translate_view_ = CreateViewAfterTranslate(); + error_view_ = CreateViewError(); + advanced_view_ = CreateViewAdvanced(); + + AddChildView(before_translate_view_); + AddChildView(translating_view_); + AddChildView(after_translate_view_); + AddChildView(error_view_); + AddChildView(advanced_view_); + + AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE)); + + UpdateChildVisibilities(); +} + +void TranslateBubbleView::ButtonPressed(views::Button* sender, + const ui::Event& event) { + HandleButtonPressed(static_cast<ButtonID>(sender->id())); +} + +void TranslateBubbleView::WindowClosing() { + if (!translate_executed_) + model_->TranslationDeclined(); + + // We have to reset |translate_bubble_view_| here, not in our destructor, + // because we'll be destroyed asynchronously and the shown state will be + // checked before then. + DCHECK_EQ(translate_bubble_view_, this); + translate_bubble_view_ = NULL; +} + +bool TranslateBubbleView::AcceleratorPressed( + const ui::Accelerator& accelerator) { + switch (model_->GetViewState()) { + case TranslateBubbleModel::VIEW_STATE_BEFORE_TRANSLATE: { + if (accelerator.key_code() == ui::VKEY_RETURN) { + HandleButtonPressed(BUTTON_ID_TRANSLATE); + return true; + } + break; + } + case TranslateBubbleModel::VIEW_STATE_TRANSLATING: + break; + case TranslateBubbleModel::VIEW_STATE_AFTER_TRANSLATE: { + if (accelerator.key_code() == ui::VKEY_RETURN) { + HandleButtonPressed(BUTTON_ID_SHOW_ORIGINAL); + return true; + } + break; + } + case TranslateBubbleModel::VIEW_STATE_ERROR: + break; + case TranslateBubbleModel::VIEW_STATE_ADVANCED: { + if (accelerator.key_code() == ui::VKEY_RETURN) { + HandleButtonPressed(BUTTON_ID_DONE); + return true; + } + break; + } + } + return BubbleDelegateView::AcceleratorPressed(accelerator); +} + +gfx::Size TranslateBubbleView::GetPreferredSize() { + int width = 0; + for (int i = 0; i < child_count(); i++) { + views::View* child = child_at(i); + width = std::max(width, child->GetPreferredSize().width()); + } + int height = GetCurrentView()->GetPreferredSize().height(); + return gfx::Size(width, height); +} + +void TranslateBubbleView::OnSelectedIndexChanged(views::Combobox* combobox) { + switch (static_cast<ComboboxID>(combobox->id())) { + case COMBOBOX_ID_DENIAL: { + int index = combobox->selected_index(); + switch (index) { + case TranslateDenialComboboxModel::INDEX_NOPE: + if (!translate_executed_) + model_->TranslationDeclined(); + StartFade(false); + break; + case TranslateDenialComboboxModel::INDEX_NEVER_TRANSLATE_LANGUAGE: + model_->SetNeverTranslateLanguage(true); + StartFade(false); + break; + case TranslateDenialComboboxModel::INDEX_NEVER_TRANSLATE_SITE: + model_->SetNeverTranslateSite(true); + StartFade(false); + break; + default: + NOTREACHED(); + break; + } + break; + } + case COMBOBOX_ID_SOURCE_LANGUAGE: { + // TODO(hajimehoshi): This UMA should be counted at a model or a delegate + // not to dependent on platforms. However, this UMA has not been used at + // some platforms. See crbug/306365. + UMA_HISTOGRAM_BOOLEAN(kUMATranslateModifyOriginalLang, true); + UpdateAdvancedView(); + break; + } + case COMBOBOX_ID_TARGET_LANGUAGE: { + // TODO(hajimehoshi): Ditto. + UMA_HISTOGRAM_BOOLEAN(kUMATranslateModifyTargetLang, true); + UpdateAdvancedView(); + break; + } + } +} + +void TranslateBubbleView::LinkClicked(views::Link* source, int event_flags) { + HandleLinkClicked(static_cast<LinkID>(source->id())); +} + +TranslateBubbleModel::ViewState TranslateBubbleView::GetViewState() const { + return model_->GetViewState(); +} + +TranslateBubbleView::TranslateBubbleView( + views::View* anchor_view, + scoped_ptr<TranslateBubbleModel> model, + bool is_in_incognito_window, + Browser* browser) + : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT), + source_language_combobox_(NULL), + target_language_combobox_(NULL), + always_translate_checkbox_(NULL), + model_(model.Pass()), + is_in_incognito_window_(is_in_incognito_window), + browser_(browser), + translate_executed_(false) { + if (model_->GetViewState() != + TranslateBubbleModel::VIEW_STATE_BEFORE_TRANSLATE) { + translate_executed_ = true; + } + + set_margins(gfx::Insets(views::kPanelVertMargin, views::kPanelHorizMargin, + views::kPanelVertMargin, views::kPanelHorizMargin)); + + translate_bubble_view_ = this; +} + +views::View* TranslateBubbleView::GetCurrentView() { + switch (model_->GetViewState()) { + case TranslateBubbleModel::VIEW_STATE_BEFORE_TRANSLATE: + return before_translate_view_; + case TranslateBubbleModel::VIEW_STATE_TRANSLATING: + return translating_view_; + case TranslateBubbleModel::VIEW_STATE_AFTER_TRANSLATE: + return after_translate_view_; + case TranslateBubbleModel::VIEW_STATE_ERROR: + return error_view_; + case TranslateBubbleModel::VIEW_STATE_ADVANCED: + return advanced_view_; + } + NOTREACHED(); + return NULL; +} + +void TranslateBubbleView::HandleButtonPressed( + TranslateBubbleView::ButtonID sender_id) { + switch (sender_id) { + case BUTTON_ID_TRANSLATE: { + translate_executed_ = true; + model_->Translate(); + break; + } + case BUTTON_ID_DONE: { + translate_executed_ = true; + DCHECK(source_language_combobox_); + DCHECK(target_language_combobox_); + + model_->SetOriginalLanguageIndex( + source_language_combobox_->selected_index()); + model_->SetTargetLanguageIndex( + target_language_combobox_->selected_index()); + model_->Translate(); + break; + } + case BUTTON_ID_CANCEL: { + model_->GoBackFromAdvanced(); + UpdateChildVisibilities(); + SizeToContents(); + break; + } + case BUTTON_ID_TRY_AGAIN: { + translate_executed_ = true; + model_->Translate(); + break; + } + case BUTTON_ID_SHOW_ORIGINAL: { + model_->RevertTranslation(); + StartFade(false); + break; + } + case BUTTON_ID_ALWAYS_TRANSLATE: { + DCHECK(always_translate_checkbox_); + model_->SetAlwaysTranslate(always_translate_checkbox_->checked()); + UpdateAdvancedView(); + break; + } + } +} + +void TranslateBubbleView::HandleLinkClicked( + TranslateBubbleView::LinkID sender_id) { + switch (sender_id) { + case LINK_ID_ADVANCED: { + SwitchView(TranslateBubbleModel::VIEW_STATE_ADVANCED); + break; + } + case LINK_ID_LEARN_MORE: { + browser_->OpenURL(content::OpenURLParams( + GURL(chrome::kAboutGoogleTranslateURL), + content::Referrer(), + NEW_FOREGROUND_TAB, + content::PAGE_TRANSITION_LINK, + false)); + break; + } + } +} + +void TranslateBubbleView::UpdateChildVisibilities() { + for (int i = 0; i < child_count(); i++) { + views::View* view = child_at(i); + view->SetVisible(view == GetCurrentView()); + } +} + +views::View* TranslateBubbleView::CreateViewBeforeTranslate() { + views::Label* message_label = new views::Label( + l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_BEFORE_TRANSLATE)); + + string16 original_language_name = + model_->GetLanguageNameAt(model_->GetOriginalLanguageIndex()); + views::Combobox* denial_combobox = new views::Combobox( + new TranslateDenialComboboxModel(original_language_name)); + denial_combobox->set_id(COMBOBOX_ID_DENIAL); + denial_combobox->set_listener(this); + + views::View* view = new views::View(); + views::GridLayout* layout = new views::GridLayout(view); + view->SetLayoutManager(layout); + + using views::GridLayout; + + enum { + COLUMN_SET_ID_MESSAGE, + COLUMN_SET_ID_CONTENT, + }; + + views::ColumnSet* cs = layout->AddColumnSet(COLUMN_SET_ID_MESSAGE); + cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, + 0, GridLayout::USE_PREF, 0, 0); + cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing); + cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, + 0, GridLayout::USE_PREF, 0, 0); + cs->AddPaddingColumn(1, 0); + + cs = layout->AddColumnSet(COLUMN_SET_ID_CONTENT); + cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, + 0, GridLayout::USE_PREF, 0, 0); + cs->AddPaddingColumn(1, views::kUnrelatedControlHorizontalSpacing); + cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, + 0, GridLayout::USE_PREF, 0, 0); + cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing); + cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, + 0, GridLayout::USE_PREF, 0, 0); + + layout->StartRow(0, COLUMN_SET_ID_MESSAGE); + layout->AddView(message_label); + layout->AddView(CreateLink(this, + IDS_TRANSLATE_BUBBLE_ADVANCED, + LINK_ID_ADVANCED)); + + layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing); + + layout->StartRow(0, COLUMN_SET_ID_CONTENT); + layout->AddView(CreateLink(this, + IDS_TRANSLATE_BUBBLE_LEARN_MORE, + LINK_ID_LEARN_MORE)); + layout->AddView(denial_combobox); + layout->AddView(CreateLabelButton( + this, + l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_ACCEPT), + BUTTON_ID_TRANSLATE)); + + return view; +} + +views::View* TranslateBubbleView::CreateViewTranslating() { + string16 target_language_name = + model_->GetLanguageNameAt(model_->GetTargetLanguageIndex()); + views::Label* label = new views::Label( + l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_TRANSLATING)); + + views::View* view = new views::View(); + views::GridLayout* layout = new views::GridLayout(view); + view->SetLayoutManager(layout); + + using views::GridLayout; + + enum { + COLUMN_SET_ID_MESSAGE, + COLUMN_SET_ID_CONTENT, + }; + + views::ColumnSet* cs = layout->AddColumnSet(COLUMN_SET_ID_MESSAGE); + cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, + 0, views::GridLayout::USE_PREF, 0, 0); + cs->AddPaddingColumn(1, 0); + + cs = layout->AddColumnSet(COLUMN_SET_ID_CONTENT); + cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, + 0, GridLayout::USE_PREF, 0, 0); + cs->AddPaddingColumn(1, views::kUnrelatedControlHorizontalSpacing); + cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, + 0, GridLayout::USE_PREF, 0, 0); + + layout->StartRow(0, COLUMN_SET_ID_MESSAGE); + layout->AddView(label); + + layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing); + + layout->StartRow(0, COLUMN_SET_ID_CONTENT); + layout->AddView(CreateLink(this, + IDS_TRANSLATE_BUBBLE_LEARN_MORE, + LINK_ID_LEARN_MORE)); + views::LabelButton* revert_button = CreateLabelButton( + this, + l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_REVERT), + BUTTON_ID_SHOW_ORIGINAL); + revert_button->SetEnabled(false); + layout->AddView(revert_button); + + return view; +} + +views::View* TranslateBubbleView::CreateViewAfterTranslate() { + views::Label* label = new views::Label( + l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_TRANSLATED)); + + views::View* view = new views::View(); + views::GridLayout* layout = new views::GridLayout(view); + view->SetLayoutManager(layout); + + using views::GridLayout; + + enum { + COLUMN_SET_ID_MESSAGE, + COLUMN_SET_ID_CONTENT, + }; + + views::ColumnSet* cs = layout->AddColumnSet(COLUMN_SET_ID_MESSAGE); + cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, + 0, views::GridLayout::USE_PREF, 0, 0); + cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing); + cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, + 0, views::GridLayout::USE_PREF, 0, 0); + cs->AddPaddingColumn(1, 0); + + cs = layout->AddColumnSet(COLUMN_SET_ID_CONTENT); + cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, + 0, GridLayout::USE_PREF, 0, 0); + cs->AddPaddingColumn(1, views::kUnrelatedControlHorizontalSpacing); + cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, + 0, GridLayout::USE_PREF, 0, 0); + + layout->StartRow(0, COLUMN_SET_ID_MESSAGE); + layout->AddView(label); + layout->AddView(CreateLink(this, + IDS_TRANSLATE_BUBBLE_ADVANCED, + LINK_ID_ADVANCED)); + + layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing); + + layout->StartRow(0, COLUMN_SET_ID_CONTENT); + layout->AddView(CreateLink(this, + IDS_TRANSLATE_BUBBLE_LEARN_MORE, + LINK_ID_LEARN_MORE)); + layout->AddView(CreateLabelButton( + this, + l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_REVERT), + BUTTON_ID_SHOW_ORIGINAL)); + + return view; +} + +views::View* TranslateBubbleView::CreateViewError() { + views::Label* label = new views::Label( + l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_COULD_NOT_TRANSLATE)); + + views::View* view = new views::View(); + views::GridLayout* layout = new views::GridLayout(view); + view->SetLayoutManager(layout); + + using views::GridLayout; + + enum { + COLUMN_SET_ID_MESSAGE, + COLUMN_SET_ID_CONTENT, + }; + + views::ColumnSet* cs = layout->AddColumnSet(COLUMN_SET_ID_MESSAGE); + cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, + 0, GridLayout::USE_PREF, 0, 0); + cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing); + cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, + 0, GridLayout::USE_PREF, 0, 0); + cs->AddPaddingColumn(1, 0); + + cs = layout->AddColumnSet(COLUMN_SET_ID_CONTENT); + cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, + 0, GridLayout::USE_PREF, 0, 0); + cs->AddPaddingColumn(1, views::kUnrelatedControlHorizontalSpacing); + cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, + 0, GridLayout::USE_PREF, 0, 0); + + layout->StartRow(0, COLUMN_SET_ID_MESSAGE); + layout->AddView(label); + layout->AddView(CreateLink(this, + IDS_TRANSLATE_BUBBLE_ADVANCED, + LINK_ID_ADVANCED)); + + layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing); + + layout->StartRow(0, COLUMN_SET_ID_CONTENT); + layout->AddView(CreateLink(this, + IDS_TRANSLATE_BUBBLE_LEARN_MORE, + LINK_ID_LEARN_MORE)); + layout->AddView(CreateLabelButton( + this, + l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_TRY_AGAIN), + BUTTON_ID_TRY_AGAIN)); + + return view; +} + +// TODO(hajimehoshi): Revice this later to show a specific message for each +// error. (crbug/307350) +views::View* TranslateBubbleView::CreateViewAdvanced() { + views::Label* source_language_label = new views::Label( + l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_PAGE_LANGUAGE)); + + views::Label* target_language_label = new views::Label( + l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_TRANSLATION_LANGUAGE)); + + int source_default_index = model_->GetOriginalLanguageIndex(); + source_language_combobox_model_.reset( + new LanguageComboboxModel(source_default_index, model_.get())); + source_language_combobox_ = + new views::Combobox(source_language_combobox_model_.get()); + + source_language_combobox_->set_id(COMBOBOX_ID_SOURCE_LANGUAGE); + source_language_combobox_->set_listener(this); + + int target_default_index = model_->GetTargetLanguageIndex(); + target_language_combobox_model_.reset( + new LanguageComboboxModel(target_default_index, model_.get())); + target_language_combobox_ = + new views::Combobox(target_language_combobox_model_.get()); + + target_language_combobox_->set_id(COMBOBOX_ID_TARGET_LANGUAGE); + target_language_combobox_->set_listener(this); + + // In an incognito window, "Always translate" checkbox shouldn't be shown. + if (!is_in_incognito_window_) { + always_translate_checkbox_ = new views::Checkbox(string16()); + always_translate_checkbox_->set_id(BUTTON_ID_ALWAYS_TRANSLATE); + always_translate_checkbox_->set_listener(this); + } + + views::View* view = new views::View(); + views::GridLayout* layout = new views::GridLayout(view); + view->SetLayoutManager(layout); + + using views::GridLayout; + + enum { + COLUMN_SET_ID_LANGUAGES, + COLUMN_SET_ID_ALWAYS_TRANSLATE, + COLUMN_SET_ID_BUTTONS, + }; + + views::ColumnSet* cs = layout->AddColumnSet(COLUMN_SET_ID_LANGUAGES); + cs->AddColumn(GridLayout::TRAILING, GridLayout::CENTER, + 0, GridLayout::USE_PREF, 0, 0); + cs->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); + cs->AddColumn(GridLayout::FILL, GridLayout::CENTER, + 0, GridLayout::USE_PREF, 0, 0); + cs->AddPaddingColumn(1, 0); + + if (!is_in_incognito_window_) { + cs = layout->AddColumnSet(COLUMN_SET_ID_ALWAYS_TRANSLATE); + cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, + 0, GridLayout::USE_PREF, 0, 0); + cs->AddPaddingColumn(1, 0); + } + + cs = layout->AddColumnSet(COLUMN_SET_ID_BUTTONS); + cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, + 0, GridLayout::USE_PREF, 0, 0); + cs->AddPaddingColumn(1, views::kUnrelatedControlHorizontalSpacing); + cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, + 0, GridLayout::USE_PREF, 0, 0); + cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing); + cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, + 0, GridLayout::USE_PREF, 0, 0); + + layout->StartRow(0, COLUMN_SET_ID_LANGUAGES); + layout->AddView(source_language_label); + layout->AddView(source_language_combobox_); + + layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); + + layout->StartRow(0, COLUMN_SET_ID_LANGUAGES); + layout->AddView(target_language_label); + layout->AddView(target_language_combobox_); + + if (!is_in_incognito_window_) { + layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing); + + layout->StartRow(0, COLUMN_SET_ID_ALWAYS_TRANSLATE); + layout->AddView(always_translate_checkbox_); + } + + layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing); + + layout->StartRow(0, COLUMN_SET_ID_BUTTONS); + layout->AddView(CreateLink(this, + IDS_TRANSLATE_BUBBLE_LEARN_MORE, + LINK_ID_LEARN_MORE)); + views::LabelButton* cancel_button = CreateLabelButton( + this, l10n_util::GetStringUTF16(IDS_CANCEL), BUTTON_ID_CANCEL); + layout->AddView(cancel_button); + views::LabelButton* done_button = CreateLabelButton( + this, l10n_util::GetStringUTF16(IDS_DONE), BUTTON_ID_DONE); + done_button->SetIsDefault(true); + layout->AddView(done_button); + + UpdateAdvancedView(); + + return view; +} + +void TranslateBubbleView::SwitchView( + TranslateBubbleModel::ViewState view_state) { + if (model_->GetViewState() == view_state) + return; + + model_->SetViewState(view_state); + UpdateChildVisibilities(); + SizeToContents(); +} + +void TranslateBubbleView::UpdateAdvancedView() { + DCHECK(source_language_combobox_); + DCHECK(target_language_combobox_); + + model_->SetOriginalLanguageIndex( + source_language_combobox_->selected_index()); + model_->SetTargetLanguageIndex( + target_language_combobox_->selected_index()); + + string16 source_language_name = + model_->GetLanguageNameAt(model_->GetOriginalLanguageIndex()); + string16 target_language_name = + model_->GetLanguageNameAt(model_->GetTargetLanguageIndex()); + + string16 message = + l10n_util::GetStringFUTF16(IDS_TRANSLATE_BUBBLE_ALWAYS, + source_language_name, + target_language_name); + // "Always translate" checkbox doesn't exist in an incognito window. + if (always_translate_checkbox_) { + always_translate_checkbox_->SetText(message); + always_translate_checkbox_->SetChecked( + model_->ShouldAlwaysTranslate()); + } +} diff --git a/chrome/browser/ui/views/translate/translate_bubble_view.h b/chrome/browser/ui/views/translate/translate_bubble_view.h new file mode 100644 index 0000000000..937ced284f --- /dev/null +++ b/chrome/browser/ui/views/translate/translate_bubble_view.h @@ -0,0 +1,176 @@ +// Copyright 2013 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_UI_VIEWS_TRANSLATE_TRANSLATE_BUBBLE_VIEW_H_ +#define CHROME_BROWSER_UI_VIEWS_TRANSLATE_TRANSLATE_BUBBLE_VIEW_H_ + +#include <map> +#include <string> + +#include "base/basictypes.h" +#include "chrome/browser/ui/translate/language_combobox_model.h" +#include "chrome/browser/ui/translate/translate_bubble_model.h" +#include "ui/views/bubble/bubble_delegate.h" +#include "ui/views/controls/button/button.h" +#include "ui/views/controls/combobox/combobox_listener.h" +#include "ui/views/controls/link_listener.h" + +class Browser; +class PrefService; +class TranslateBubbleModel; + +namespace content { +class WebContents; +} + +namespace views { +class Checkbox; +class GridLayout; +class LabelButton; +class Link; +class View; +} + +class TranslateBubbleView : public views::BubbleDelegateView, + public views::ButtonListener, + public views::ComboboxListener, + public views::LinkListener { + public: + virtual ~TranslateBubbleView(); + + // Shows the Translate bubble. + static void ShowBubble(views::View* anchor_view, + content::WebContents* web_contents, + TranslateBubbleModel::ViewState type, + Browser* browser); + + // If true, the Translate bubble is being shown. + static bool IsShowing(); + + // views::BubbleDelegateView methods. + virtual void Init() OVERRIDE; + virtual void ButtonPressed(views::Button* sender, + const ui::Event& event) OVERRIDE; + + // views::WidgetDelegate method. + virtual void WindowClosing() OVERRIDE; + + // views::View methods. + virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE; + virtual gfx::Size GetPreferredSize() OVERRIDE; + + // views::CombboxListener method. + virtual void OnSelectedIndexChanged(views::Combobox* combobox) OVERRIDE; + + // views::LinkListener method. + virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE; + + // Returns the current view state. + TranslateBubbleModel::ViewState GetViewState() const; + + private: + enum LinkID { + LINK_ID_ADVANCED, + LINK_ID_LEARN_MORE, + }; + + enum ButtonID { + BUTTON_ID_TRANSLATE, + BUTTON_ID_DONE, + BUTTON_ID_CANCEL, + BUTTON_ID_SHOW_ORIGINAL, + BUTTON_ID_TRY_AGAIN, + BUTTON_ID_ALWAYS_TRANSLATE, + }; + + enum ComboboxID { + COMBOBOX_ID_DENIAL, + COMBOBOX_ID_SOURCE_LANGUAGE, + COMBOBOX_ID_TARGET_LANGUAGE, + }; + + friend class TranslateBubbleViewTest; + FRIEND_TEST_ALL_PREFIXES(TranslateBubbleViewTest, TranslateButton); + FRIEND_TEST_ALL_PREFIXES(TranslateBubbleViewTest, AdvancedLink); + FRIEND_TEST_ALL_PREFIXES(TranslateBubbleViewTest, ShowOriginalButton); + FRIEND_TEST_ALL_PREFIXES(TranslateBubbleViewTest, TryAgainButton); + FRIEND_TEST_ALL_PREFIXES(TranslateBubbleViewTest, AlwaysTranslateCheckbox); + FRIEND_TEST_ALL_PREFIXES(TranslateBubbleViewTest, DoneButton); + FRIEND_TEST_ALL_PREFIXES(TranslateBubbleViewTest, + CancelButtonReturningBeforeTranslate); + FRIEND_TEST_ALL_PREFIXES(TranslateBubbleViewTest, + CancelButtonReturningAfterTranslate); + FRIEND_TEST_ALL_PREFIXES(TranslateBubbleViewTest, CancelButtonReturningError); + + TranslateBubbleView(views::View* anchor_view, + scoped_ptr<TranslateBubbleModel> model, + bool is_in_incognito_window, + Browser* browser); + + // Returns the current child view. + views::View* GetCurrentView(); + + // Handles the event when the user presses a button. + void HandleButtonPressed(ButtonID sender_id); + + // Handles the event when the user clicks a link. + void HandleLinkClicked(LinkID sender_id); + + // Updates the visibilities of child views according to the current view type. + void UpdateChildVisibilities(); + + // Creates the 'before translate' view. Caller takes ownership of the returned + // view. + views::View* CreateViewBeforeTranslate(); + + // Creates the 'translating' view. Caller takes ownership of the returned + // view. + views::View* CreateViewTranslating(); + + // Creates the 'after translate' view. Caller takes ownership of the returned + // view. + views::View* CreateViewAfterTranslate(); + + // Creates the 'error' view. Caller takes ownership of the returned view. + views::View* CreateViewError(); + + // Creates the 'advanced' view. Caller takes ownership of the returned view. + views::View* CreateViewAdvanced(); + + // Switches the view type. + void SwitchView(TranslateBubbleModel::ViewState view_state); + + // Updates the advanced view. + void UpdateAdvancedView(); + + static TranslateBubbleView* translate_bubble_view_; + + views::View* before_translate_view_; + views::View* translating_view_; + views::View* after_translate_view_; + views::View* error_view_; + views::View* advanced_view_; + + scoped_ptr<LanguageComboboxModel> source_language_combobox_model_; + scoped_ptr<LanguageComboboxModel> target_language_combobox_model_; + + views::Combobox* source_language_combobox_; + views::Combobox* target_language_combobox_; + views::Checkbox* always_translate_checkbox_; + + scoped_ptr<TranslateBubbleModel> model_; + + // Whether the window is an incognito window. + bool is_in_incognito_window_; + + // The browser to open the help URL into a new tab. + Browser* browser_; + + // Whether the translation is acutually executed. + bool translate_executed_; + + DISALLOW_COPY_AND_ASSIGN(TranslateBubbleView); +}; + +#endif // CHROME_BROWSER_UI_VIEWS_TRANSLATE_TRANSLATE_BUBBLE_VIEW_H_ diff --git a/chrome/browser/ui/views/translate/translate_bubble_view_unittest.cc b/chrome/browser/ui/views/translate/translate_bubble_view_unittest.cc new file mode 100644 index 0000000000..9082f0698a --- /dev/null +++ b/chrome/browser/ui/views/translate/translate_bubble_view_unittest.cc @@ -0,0 +1,256 @@ +// Copyright 2013 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/ui/views/translate/translate_bubble_view.h" + +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/ui/translate/translate_bubble_model.h" +#include "chrome/browser/ui/translate/translate_bubble_view_state_transition.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/views/controls/button/checkbox.h" +#include "ui/views/controls/combobox/combobox.h" +#include "ui/views/test/views_test_base.h" +#include "ui/views/widget/widget.h" + +namespace { + +class MockTranslateBubbleModel : public TranslateBubbleModel { + public: + explicit MockTranslateBubbleModel(TranslateBubbleModel::ViewState view_state) + : view_state_transition_(view_state), + original_language_index_(0), + target_language_index_(1), + never_translate_language_(false), + never_translate_site_(false), + should_always_translate_(false), + set_always_translate_called_count_(0), + translate_called_(false), + revert_translation_called_(false), + translation_declined_called_(false) { + } + + virtual TranslateBubbleModel::ViewState GetViewState() const OVERRIDE { + return view_state_transition_.view_state(); + } + + virtual void SetViewState(TranslateBubbleModel::ViewState view_state) + OVERRIDE { + view_state_transition_.SetViewState(view_state); + } + + virtual void GoBackFromAdvanced() OVERRIDE { + view_state_transition_.GoBackFromAdvanced(); + } + + virtual int GetNumberOfLanguages() const OVERRIDE { + return 1000; + } + + virtual string16 GetLanguageNameAt(int index) const OVERRIDE { + return string16(); + } + + virtual int GetOriginalLanguageIndex() const OVERRIDE { + return original_language_index_; + } + + virtual void SetOriginalLanguageIndex(int index) OVERRIDE { + original_language_index_ = index; + } + + virtual int GetTargetLanguageIndex() const OVERRIDE { + return target_language_index_; + } + + virtual void SetTargetLanguageIndex(int index) OVERRIDE { + target_language_index_ = index; + } + + virtual void SetNeverTranslateLanguage(bool value) OVERRIDE { + never_translate_language_ = value; + } + + virtual void SetNeverTranslateSite(bool value) OVERRIDE { + never_translate_site_ = value; + } + + virtual bool ShouldAlwaysTranslate() const OVERRIDE { + return should_always_translate_; + } + + virtual void SetAlwaysTranslate(bool value) OVERRIDE { + should_always_translate_ = value; + set_always_translate_called_count_++; + } + + virtual void Translate() OVERRIDE { + translate_called_ = true; + } + + virtual void RevertTranslation() OVERRIDE { + revert_translation_called_ = true; + } + + virtual void TranslationDeclined() OVERRIDE { + translation_declined_called_ = true; + } + + TranslateBubbleViewStateTransition view_state_transition_; + int original_language_index_; + int target_language_index_; + bool never_translate_language_; + bool never_translate_site_; + bool should_always_translate_; + int set_always_translate_called_count_; + bool translate_called_; + bool revert_translation_called_; + bool translation_declined_called_; +}; + +} // namespace + +class TranslateBubbleViewTest : public views::ViewsTestBase { + public: + TranslateBubbleViewTest() { + } + + protected: + virtual void SetUp() OVERRIDE { + views::ViewsTestBase::SetUp(); + + // The bubble needs the parent as an anchor. + views::Widget::InitParams params = + CreateParams(views::Widget::InitParams::TYPE_WINDOW); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + + anchor_widget_.reset(new views::Widget()); + anchor_widget_->Init(params); + anchor_widget_->Show(); + + mock_model_ = new MockTranslateBubbleModel( + TranslateBubbleModel::VIEW_STATE_BEFORE_TRANSLATE); + scoped_ptr<TranslateBubbleModel> model(mock_model_); + bubble_ = new TranslateBubbleView(anchor_widget_->GetContentsView(), + model.Pass(), + false, + NULL); + views::BubbleDelegateView::CreateBubble(bubble_)->Show(); + } + + virtual void TearDown() OVERRIDE { + bubble_->GetWidget()->CloseNow(); + anchor_widget_.reset(); + + views::ViewsTestBase::TearDown(); + } + + scoped_ptr<views::Widget> anchor_widget_; + MockTranslateBubbleModel* mock_model_; + TranslateBubbleView* bubble_; +}; + +TEST_F(TranslateBubbleViewTest, TranslateButton) { + EXPECT_FALSE(mock_model_->translate_called_); + + // Press the "Translate" button. + bubble_->HandleButtonPressed(TranslateBubbleView::BUTTON_ID_TRANSLATE); + EXPECT_TRUE(mock_model_->translate_called_); +} + +TEST_F(TranslateBubbleViewTest, AdvancedLink) { + EXPECT_EQ(TranslateBubbleModel::VIEW_STATE_BEFORE_TRANSLATE, + bubble_->GetViewState()); + + // Click the "Advanced" link. + bubble_->HandleLinkClicked(TranslateBubbleView::LINK_ID_ADVANCED); + EXPECT_EQ(TranslateBubbleModel::VIEW_STATE_ADVANCED, bubble_->GetViewState()); +} + +TEST_F(TranslateBubbleViewTest, ShowOriginalButton) { + bubble_->SwitchView(TranslateBubbleModel::VIEW_STATE_AFTER_TRANSLATE); + + // Click the "Show original" button to revert translation. + EXPECT_FALSE(mock_model_->revert_translation_called_); + bubble_->HandleButtonPressed(TranslateBubbleView::BUTTON_ID_SHOW_ORIGINAL); + EXPECT_TRUE(mock_model_->revert_translation_called_); +} + +TEST_F(TranslateBubbleViewTest, TryAgainButton) { + bubble_->SwitchView(TranslateBubbleModel::VIEW_STATE_ERROR); + + // Click the "Try again" button to translate. + EXPECT_FALSE(mock_model_->translate_called_); + bubble_->HandleButtonPressed(TranslateBubbleView::BUTTON_ID_TRY_AGAIN); + EXPECT_TRUE(mock_model_->translate_called_); +} + +TEST_F(TranslateBubbleViewTest, AlwaysTranslateCheckbox) { + bubble_->SwitchView(TranslateBubbleModel::VIEW_STATE_ADVANCED); + + // Click the "Always Translate" checkbox. Changing the state of this checkbox + // should affect the model immediately. + + // Check the initial state. + EXPECT_FALSE(mock_model_->should_always_translate_); + EXPECT_EQ(0, mock_model_->set_always_translate_called_count_); + EXPECT_FALSE(bubble_->always_translate_checkbox_->checked()); + + // Click the checkbox. + bubble_->always_translate_checkbox_->SetChecked(true); + bubble_->HandleButtonPressed(TranslateBubbleView::BUTTON_ID_ALWAYS_TRANSLATE); + EXPECT_TRUE(mock_model_->should_always_translate_); + EXPECT_EQ(1, mock_model_->set_always_translate_called_count_); + + // Click this again and check the state is reverted. + bubble_->always_translate_checkbox_->SetChecked(false); + bubble_->HandleButtonPressed(TranslateBubbleView::BUTTON_ID_ALWAYS_TRANSLATE); + EXPECT_FALSE(mock_model_->should_always_translate_); + EXPECT_EQ(2, mock_model_->set_always_translate_called_count_); +} + +TEST_F(TranslateBubbleViewTest, DoneButton) { + bubble_->SwitchView(TranslateBubbleModel::VIEW_STATE_ADVANCED); + + // Click the "Done" button to translate. The selected languages by the user + // are applied. + EXPECT_FALSE(mock_model_->translate_called_); + bubble_->source_language_combobox_->SetSelectedIndex(10); + bubble_->target_language_combobox_->SetSelectedIndex(20); + bubble_->HandleButtonPressed(TranslateBubbleView::BUTTON_ID_DONE); + EXPECT_TRUE(mock_model_->translate_called_); + EXPECT_EQ(10, mock_model_->original_language_index_); + EXPECT_EQ(20, mock_model_->target_language_index_); +} + +TEST_F(TranslateBubbleViewTest, CancelButtonReturningBeforeTranslate) { + bubble_->SwitchView(TranslateBubbleModel::VIEW_STATE_BEFORE_TRANSLATE); + bubble_->SwitchView(TranslateBubbleModel::VIEW_STATE_ADVANCED); + + // Click the "Cancel" button to go back. + EXPECT_EQ(TranslateBubbleModel::VIEW_STATE_ADVANCED, bubble_->GetViewState()); + bubble_->HandleButtonPressed(TranslateBubbleView::BUTTON_ID_CANCEL); + EXPECT_EQ(TranslateBubbleModel::VIEW_STATE_BEFORE_TRANSLATE, + bubble_->GetViewState()); +} + +TEST_F(TranslateBubbleViewTest, CancelButtonReturningAfterTranslate) { + bubble_->SwitchView(TranslateBubbleModel::VIEW_STATE_AFTER_TRANSLATE); + bubble_->SwitchView(TranslateBubbleModel::VIEW_STATE_ADVANCED); + + // Click the "Cancel" button to go back. + EXPECT_EQ(TranslateBubbleModel::VIEW_STATE_ADVANCED, bubble_->GetViewState()); + bubble_->HandleButtonPressed(TranslateBubbleView::BUTTON_ID_CANCEL); + EXPECT_EQ(TranslateBubbleModel::VIEW_STATE_AFTER_TRANSLATE, + bubble_->GetViewState()); +} + +TEST_F(TranslateBubbleViewTest, CancelButtonReturningError) { + bubble_->SwitchView(TranslateBubbleModel::VIEW_STATE_ERROR); + bubble_->SwitchView(TranslateBubbleModel::VIEW_STATE_ADVANCED); + + // Click the "Cancel" button to go back. + EXPECT_EQ(TranslateBubbleModel::VIEW_STATE_ADVANCED, bubble_->GetViewState()); + bubble_->HandleButtonPressed(TranslateBubbleView::BUTTON_ID_CANCEL); + EXPECT_EQ(TranslateBubbleModel::VIEW_STATE_ERROR, bubble_->GetViewState()); +} diff --git a/chrome/browser/ui/webui/chromeos/diagnostics/diagnostics_ui.cc b/chrome/browser/ui/webui/chromeos/diagnostics/diagnostics_ui.cc index e891a1622f..2d0ec453d3 100644 --- a/chrome/browser/ui/webui/chromeos/diagnostics/diagnostics_ui.cc +++ b/chrome/browser/ui/webui/chromeos/diagnostics/diagnostics_ui.cc @@ -22,8 +22,11 @@ namespace chromeos { namespace { +// Key for devices dictionary in JSON returned from GetNetworkStatus. +const char kDevicesKey[] = "devices"; + // JS API callback names. -const char kJsApiSetNetifStatus[] = "diag.DiagPage.setNetifStatus"; +const char kJsApiSetDeviceStatus[] = "diag.DiagPage.setDeviceStatus"; const char kJsApiSetTestICMPStatus[] = "diag.DiagPage.setTestICMPStatus"; //////////////////////////////////////////////////////////////////////////////// @@ -47,11 +50,11 @@ class DiagnosticsWebUIHandler : public content::WebUIMessageHandler { // Called by JS layer to test ICMP connectivity to a specified host. void TestICMP(const base::ListValue* args); - // Called when GetNetworkInterfaces() is complete. + // Called when GetNetworkStatus() is complete. // |succeeded|: information was obtained successfully. // |status|: network interfaces information in json. See - // DebugDaemonClient::GetNetworkInterfaces() for details. - void OnGetNetworkInterfaces(bool succeeded, const std::string& status); + // DebugDaemonClient::GetNetworkStatus() for details. + void OnGetNetworkStatus(bool succeeded, const std::string& status); // Called when TestICMP() is complete. // |succeeded|: information was obtained successfully. @@ -80,8 +83,8 @@ void DiagnosticsWebUIHandler::GetNetworkInterfaces( chromeos::DBusThreadManager::Get()->GetDebugDaemonClient(); DCHECK(debugd_client); - debugd_client->GetNetworkInterfaces( - base::Bind(&DiagnosticsWebUIHandler::OnGetNetworkInterfaces, + debugd_client->GetNetworkStatus( + base::Bind(&DiagnosticsWebUIHandler::OnGetNetworkStatus, weak_ptr_factory_.GetWeakPtr())); } @@ -103,7 +106,7 @@ void DiagnosticsWebUIHandler::TestICMP(const base::ListValue* args) { weak_ptr_factory_.GetWeakPtr())); } -void DiagnosticsWebUIHandler::OnGetNetworkInterfaces( +void DiagnosticsWebUIHandler::OnGetNetworkStatus( bool succeeded, const std::string& status) { if (!succeeded) return; @@ -111,7 +114,12 @@ void DiagnosticsWebUIHandler::OnGetNetworkInterfaces( if (parsed_value.get() && parsed_value->IsType(Value::TYPE_DICTIONARY)) { base::DictionaryValue* result = static_cast<DictionaryValue*>(parsed_value.get()); - web_ui()->CallJavascriptFunction(kJsApiSetNetifStatus, *result); + base::DictionaryValue* devices_info; + if (!result->GetDictionary(std::string(kDevicesKey), &devices_info)) { + NOTREACHED() << + "Received improperly formatted result from GetNetworkStatus"; + } + web_ui()->CallJavascriptFunction(kJsApiSetDeviceStatus, *devices_info); } } @@ -150,10 +158,10 @@ DiagnosticsUI::DiagnosticsUI(content::WebUI* web_ui) source->AddLocalizedString("connectivity", IDS_DIAGNOSTICS_CONNECTIVITY_TITLE); source->AddLocalizedString("loading", IDS_DIAGNOSTICS_LOADING); - source->AddLocalizedString("wlan0", IDS_DIAGNOSTICS_ADAPTER_WLAN0); - source->AddLocalizedString("eth0", IDS_DIAGNOSTICS_ADAPTER_ETH0); - source->AddLocalizedString("eth1", IDS_DIAGNOSTICS_ADAPTER_ETH1); - source->AddLocalizedString("wwan0", IDS_DIAGNOSTICS_ADAPTER_WWAN0); + source->AddLocalizedString("wifi", IDS_DIAGNOSTICS_ADAPTER_WIFI); + source->AddLocalizedString("ethernet1", IDS_DIAGNOSTICS_ADAPTER_ETHERNET1); + source->AddLocalizedString("ethernet2", IDS_DIAGNOSTICS_ADAPTER_ETHERNET2); + source->AddLocalizedString("3g", IDS_DIAGNOSTICS_ADAPTER_3G); source->AddLocalizedString("testing-hardware", IDS_DIAGNOSTICS_TESTING_HARDWARE); source->AddLocalizedString("testing-connection-to-router", diff --git a/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc b/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc index 051437391a..cd009b09df 100644 --- a/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc +++ b/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc @@ -52,7 +52,7 @@ namespace { // Gets metadata of all files and directories in |root_path| // recursively. Stores the result as a list of dictionaries like: // -// [{ path: 'GCache/v1/tmp/<resource_id>', +// [{ path: 'GCache/v1/tmp/<local_id>', // size: 12345, // is_directory: false, // last_modified: '2005-08-09T09:57:00-08:00', @@ -130,6 +130,7 @@ std::string FormatEntry(const base::FilePath& path, std::string out; StringAppendF(&out, "%s\n", path.AsUTF8Unsafe().c_str()); StringAppendF(&out, " title: %s\n", entry.title().c_str()); + StringAppendF(&out, " local_id: %s\n", entry.local_id().c_str()); StringAppendF(&out, " resource_id: %s\n", entry.resource_id().c_str()); StringAppendF(&out, " parent_local_id: %s\n", entry.parent_local_id().c_str()); @@ -159,8 +160,6 @@ std::string FormatEntry(const base::FilePath& path, if (entry.has_file_specific_info()) { const drive::FileSpecificInfo& file_specific_info = entry.file_specific_info(); - StringAppendF(&out, " thumbnail_url: %s\n", - file_specific_info.thumbnail_url().c_str()); StringAppendF(&out, " alternate_url: %s\n", file_specific_info.alternate_url().c_str()); StringAppendF(&out, " content_mime_type: %s\n", @@ -260,7 +259,7 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler { scoped_ptr<drive::ResourceEntryVector> entries); // Called as the iterator for DebugInfoCollector::IterateFileCache(). - void UpdateCacheEntry(const std::string& resource_id, + void UpdateCacheEntry(const std::string& local_id, const drive::FileCacheEntry& cache_entry); // Called when GetFreeDiskSpace() is complete. @@ -791,13 +790,13 @@ void DriveInternalsWebUIHandler::OnReadDirectoryByPath( } void DriveInternalsWebUIHandler::UpdateCacheEntry( - const std::string& resource_id, + const std::string& local_id, const drive::FileCacheEntry& cache_entry) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // Convert |cache_entry| into a dictionary. base::DictionaryValue value; - value.SetString("resource_id", resource_id); + value.SetString("local_id", local_id); value.SetString("md5", cache_entry.md5()); value.SetBoolean("is_present", cache_entry.is_present()); value.SetBoolean("is_pinned", cache_entry.is_pinned()); diff --git a/chrome/browser/ui/webui/chromeos/imageburner/imageburner_ui.cc b/chrome/browser/ui/webui/chromeos/imageburner/imageburner_ui.cc index 99125e9e87..08b3146a7e 100644 --- a/chrome/browser/ui/webui/chromeos/imageburner/imageburner_ui.cc +++ b/chrome/browser/ui/webui/chromeos/imageburner/imageburner_ui.cc @@ -31,7 +31,6 @@ namespace { const char kPropertyDevicePath[] = "devicePath"; const char kPropertyFilePath[] = "filePath"; const char kPropertyLabel[] = "label"; -const char kPropertyPath[] = "path"; const char kPropertyDeviceType[] = "type"; // Link displayed on imageburner ui. diff --git a/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc index 70536f75b3..3b256e0a02 100644 --- a/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc +++ b/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc @@ -37,8 +37,6 @@ const char kGaiaExtStartPage[] = // Enrollment step names. const char kEnrollmentStepSignin[] = "signin"; -const char kEnrollmentStepWorking[] = "working"; -const char kEnrollmentStepError[] = "error"; const char kEnrollmentStepSuccess[] = "success"; } // namespace diff --git a/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc index cddd932b63..8724a7b9a1 100644 --- a/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc +++ b/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc @@ -27,22 +27,6 @@ const char kJsScreenPath[] = "login.ErrorMessageScreen"; namespace chromeos { -namespace { - -void EnableLazyDetection() { - NetworkPortalDetector* detector = NetworkPortalDetector::GetInstance(); - if (NetworkPortalDetector::IsEnabledInCommandLine() && detector) - detector->EnableLazyDetection(); -} - -void DisableLazyDetection() { - NetworkPortalDetector* detector = NetworkPortalDetector::GetInstance(); - if (NetworkPortalDetector::IsEnabledInCommandLine() && detector) - detector->DisableLazyDetection(); -} - -} // namespace - ErrorScreenHandler::ErrorScreenHandler( const scoped_refptr<NetworkStateInformer>& network_state_informer) : BaseScreenHandler(kJsScreenPath), @@ -63,7 +47,7 @@ void ErrorScreenHandler::Show(OobeDisplay::Screen parent_screen, parent_screen_ = parent_screen; ShowScreen(OobeUI::kScreenErrorMessage, params); NetworkErrorShown(); - EnableLazyDetection(); + NetworkPortalDetector::Get()->EnableLazyDetection(); LOG(WARNING) << "Offline message is displayed"; } @@ -73,7 +57,7 @@ void ErrorScreenHandler::Hide() { std::string screen_name; if (GetScreenName(parent_screen_, &screen_name)) ShowScreen(screen_name.c_str(), NULL); - DisableLazyDetection(); + NetworkPortalDetector::Get()->DisableLazyDetection(); LOG(WARNING) << "Offline message is hidden"; } diff --git a/chrome/browser/ui/webui/chromeos/login/locally_managed_user_creation_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/locally_managed_user_creation_screen_handler.cc index 92ba6b8624..5af0b3f8ab 100644 --- a/chrome/browser/ui/webui/chromeos/login/locally_managed_user_creation_screen_handler.cc +++ b/chrome/browser/ui/webui/chromeos/login/locally_managed_user_creation_screen_handler.cc @@ -7,6 +7,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/chromeos/login/managed/locally_managed_user_creation_flow.h" +#include "chrome/browser/chromeos/login/supervised_user_manager.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/login/wallpaper_manager.h" #include "chrome/browser/chromeos/settings/cros_settings.h" @@ -19,16 +20,8 @@ #include "net/base/escape.h" #include "ui/base/l10n/l10n_util.h" -namespace { - const char kJsScreenPath[] = "login.LocallyManagedUserCreationScreen"; -// Locally managed user creation screen id. -const char kLocallyManagedUserCreationScreen[] = - "locally-managed-user-creation"; - -} // namespace - namespace chromeos { LocallyManagedUserCreationScreenHandler:: @@ -115,6 +108,21 @@ void LocallyManagedUserCreationScreenHandler::DeclareLocalizedValues( builder->Add("createManagedUserCreatedText3", IDS_CREATE_LOCALLY_MANAGED_USER_CREATED_1_TEXT_3); + builder->Add("importExistingSupervisedUserTitle", + IDS_IMPORT_EXISTING_MANAGED_USER_TITLE); + builder->Add("importExistingSupervisedUserText", + IDS_IMPORT_EXISTING_MANAGED_USER_TEXT); + builder->Add("managedUserCreationFlowImportButtonTitle", + IDS_IMPORT_EXISTING_MANAGED_USER_OK); + builder->Add("importSupervisedUserLink", + IDS_PROFILES_IMPORT_EXISTING_MANAGED_USER_LINK); + builder->Add("createSupervisedUserLink", + IDS_CREATE_NEW_USER_LINK); + builder->Add("importBubbleText", IDS_SUPERVISED_USER_IMPORT_BUBBLE_TEXT); + builder->Add("importUserExists", IDS_SUPERVISED_USER_IMPORT_USER_EXIST); + builder->Add("importUsernameExists", + IDS_SUPERVISED_USER_IMPORT_USERNAME_EXIST); + builder->Add("managementURL", chrome::kSupervisedUserManagementDisplayURL); // TODO(antrim) : this is an explicit code duplications with UserImageScreen. @@ -145,6 +153,13 @@ void LocallyManagedUserCreationScreenHandler::RegisterMessages() { AddCallback("managerSelectedOnLocallyManagedUserCreationFlow", &LocallyManagedUserCreationScreenHandler:: HandleManagerSelected); + AddCallback("userSelectedForImportInManagedUserCreationFlow", + &LocallyManagedUserCreationScreenHandler:: + HandleImportUserSelected); + AddCallback("importSupervisedUser", + &LocallyManagedUserCreationScreenHandler:: + HandleImportSupervisedUser); + // TODO(antrim) : this is an explicit code duplications with UserImageScreen. // It should be removed by issue 251179. @@ -244,10 +259,17 @@ void LocallyManagedUserCreationScreenHandler::HandleManagerSelected( WallpaperManager::Get()->SetUserWallpaper(manager_id); } +void LocallyManagedUserCreationScreenHandler::HandleImportUserSelected( + const std::string& user_id) { + if (!delegate_) + return; +} + void LocallyManagedUserCreationScreenHandler::HandleCheckLocallyManagedUserName( const string16& name) { - if (NULL != UserManager::Get()-> - FindLocallyManagedUser(CollapseWhitespace(name, true))) { + std::string user_id; + if (NULL != UserManager::Get()->GetSupervisedUserManager()-> + FindByDisplayName(CollapseWhitespace(name, true))) { CallJS("managedUserNameError", name, l10n_util::GetStringUTF16( IDS_CREATE_LOCALLY_MANAGED_USER_CREATE_USERNAME_ALREADY_EXISTS)); @@ -255,6 +277,9 @@ void LocallyManagedUserCreationScreenHandler::HandleCheckLocallyManagedUserName( CallJS("managedUserNameError", name, l10n_util::GetStringUTF16( IDS_CREATE_LOCALLY_MANAGED_USER_CREATE_ILLEGAL_USERNAME)); + } else if (delegate_ && delegate_->FindUserByDisplayName( + CollapseWhitespace(name, true), &user_id)) { + CallJS("managedUserSuggestImport", name, user_id); } else { CallJS("managedUserNameOk", name); } @@ -266,7 +291,8 @@ void LocallyManagedUserCreationScreenHandler::HandleCreateManagedUser( if (!delegate_) return; const string16 new_user_name = CollapseWhitespace(new_raw_user_name, true); - if (NULL != UserManager::Get()->FindLocallyManagedUser(new_user_name)) { + if (NULL != UserManager::Get()->GetSupervisedUserManager()-> + FindByDisplayName(new_user_name)) { CallJS("managedUserNameError", new_user_name, l10n_util::GetStringFUTF16( IDS_CREATE_LOCALLY_MANAGED_USER_CREATE_USERNAME_ALREADY_EXISTS, @@ -293,6 +319,17 @@ void LocallyManagedUserCreationScreenHandler::HandleCreateManagedUser( delegate_->CreateManagedUser(new_user_name, new_user_password); } +void LocallyManagedUserCreationScreenHandler::HandleImportSupervisedUser( + const std::string& user_id) { + if (!delegate_) + return; + + ShowStatusMessage(true /* progress */, l10n_util::GetStringUTF16( + IDS_CREATE_LOCALLY_MANAGED_USER_CREATION_CREATION_PROGRESS_MESSAGE)); + + delegate_->ImportManagedUser(user_id); +} + void LocallyManagedUserCreationScreenHandler::HandleAuthenticateManager( const std::string& raw_manager_username, const std::string& manager_password) { @@ -361,4 +398,9 @@ void LocallyManagedUserCreationScreenHandler::SetCameraPresent(bool present) { CallJS("setCameraPresent", present); } +void LocallyManagedUserCreationScreenHandler::ShowExistingManagedUsers( + const base::ListValue* users) { + CallJS("setExistingManagedUsers", *users); +} + } // namespace chromeos diff --git a/chrome/browser/ui/webui/chromeos/login/locally_managed_user_creation_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/locally_managed_user_creation_screen_handler.h index 2d0bf73d62..f39c93e577 100644 --- a/chrome/browser/ui/webui/chromeos/login/locally_managed_user_creation_screen_handler.h +++ b/chrome/browser/ui/webui/chromeos/login/locally_managed_user_creation_screen_handler.h @@ -14,7 +14,7 @@ #include "content/public/browser/web_ui.h" namespace base { -class DictionaryValue; +class ListValue; } namespace chromeos { @@ -35,13 +35,21 @@ class LocallyManagedUserCreationScreenHandler : public BaseScreenHandler { virtual void AuthenticateManager(const std::string& manager_id, const std::string& manager_password) = 0; - // Starts managed user creation flow, with manager identified by - // |manager_id| and |manager_password|, and locally managed user that would - // have |display_name| and authenticated by the |managed_user_password|. + // Starts managed user creation flow, with supervised user that would have + // |display_name| and authenticated by the |managed_user_password|. virtual void CreateManagedUser( const string16& display_name, const std::string& managed_user_password) = 0; + // Look up if user with name |display_name| already exist and can be + // imported. Returns user ID in |out_id|. Returns true if user was found, + // false otherwise. + virtual bool FindUserByDisplayName(const string16& display_name, + std::string *out_id) const = 0; + + // Starts managed user import flow for user identified with |user_id|. + virtual void ImportManagedUser(const std::string& user_id) = 0; + virtual void AbortFlow() = 0; virtual void FinishFlow() = 0; @@ -81,6 +89,8 @@ class LocallyManagedUserCreationScreenHandler : public BaseScreenHandler { void SetCameraPresent(bool enabled); + void ShowExistingManagedUsers(const base::ListValue* users); + // BaseScreenHandler implementation: virtual void DeclareLocalizedValues(LocalizedValuesBuilder* builder) OVERRIDE; virtual void Initialize() OVERRIDE; @@ -93,6 +103,7 @@ class LocallyManagedUserCreationScreenHandler : public BaseScreenHandler { void HandleCheckLocallyManagedUserName(const string16& name); void HandleManagerSelected(const std::string& manager_id); + void HandleImportUserSelected(const std::string& user_id); void HandleFinishLocalManagedUserCreation(); void HandleAbortLocalManagedUserCreation(); @@ -103,6 +114,7 @@ class LocallyManagedUserCreationScreenHandler : public BaseScreenHandler { const std::string& manager_password); void HandleCreateManagedUser(const string16& new_raw_user_name, const std::string& new_user_password); + void HandleImportSupervisedUser(const std::string& user_id); void HandleGetImages(); void HandlePhotoTaken(const std::string& image_url); diff --git a/chrome/browser/ui/webui/chromeos/login/network_state_informer.cc b/chrome/browser/ui/webui/chromeos/login/network_state_informer.cc index a7381db48e..115f5451bc 100644 --- a/chrome/browser/ui/webui/chromeos/login/network_state_informer.cc +++ b/chrome/browser/ui/webui/chromeos/login/network_state_informer.cc @@ -41,10 +41,9 @@ NetworkStateInformer::State GetStateForDefaultNetwork() { if (!network) return NetworkStateInformer::OFFLINE; - if (NetworkPortalDetector::IsEnabledInCommandLine() && - NetworkPortalDetector::GetInstance()) { + if (NetworkPortalDetector::Get()->IsEnabled()) { NetworkPortalDetector::CaptivePortalState state = - NetworkPortalDetector::GetInstance()->GetCaptivePortalState(network); + NetworkPortalDetector::Get()->GetCaptivePortalState(network); NetworkPortalDetector::CaptivePortalStatus status = state.status; if (status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN && NetworkState::StateIsConnecting(network->connection_state())) { @@ -90,10 +89,7 @@ NetworkStateInformer::~NetworkStateInformer() { NetworkHandler::Get()->network_state_handler()->RemoveObserver( this, FROM_HERE); } - if (NetworkPortalDetector::IsEnabledInCommandLine() && - NetworkPortalDetector::GetInstance()) { - NetworkPortalDetector::GetInstance()->RemoveObserver(this); - } + NetworkPortalDetector::Get()->RemoveObserver(this); } void NetworkStateInformer::Init() { @@ -101,10 +97,7 @@ void NetworkStateInformer::Init() { NetworkHandler::Get()->network_state_handler()->AddObserver( this, FROM_HERE); - if (NetworkPortalDetector::IsEnabledInCommandLine() && - NetworkPortalDetector::GetInstance()) { - NetworkPortalDetector::GetInstance()->AddAndFireObserver(this); - } + NetworkPortalDetector::Get()->AddAndFireObserver(this); registrar_.Add(this, chrome::NOTIFICATION_LOGIN_PROXY_CHANGED, diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc index 9a85f0a86f..1d65c13cd7 100644 --- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc +++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc @@ -240,7 +240,7 @@ std::string GetNetworkName(const std::string& service_path) { // Returns captive portal state for a network by its service path. NetworkPortalDetector::CaptivePortalState GetCaptivePortalState( const std::string& service_path) { - NetworkPortalDetector* detector = NetworkPortalDetector::GetInstance(); + NetworkPortalDetector* detector = NetworkPortalDetector::Get(); const NetworkState* network = NetworkHandler::Get()->network_state_handler()-> GetNetworkState(service_path); if (!detector || !network) diff --git a/chrome/browser/ui/webui/chromeos/mobile_setup_ui.cc b/chrome/browser/ui/webui/chromeos/mobile_setup_ui.cc index 6a8bf23ca3..c9bc8d7338 100644 --- a/chrome/browser/ui/webui/chromeos/mobile_setup_ui.cc +++ b/chrome/browser/ui/webui/chromeos/mobile_setup_ui.cc @@ -33,7 +33,6 @@ #include "chromeos/network/network_state_handler.h" #include "chromeos/network/network_state_handler_observer.h" #include "content/public/browser/browser_thread.h" -#include "content/public/browser/render_view_host_observer.h" #include "content/public/browser/url_data_source.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_ui.h" @@ -132,51 +131,6 @@ void GetDeviceInfo(const DictionaryValue& properties, DictionaryValue* value) { } // namespace -// Observes IPC messages from the rederer and notifies JS if frame loading error -// appears. -class PortalFrameLoadObserver : public content::RenderViewHostObserver { - public: - PortalFrameLoadObserver(const base::WeakPtr<MobileSetupUI>& parent, - RenderViewHost* host) - : content::RenderViewHostObserver(host), parent_(parent) { - Send(new ChromeViewMsg_StartFrameSniffer(routing_id(), - UTF8ToUTF16("paymentForm"))); - } - - // IPC::Listener implementation. - virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(PortalFrameLoadObserver, message) - IPC_MESSAGE_HANDLER(ChromeViewHostMsg_FrameLoadingError, OnFrameLoadError) - IPC_MESSAGE_HANDLER(ChromeViewHostMsg_FrameLoadingCompleted, - OnFrameLoadCompleted) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; - } - - private: - void OnFrameLoadError(int error) { - if (!parent_.get()) - return; - - base::FundamentalValue result_value(error); - parent_->web_ui()->CallJavascriptFunction(kJsPortalFrameLoadFailedCallback, - result_value); - } - - void OnFrameLoadCompleted() { - if (!parent_.get()) - return; - - parent_->web_ui()->CallJavascriptFunction( - kJsPortalFrameLoadCompletedCallback); - } - - base::WeakPtr<MobileSetupUI> parent_; - DISALLOW_COPY_AND_ASSIGN(PortalFrameLoadObserver); -}; - class MobileSetupUIHTMLSource : public content::URLDataSource { public: MobileSetupUIHTMLSource(); @@ -661,9 +615,36 @@ MobileSetupUI::MobileSetupUI(content::WebUI* web_ui) // Set up the chrome://mobilesetup/ source. Profile* profile = Profile::FromWebUI(web_ui); content::URLDataSource::Add(profile, html_source); + + content::WebContentsObserver::Observe(web_ui->GetWebContents()); +} + +void MobileSetupUI::DidCommitProvisionalLoadForFrame( + int64 frame_id, + const string16& frame_unique_name, + bool is_main_frame, + const GURL& url, + content::PageTransition transition_type, + content::RenderViewHost* render_view_host) { + if (frame_unique_name != UTF8ToUTF16("paymentForm")) + return; + + web_ui()->CallJavascriptFunction( + kJsPortalFrameLoadCompletedCallback); } -void MobileSetupUI::RenderViewCreated(RenderViewHost* host) { - // Destroyed by the corresponding RenderViewHost - new PortalFrameLoadObserver(AsWeakPtr(), host); +void MobileSetupUI::DidFailProvisionalLoad( + int64 frame_id, + const string16& frame_unique_name, + bool is_main_frame, + const GURL& validated_url, + int error_code, + const string16& error_description, + content::RenderViewHost* render_view_host) { + if (frame_unique_name != UTF8ToUTF16("paymentForm")) + return; + + base::FundamentalValue result_value(-error_code); + web_ui()->CallJavascriptFunction(kJsPortalFrameLoadFailedCallback, + result_value); } diff --git a/chrome/browser/ui/webui/chromeos/mobile_setup_ui.h b/chrome/browser/ui/webui/chromeos/mobile_setup_ui.h index b16ce7e16d..336d2fd993 100644 --- a/chrome/browser/ui/webui/chromeos/mobile_setup_ui.h +++ b/chrome/browser/ui/webui/chromeos/mobile_setup_ui.h @@ -6,18 +6,33 @@ #define CHROME_BROWSER_UI_WEBUI_CHROMEOS_MOBILE_SETUP_UI_H_ #include "base/memory/weak_ptr.h" +#include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_ui_controller.h" // A custom WebUI that defines datasource for mobile setup registration page // that is used in Chrome OS activate modem and perform plan subscription tasks. class MobileSetupUI : public content::WebUIController, + public content::WebContentsObserver, public base::SupportsWeakPtr<MobileSetupUI> { public: explicit MobileSetupUI(content::WebUI* web_ui); private: - // WebUIController overrides. - virtual void RenderViewCreated( + // content::WebContentsObserver overrides. + virtual void DidCommitProvisionalLoadForFrame( + int64 frame_id, + const string16& frame_unique_name, + bool is_main_frame, + const GURL& url, + content::PageTransition transition_type, + content::RenderViewHost* render_view_host) OVERRIDE; + virtual void DidFailProvisionalLoad( + int64 frame_id, + const string16& frame_unique_name, + bool is_main_frame, + const GURL& validated_url, + int error_code, + const string16& error_description, content::RenderViewHost* render_view_host) OVERRIDE; DISALLOW_COPY_AND_ASSIGN(MobileSetupUI); diff --git a/chrome/browser/ui/webui/chromeos/network_ui.cc b/chrome/browser/ui/webui/chromeos/network_ui.cc index 4478131e26..42c391f250 100644 --- a/chrome/browser/ui/webui/chromeos/network_ui.cc +++ b/chrome/browser/ui/webui/chromeos/network_ui.cc @@ -30,7 +30,6 @@ const char kRequestNetworkInfoCallback[] = "requestNetworkInfo"; const char kNetworkEventLogTag[] = "networkEventLog"; const char kNetworkStateTag[] = "networkState"; const char kOnNetworkInfoReceivedFunction[] = "NetworkUI.onNetworkInfoReceived"; -const char kLevelDebugTag[] = "Debug"; class NetworkMessageHandler : public content::WebUIMessageHandler { public: diff --git a/chrome/browser/ui/webui/chromeos/sim_unlock_ui.cc b/chrome/browser/ui/webui/chromeos/sim_unlock_ui.cc index e46177aed4..3a804010a3 100644 --- a/chrome/browser/ui/webui/chromeos/sim_unlock_ui.cc +++ b/chrome/browser/ui/webui/chromeos/sim_unlock_ui.cc @@ -64,7 +64,6 @@ const char kTriesLeft[] = "tries"; // Error constants, passed to the page. const char kErrorPin[] = "incorrectPin"; -const char kErrorPuk[] = "incorrectPuk"; const char kErrorOk[] = "ok"; chromeos::NetworkDeviceHandler* GetNetworkDeviceHandler() { diff --git a/chrome/browser/ui/webui/devtools_ui.cc b/chrome/browser/ui/webui/devtools_ui.cc index 0a71d19a72..f75527214f 100644 --- a/chrome/browser/ui/webui/devtools_ui.cc +++ b/chrome/browser/ui/webui/devtools_ui.cc @@ -40,10 +40,15 @@ const char kHttpNotFound[] = "HTTP/1.1 404 Not Found\n\n"; #if defined(DEBUG_DEVTOOLS) // Local frontend url provided by InspectUI. -const char kLocalFrontendURL[] = +const char kFallbackFrontendURL[] = "chrome-devtools://devtools/bundled/devtools.html"; +#else +// URL causing the DevTools window to display a plain text warning. +const char kFallbackFrontendURL[] = + "data:text/plain,Cannot load DevTools frontend from an untrusted origin"; #endif // defined(DEBUG_DEVTOOLS) + class FetchRequest : public net::URLFetcherDelegate { public: FetchRequest(net::URLRequestContextGetter* request_context, @@ -190,13 +195,9 @@ class DevToolsDataSource : public content::URLDataSource { // static GURL DevToolsUI::GetProxyURL(const std::string& frontend_url) { GURL url(frontend_url); -#if defined(DEBUG_DEVTOOLS) if (!url.is_valid() || url.host() != kRemoteFrontendDomain) { - return GURL(kLocalFrontendURL); + return GURL(kFallbackFrontendURL); } -#endif // defined(DEBUG_DEVTOOLS) - CHECK(url.is_valid()); - CHECK_EQ(url.host(), kRemoteFrontendDomain); return GURL(base::StringPrintf("%s://%s/%s/%s", chrome::kChromeDevToolsScheme, chrome::kChromeUIDevToolsHost, diff --git a/chrome/browser/ui/webui/downloads_dom_handler.cc b/chrome/browser/ui/webui/downloads_dom_handler.cc index 562856f646..f964d84566 100644 --- a/chrome/browser/ui/webui/downloads_dom_handler.cc +++ b/chrome/browser/ui/webui/downloads_dom_handler.cc @@ -24,7 +24,6 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/download/download_crx_util.h" #include "chrome/browser/download/download_danger_prompt.h" -#include "chrome/browser/download/download_field_trial.h" #include "chrome/browser/download/download_history.h" #include "chrome/browser/download/download_item_model.h" #include "chrome/browser/download/download_prefs.h" @@ -182,29 +181,14 @@ DictionaryValue* CreateDownloadItemValue( content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST || download_item->GetDangerType() == content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED); - std::string trial_condition = - base::FieldTrialList::FindFullName(kMalwareWarningFinchTrialName); const char* danger_type_value = GetDangerTypeString(download_item->GetDangerType()); file_value->SetString("danger_type", danger_type_value); - if (!trial_condition.empty()) { - base::string16 finch_string; - content::DownloadDangerType danger_type = - download_item->GetDangerType(); - if (danger_type == content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL || - danger_type == content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT || - danger_type == content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST) { - finch_string = - AssembleMalwareFinchString(trial_condition, file_name); - } - file_value->SetString("finch_string", finch_string); - } } else if (download_item->IsPaused()) { file_value->SetString("state", "PAUSED"); } else { file_value->SetString("state", "IN_PROGRESS"); } - file_value->SetString("progress_status_text", download_model.GetTabProgressStatusText()); diff --git a/chrome/browser/ui/webui/downloads_ui.cc b/chrome/browser/ui/webui/downloads_ui.cc index 6b71ca3fe5..5a88c51d1a 100644 --- a/chrome/browser/ui/webui/downloads_ui.cc +++ b/chrome/browser/ui/webui/downloads_ui.cc @@ -63,6 +63,7 @@ content::WebUIDataSource* CreateDownloadsUIHTMLSource(Profile* profile) { source->AddLocalizedString("danger_potentially_unwanted_desc", IDS_PROMPT_DOWNLOAD_CHANGES_SEARCH_SETTINGS); source->AddLocalizedString("danger_save", IDS_CONFIRM_DOWNLOAD); + source->AddLocalizedString("danger_restore", IDS_CONFIRM_DOWNLOAD_RESTORE); source->AddLocalizedString("danger_discard", IDS_DISCARD_DOWNLOAD); // Controls. diff --git a/chrome/browser/ui/webui/downloads_ui_browsertest.cc b/chrome/browser/ui/webui/downloads_ui_browsertest.cc index 1abeb5ecaf..41958640a3 100644 --- a/chrome/browser/ui/webui/downloads_ui_browsertest.cc +++ b/chrome/browser/ui/webui/downloads_ui_browsertest.cc @@ -4,27 +4,26 @@ #include "chrome/browser/ui/webui/downloads_ui_browsertest.h" +#include "base/command_line.h" #include "base/prefs/pref_service.h" #include "chrome/browser/managed_mode/managed_user_service.h" #include "chrome/browser/managed_mode/managed_user_service_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" +#include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" #include "content/public/test/test_utils.h" -DownloadsUIBrowserTest::DownloadsUIBrowserTest() { -} +DownloadsUIBrowserTest::DownloadsUIBrowserTest() {} -DownloadsUIBrowserTest::~DownloadsUIBrowserTest() { -} +DownloadsUIBrowserTest::~DownloadsUIBrowserTest() {} void DownloadsUIBrowserTest::SetDeleteAllowed(bool allowed) { browser()->profile()->GetPrefs()-> SetBoolean(prefs::kAllowDeletingBrowserHistory, allowed); } -void DownloadsUIBrowserTest::ChangeProfileToSupervised() { - ManagedUserServiceFactory::GetForProfile( - browser()->profile())->InitForTesting(); - content::RunAllPendingInMessageLoop(); +void DownloadsWebUIForSupervisedUsersTest::SetUpCommandLine( + CommandLine* command_line) { + command_line->AppendSwitch(switches::kNewProfileIsSupervised); } diff --git a/chrome/browser/ui/webui/downloads_ui_browsertest.h b/chrome/browser/ui/webui/downloads_ui_browsertest.h index f5370c8392..d38ee1e9cb 100644 --- a/chrome/browser/ui/webui/downloads_ui_browsertest.h +++ b/chrome/browser/ui/webui/downloads_ui_browsertest.h @@ -17,10 +17,14 @@ class DownloadsUIBrowserTest : public WebUIBrowserTest { // Sets the pref to allow or prohibit deleting history entries. void SetDeleteAllowed(bool allowed); - void ChangeProfileToSupervised(); - private: DISALLOW_COPY_AND_ASSIGN(DownloadsUIBrowserTest); }; +class DownloadsWebUIForSupervisedUsersTest : public DownloadsUIBrowserTest { + public: + // InProcessBrowserTest overrides: + virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE; +}; + #endif // CHROME_BROWSER_UI_WEBUI_DOWNLOADS_UI_BROWSERTEST_H_ diff --git a/chrome/browser/ui/webui/downloads_ui_browsertest.js b/chrome/browser/ui/webui/downloads_ui_browsertest.js index c55ca6f614..f4e43c82f4 100644 --- a/chrome/browser/ui/webui/downloads_ui_browsertest.js +++ b/chrome/browser/ui/webui/downloads_ui_browsertest.js @@ -171,9 +171,7 @@ DownloadsWebUIForSupervisedUsersTest.prototype = { __proto__: BaseDownloadsWebUITest.prototype, /** @override */ - testGenPreamble: function() { - GEN(' ChangeProfileToSupervised();'); - }, + typedefCppFixture: 'DownloadsWebUIForSupervisedUsersTest', }; // Test UI for supervised users, removing entries should be disabled diff --git a/chrome/browser/ui/webui/extensions/command_handler.cc b/chrome/browser/ui/webui/extensions/command_handler.cc index f89634c501..6086dc0679 100644 --- a/chrome/browser/ui/webui/extensions/command_handler.cc +++ b/chrome/browser/ui/webui/extensions/command_handler.cc @@ -37,6 +37,12 @@ void CommandHandler::GetLocalizedValues(content::WebUIDataSource* source) { l10n_util::GetStringUTF16(IDS_EXTENSION_TYPE_SHORTCUT)); source->AddString("extensionCommandsDelete", l10n_util::GetStringUTF16(IDS_EXTENSION_DELETE_SHORTCUT)); + source->AddString("extensionCommandsGlobalTooltip", + l10n_util::GetStringUTF16(IDS_EXTENSION_COMMANDS_GLOBAL)); + source->AddString("extensionCommandsNotGlobalTooltip", + l10n_util::GetStringUTF16(IDS_EXTENSION_COMMANDS_NOT_GLOBAL)); + source->AddString("extensionCommandsNotGlobalPermanentTooltip", + l10n_util::GetStringUTF16(IDS_EXTENSION_COMMANDS_NOT_GLOBAL_PERMANENT)); source->AddString("ok", l10n_util::GetStringUTF16(IDS_OK)); } @@ -55,6 +61,9 @@ void CommandHandler::RegisterMessages() { web_ui()->RegisterMessageCallback("setExtensionCommandShortcut", base::Bind(&CommandHandler::HandleSetExtensionCommandShortcut, base::Unretained(this))); + web_ui()->RegisterMessageCallback("toggleCommandScope", + base::Bind(&CommandHandler::HandleToggleCommandScope, + base::Unretained(this))); } void CommandHandler::Observe( @@ -96,6 +105,23 @@ void CommandHandler::HandleSetExtensionCommandShortcut( UpdateCommandDataOnPage(); } +void CommandHandler::HandleToggleCommandScope( + const base::ListValue* args) { + std::string extension_id; + std::string command_name; + if (!args->GetString(0, &extension_id) || + !args->GetString(1, &command_name)) { + NOTREACHED(); + return; + } + + Profile* profile = Profile::FromWebUI(web_ui()); + CommandService* command_service = CommandService::Get(profile); + command_service->ToggleScope(extension_id, command_name); + + UpdateCommandDataOnPage(); +} + void CommandHandler::HandleSetShortcutHandlingSuspended(const ListValue* args) { bool suspended; if (args->GetBoolean(0, &suspended)) @@ -153,9 +179,10 @@ void CommandHandler::GetAllCommands(base::DictionaryValue* commands) { &named_commands)) { for (extensions::CommandMap::const_iterator iter = named_commands.begin(); iter != named_commands.end(); ++iter) { - ui::Accelerator shortcut_assigned = - command_service->FindShortcutForCommand( + extensions::Command command = command_service->FindCommandByName( (*extension)->id(), iter->second.command_name()); + ui::Accelerator shortcut_assigned = command.accelerator(); + active = (shortcut_assigned.key_code() != ui::VKEY_UNKNOWN); extensions_list->Append( diff --git a/chrome/browser/ui/webui/extensions/command_handler.h b/chrome/browser/ui/webui/extensions/command_handler.h index 0b43f1a2df..69f869a5bb 100644 --- a/chrome/browser/ui/webui/extensions/command_handler.h +++ b/chrome/browser/ui/webui/extensions/command_handler.h @@ -60,6 +60,10 @@ class CommandHandler : public content::WebUIMessageHandler, // for a given extension command. void HandleSetExtensionCommandShortcut(const base::ListValue* args); + // Handles requests from javascript to change the scope of a particular + // keyboard shortcut for a given extension command. + void HandleToggleCommandScope(const base::ListValue* args); + // Handles requests from javascript to temporarily disable general Chrome // shortcut handling while the web page is capturing which shortcut to use. void HandleSetShortcutHandlingSuspended(const base::ListValue* args); diff --git a/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc b/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc new file mode 100644 index 0000000000..612d8fbe6a --- /dev/null +++ b/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc @@ -0,0 +1,130 @@ +// Copyright 2013 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/ui/webui/extensions/extension_settings_browsertest.h" + +#include "base/files/file_path.h" +#include "base/path_service.h" +#include "base/strings/string_number_conversions.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/extensions/crx_installer.h" +#include "chrome/browser/extensions/extension_error_reporter.h" +#include "chrome/browser/extensions/extension_install_prompt.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/unpacked_installer.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/common/chrome_paths.h" +#include "content/public/browser/notification_registrar.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/test/browser_test_utils.h" +#include "content/public/test/test_utils.h" + +using extensions::Extension; + +ExtensionSettingsUIBrowserTest::ExtensionSettingsUIBrowserTest() + : profile_(NULL) {} + +ExtensionSettingsUIBrowserTest::~ExtensionSettingsUIBrowserTest() {} + +Profile* ExtensionSettingsUIBrowserTest::GetProfile() { + if (!profile_) { + profile_ = + browser() ? browser()->profile() : ProfileManager::GetDefaultProfile(); + } + return profile_; +} + +void ExtensionSettingsUIBrowserTest::SetUpOnMainThread() { + WebUIBrowserTest::SetUpOnMainThread(); + observer_.reset(new ExtensionTestNotificationObserver(browser())); +} + +void ExtensionSettingsUIBrowserTest::InstallGoodExtension() { + base::FilePath test_data_dir; + if (!PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir)) { + ADD_FAILURE(); + return; + } + base::FilePath extensions_data_dir = test_data_dir.AppendASCII("extensions"); + InstallExtension(extensions_data_dir.AppendASCII("good.crx")); +} + +class MockAutoConfirmExtensionInstallPrompt : public ExtensionInstallPrompt { + public: + explicit MockAutoConfirmExtensionInstallPrompt( + content::WebContents* web_contents) + : ExtensionInstallPrompt(web_contents) {} + + // Proceed without confirmation prompt. + virtual void ConfirmInstall( + Delegate* delegate, + const Extension* extension, + const ShowDialogCallback& show_dialog_callback) OVERRIDE { + delegate->InstallUIProceed(); + } +}; + +const Extension* ExtensionSettingsUIBrowserTest::InstallExtension( + const base::FilePath& path) { + Profile* profile = this->GetProfile(); + ExtensionService* service = profile->GetExtensionService(); + service->set_show_extensions_prompts(false); + size_t num_before = service->extensions()->size(); + { + scoped_ptr<ExtensionInstallPrompt> install_ui; + install_ui.reset(new MockAutoConfirmExtensionInstallPrompt( + browser()->tab_strip_model()->GetActiveWebContents())); + + base::FilePath crx_path = path; + DCHECK(crx_path.Extension() == FILE_PATH_LITERAL(".crx")); + if (crx_path.empty()) + return NULL; + + scoped_refptr<extensions::CrxInstaller> installer( + extensions::CrxInstaller::Create(service, install_ui.Pass())); + installer->set_expected_id(std::string()); + installer->set_is_gallery_install(false); + installer->set_install_source(extensions::Manifest::INTERNAL); + installer->set_install_wait_for_idle(false); + installer->set_off_store_install_allow_reason( + extensions::CrxInstaller::OffStoreInstallAllowedInTest); + + observer_->Watch( + chrome::NOTIFICATION_CRX_INSTALLER_DONE, + content::Source<extensions::CrxInstaller>(installer.get())); + + installer->InstallCrx(crx_path); + + observer_->Wait(); + } + + size_t num_after = service->extensions()->size(); + if (num_before + 1 != num_after) { + VLOG(1) << "Num extensions before: " << base::IntToString(num_before) + << " num after: " << base::IntToString(num_after) + << " Installed extensions follow:"; + + for (ExtensionSet::const_iterator it = service->extensions()->begin(); + it != service->extensions()->end(); ++it) + VLOG(1) << " " << (*it)->id(); + + VLOG(1) << "Errors follow:"; + const std::vector<string16>* errors = + ExtensionErrorReporter::GetInstance()->GetErrors(); + for (std::vector<string16>::const_iterator iter = errors->begin(); + iter != errors->end(); ++iter) + VLOG(1) << *iter; + + return NULL; + } + + if (!observer_->WaitForExtensionViewsToLoad()) + return NULL; + return service->GetExtensionById(last_loaded_extension_id(), false); +} diff --git a/chrome/browser/ui/webui/extensions/extension_settings_browsertest.h b/chrome/browser/ui/webui/extensions/extension_settings_browsertest.h new file mode 100644 index 0000000000..14edcd58d4 --- /dev/null +++ b/chrome/browser/ui/webui/extensions/extension_settings_browsertest.h @@ -0,0 +1,46 @@ +// Copyright 2013 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_UI_WEBUI_EXTENSIONS_EXTENSION_SETTINGS_BROWSERTEST_H_ +#define CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_SETTINGS_BROWSERTEST_H_ + +#include "chrome/browser/extensions/extension_test_notification_observer.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/test/base/web_ui_browsertest.h" + +class Profile; + +// C++ test fixture used by extension_settings_browsertest.js. +class ExtensionSettingsUIBrowserTest : public WebUIBrowserTest { + public: + ExtensionSettingsUIBrowserTest(); + virtual ~ExtensionSettingsUIBrowserTest(); + + protected: + // Get the profile to use. + Profile* GetProfile(); + + const std::string& last_loaded_extension_id() { + return observer_->last_loaded_extension_id(); + } + + virtual void SetUpOnMainThread() OVERRIDE; + + void InstallGoodExtension(); + + private: + bool WaitForExtensionViewsToLoad(); + const extensions::Extension* LoadUnpackedExtension( + const base::FilePath& path); + const extensions::Extension* InstallExtension(const base::FilePath& path); + + scoped_ptr<ExtensionTestNotificationObserver> observer_; + + // The default profile to be used. + Profile* profile_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionSettingsUIBrowserTest); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_SETTINGS_BROWSERTEST_H_ diff --git a/chrome/browser/ui/webui/extensions/extension_settings_browsertest.js b/chrome/browser/ui/webui/extensions/extension_settings_browsertest.js index 8b403198fd..2324d6df02 100644 --- a/chrome/browser/ui/webui/extensions/extension_settings_browsertest.js +++ b/chrome/browser/ui/webui/extensions/extension_settings_browsertest.js @@ -4,6 +4,16 @@ // TODO(dbeam): test for loading upacked extensions? +GEN('#include "chrome/browser/ui/webui/extensions/' + + 'extension_settings_browsertest.h"'); + +/** + * Test C++ fixture for settings WebUI testing. + * @constructor + * @extends {testing.Test} + */ +function ExtensionSettingsUIBrowserTest() {} + /** * TestFixture for extension settings WebUI testing. * @extends {testing.Test} @@ -14,12 +24,29 @@ function ExtensionSettingsWebUITest() {} ExtensionSettingsWebUITest.prototype = { __proto__: testing.Test.prototype, + accessibilityIssuesAreErrors: true, + + /** @override */ + setUp: function() { + // TODO(aboxhall): remove these when crbug.com/267035 is closed. + this.accessibilityAuditConfig.ignoreSelectors( + 'lowContrastElements', + '.enable-checkbox input:disabled + .enable-checkbox-text > *'); + this.accessibilityAuditConfig.ignoreSelectors( + 'lowContrastElements', '.extension-description > *'); + this.accessibilityAuditConfig.ignoreSelectors( + 'lowContrastElements', '.location-text'); + }, + /** * A URL to load before starting each test. * @type {string} * @const */ browsePreload: 'chrome://extensions-frame/', + + /** @override */ + typedefCppFixture: 'ExtensionSettingsUIBrowserTest', }; TEST_F('ExtensionSettingsWebUITest', 'testChromeSendHandled', function() { @@ -43,6 +70,8 @@ function ExtensionSettingsCommandsConfigWebUITest() {} ExtensionSettingsCommandsConfigWebUITest.prototype = { __proto__: testing.Test.prototype, + accessibilityIssuesAreErrors: true, + /** * A URL to load before starting each test. * @type {string} @@ -57,3 +86,23 @@ TEST_F('ExtensionSettingsCommandsConfigWebUITest', 'testChromeSendHandler', assertEquals(this.browsePreload, document.location.href); assertTrue($('extension-commands-overlay').classList.contains('showing')); }); + +function ExtensionSettingsWebUITestWithExtensionInstalled() {} + +ExtensionSettingsWebUITestWithExtensionInstalled.prototype = { + __proto__: ExtensionSettingsWebUITest.prototype, + + /** @override */ + typedefCppFixture: 'ExtensionSettingsUIBrowserTest', + + /** @override */ + testGenPreamble: function() { + GEN(' InstallGoodExtension();'); + } +} + +TEST_F('ExtensionSettingsWebUITestWithExtensionInstalled', + 'baseAccessibilityIsOk', function() { + assertEquals(this.browsePreload, document.location.href); + this.runAccessibilityAudit(); +}); diff --git a/chrome/browser/ui/webui/extensions/extension_settings_handler.cc b/chrome/browser/ui/webui/extensions/extension_settings_handler.cc index 6fea215761..403ee9e8e9 100644 --- a/chrome/browser/ui/webui/extensions/extension_settings_handler.cc +++ b/chrome/browser/ui/webui/extensions/extension_settings_handler.cc @@ -37,6 +37,7 @@ #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/extensions/extension_tab_util.h" +#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/extension_warning_set.h" #include "chrome/browser/extensions/lazy_background_task_queue.h" #include "chrome/browser/extensions/management_policy.h" @@ -209,13 +210,13 @@ base::DictionaryValue* ExtensionSettingsHandler::CreateExtensionDetailValue( extension_data->SetBoolean("terminated", extension_service_->terminated_extensions()->Contains(extension->id())); extension_data->SetBoolean("enabledIncognito", - extension_service_->IsIncognitoEnabled(extension->id())); + extension_util::IsIncognitoEnabled(extension->id(), extension_service_)); extension_data->SetBoolean("incognitoCanBeToggled", extension->can_be_incognito_enabled() && !extension->force_incognito_enabled()); extension_data->SetBoolean("wantsFileAccess", extension->wants_file_access()); extension_data->SetBoolean("allowFileAccess", - extension_service_->AllowFileAccess(extension)); + extension_util::AllowFileAccess(extension, extension_service_)); extension_data->SetBoolean("allow_reload", Manifest::IsUnpackedLocation(extension->location())); extension_data->SetBoolean("is_hosted_app", extension->is_hosted_app()); @@ -861,8 +862,9 @@ void ExtensionSettingsHandler::HandleEnableIncognitoMessage( // Bug: http://crbug.com/41384 base::AutoReset<bool> auto_reset_ignore_notifications( &ignore_notifications_, true); - extension_service_->SetIsIncognitoEnabled(extension->id(), - enable_str == "true"); + extension_util::SetIsIncognitoEnabled(extension->id(), + extension_service_, + enable_str == "true"); } void ExtensionSettingsHandler::HandleAllowFileAccessMessage( @@ -883,7 +885,8 @@ void ExtensionSettingsHandler::HandleAllowFileAccessMessage( return; } - extension_service_->SetAllowFileAccess(extension, allow_str == "true"); + extension_util::SetAllowFileAccess( + extension, extension_service_, allow_str == "true"); } void ExtensionSettingsHandler::HandleUninstallMessage( diff --git a/chrome/browser/ui/webui/inline_login_ui.cc b/chrome/browser/ui/webui/inline_login_ui.cc index f7f7a5e57f..810ee748b3 100644 --- a/chrome/browser/ui/webui/inline_login_ui.cc +++ b/chrome/browser/ui/webui/inline_login_ui.cc @@ -7,17 +7,24 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/memory/scoped_ptr.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/values.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/extensions/tab_helper.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/signin/signin_global_error.h" #include "chrome/browser/signin/signin_manager_cookie_helper.h" +#include "chrome/browser/signin/signin_names_io_thread.h" #include "chrome/browser/signin/signin_promo.h" #include "chrome/browser/signin/token_service.h" #include "chrome/browser/signin/token_service_factory.h" +#include "chrome/browser/sync/profile_sync_service.h" +#include "chrome/browser/sync/profile_sync_service_factory.h" +#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/sync/one_click_signin_sync_starter.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/url_constants.h" #include "content/public/browser/storage_partition.h" @@ -29,6 +36,7 @@ #include "google_apis/gaia/gaia_urls.h" #include "grit/browser_resources.h" #include "net/base/escape.h" +#include "net/base/url_util.h" #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/login/oauth2_token_fetcher.h" @@ -84,7 +92,7 @@ class InlineLoginUIOAuth2Delegate class InlineLoginUIHandler : public content::WebUIMessageHandler { public: explicit InlineLoginUIHandler(Profile* profile) - : profile_(profile), weak_factory_(this) {} + : profile_(profile), weak_factory_(this), choose_what_to_sync_(false) {} virtual ~InlineLoginUIHandler() {} // content::WebUIMessageHandler overrides: @@ -119,20 +127,34 @@ class InlineLoginUIHandler : public content::WebUIMessageHandler { switches::kEnableInlineSignin); params.SetInteger("authMode", enable_inline ? kInlineAuthMode : kDefaultAuthMode); - // Set continueUrl param for the inline sign in flow. It should point to - // the oauth2 auth code URL so that later we can grab the auth code from - // the cookie jar of the embedded webview. + + // Set parameters specific for inline signin flow. +#if !defined(OS_CHROMEOS) if (enable_inline) { + // Set continueUrl param for the inline sign in flow. It should point to + // the oauth2 auth code URL so that later we can grab the auth code from + // the cookie jar of the embedded webview. std::string scope = net::EscapeUrlEncodedData( gaiaUrls->oauth1_login_scope(), true); std::string client_id = net::EscapeUrlEncodedData( gaiaUrls->oauth2_chrome_client_id(), true); std::string encoded_continue_params = base::StringPrintf( "?scope=%s&client_id=%s", scope.c_str(), client_id.c_str()); + + const GURL& current_url = web_ui()->GetWebContents()->GetURL(); + signin::Source source = signin::GetSourceForPromoURL(current_url); + if (source != signin::SOURCE_UNKNOWN) { + params.SetString("service", "chromiumsync"); + base::StringAppendF( + &encoded_continue_params, "&%s=%d", "source", + static_cast<int>(source)); + } + params.SetString("continueUrl", gaiaUrls->client_login_to_oauth2_url().Resolve( encoded_continue_params).spec()); } +#endif web_ui()->CallJavascriptFunction("inline.login.loadAuthExtension", params); } @@ -160,6 +182,7 @@ class InlineLoginUIHandler : public content::WebUIMessageHandler { NOTREACHED(); return; } + dict->GetBoolean("chooseWhatToSync", &choose_what_to_sync_); content::WebContents* web_contents = web_ui()->GetWebContents(); content::StoragePartition* partition = @@ -183,24 +206,85 @@ class InlineLoginUIHandler : public content::WebUIMessageHandler { net::CookieList::const_iterator it; for (it = cookie_list.begin(); it != cookie_list.end(); ++it) { if (it->Name() == "oauth_code") { + content::WebContents* contents = web_ui()->GetWebContents(); + ProfileSyncService* sync_service = + ProfileSyncServiceFactory::GetForProfile(profile_); + const GURL& current_url = contents->GetURL(); + signin::Source source = signin::GetSourceForPromoURL(current_url); + + OneClickSigninSyncStarter::StartSyncMode start_mode = + source == signin::SOURCE_SETTINGS || choose_what_to_sync_ ? + (SigninGlobalError::GetForProfile(profile_)->HasMenuItem() && + sync_service && sync_service->HasSyncSetupCompleted()) ? + OneClickSigninSyncStarter::SHOW_SETTINGS_WITHOUT_CONFIGURE : + OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST : + OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS; + OneClickSigninSyncStarter::ConfirmationRequired confirmation_required = + source == signin::SOURCE_SETTINGS || + source == signin::SOURCE_WEBSTORE_INSTALL || + choose_what_to_sync_? + OneClickSigninSyncStarter::NO_CONFIRMATION : + OneClickSigninSyncStarter::CONFIRM_AFTER_SIGNIN; // Call OneClickSigninSyncStarter to exchange oauth code for tokens. // OneClickSigninSyncStarter will delete itself once the job is done. - // TODO(guohui): should collect from user whether they want to use - // default sync settings or configure first. new OneClickSigninSyncStarter( profile_, NULL, "0" /* session_index 0 for the default user */, UTF16ToASCII(email), UTF16ToASCII(password), it->Value(), - OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS, - web_ui()->GetWebContents(), - OneClickSigninSyncStarter::NO_CONFIRMATION, - OneClickSigninSyncStarter::Callback()); + start_mode, + contents, + confirmation_required, + base::Bind(&InlineLoginUIHandler::SyncStarterCallback, + weak_factory_.GetWeakPtr())); + break; } } web_ui()->CallJavascriptFunction("inline.login.closeDialog"); } + void SyncStarterCallback(OneClickSigninSyncStarter::SyncSetupResult result) { + content::WebContents* contents = web_ui()->GetWebContents(); + const GURL& current_url = contents->GetURL(); + bool auto_close = signin::IsAutoCloseEnabledInURL(current_url); + signin::Source source = signin::GetSourceForPromoURL(current_url); + if (auto_close) { + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind( + &InlineLoginUIHandler::CloseTab, weak_factory_.GetWeakPtr())); + } else if (source != signin::SOURCE_UNKNOWN && + source != signin::SOURCE_SETTINGS && + source != signin::SOURCE_WEBSTORE_INSTALL) { + // Redirect to NTP/Apps page and display a confirmation bubble. + // TODO(guohui): should redirect to the given continue url for webstore + // install flows. + GURL url(source == signin::SOURCE_APPS_PAGE_LINK ? + chrome::kChromeUIAppsURL : chrome::kChromeUINewTabURL); + content::OpenURLParams params(url, + content::Referrer(), + CURRENT_TAB, + content::PAGE_TRANSITION_AUTO_TOPLEVEL, + false); + contents->OpenURL(params); + } + } + + void CloseTab() { + content::WebContents* tab = web_ui()->GetWebContents(); + Browser* browser = chrome::FindBrowserWithWebContents(tab); + if (browser) { + TabStripModel* tab_strip_model = browser->tab_strip_model(); + if (tab_strip_model) { + int index = tab_strip_model->GetIndexOfWebContents(tab); + if (index != TabStripModel::kNoTab) { + tab_strip_model->ExecuteContextMenuCommand( + index, TabStripModel::CommandCloseTab); + } + } + } + } Profile* profile_; base::WeakPtrFactory<InlineLoginUIHandler> weak_factory_; + bool choose_what_to_sync_; #if defined(OS_CHROMEOS) scoped_ptr<chromeos::OAuth2TokenFetcher> oauth2_token_fetcher_; scoped_ptr<InlineLoginUIOAuth2Delegate> oauth2_delegate_; diff --git a/chrome/browser/ui/webui/inspect_ui.cc b/chrome/browser/ui/webui/inspect_ui.cc index 5f52409f5e..4a7253d72c 100644 --- a/chrome/browser/ui/webui/inspect_ui.cc +++ b/chrome/browser/ui/webui/inspect_ui.cc @@ -19,8 +19,12 @@ #include "chrome/browser/devtools/port_forwarding_controller.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser_navigator.h" +#include "chrome/browser/ui/singleton_tabs.h" #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" +#include "chrome/browser/ui/webui/extensions/extension_icon_source.h" #include "chrome/browser/ui/webui/theme_source.h" +#include "chrome/common/extensions/extension_constants.h" #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "content/public/browser/browser_child_process_observer.h" @@ -36,6 +40,7 @@ #include "content/public/browser/notification_types.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" +#include "content/public/browser/user_metrics.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_ui.h" #include "content/public/browser/web_ui_data_source.h" @@ -181,6 +186,9 @@ DictionaryValue* BuildTargetDescriptor(RenderViewHost* rvh, bool is_tab) { else target_type = kExtensionTargetType; title = extension->name(); + favicon_url = extensions::ExtensionIconSource::GetIconURL( + extension, extension_misc::EXTENSION_ICON_SMALLISH, + ExtensionIconSet::MATCH_BIGGER, false, NULL); } } } @@ -555,6 +563,14 @@ void InspectUI::OpenRemotePage(const std::string& browser_id, it->second->Open(gurl.spec()); } +void InspectUI::InspectDevices(Browser* browser) { + content::RecordAction(content::UserMetricsAction("InspectDevices")); + chrome::NavigateParams params(chrome::GetSingletonTabNavigateParams( + browser, GURL(chrome::kChromeUIInspectURL))); + params.path_behavior = chrome::NavigateParams::IGNORE_AND_NAVIGATE; + ShowSingletonTabOverwritingNTP(browser, params); +} + void InspectUI::PopulateLists() { std::set<RenderViewHost*> tab_rvhs; for (TabContentsIterator it; !it.done(); it.Next()) diff --git a/chrome/browser/ui/webui/inspect_ui.h b/chrome/browser/ui/webui/inspect_ui.h index 85ec1c5e35..6d48b3c808 100644 --- a/chrome/browser/ui/webui/inspect_ui.h +++ b/chrome/browser/ui/webui/inspect_ui.h @@ -20,6 +20,8 @@ namespace base { class Value; } +class Browser; + class InspectUI : public content::WebUIController, public content::NotificationObserver, public DevToolsAdbBridge::Listener { @@ -34,6 +36,8 @@ class InspectUI : public content::WebUIController, void ReloadRemotePage(const std::string& page_id); void OpenRemotePage(const std::string& browser_id, const std::string& url); + static void InspectDevices(Browser* browser); + private: class WorkerCreationDestructionListener; diff --git a/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc b/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc index 716dc737f5..01f6c18068 100644 --- a/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc +++ b/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc @@ -238,9 +238,6 @@ const char kURLRegisterComplete[] = const char kURLGaiaToken[] = "https://accounts.google.com/o/oauth2/token"; -const char kSampleServiceName[] = "myService._privet._tcp.local"; -const char kSampleDeviceID[] = "MyFakeID"; -const char kSampleDeviceHost[] = "myservice.local"; const char kSampleUser[] = "user@host.com"; class TestMessageLoopCondition { diff --git a/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.cc b/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.cc index 7c9bb88971..d1fafa6402 100644 --- a/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.cc +++ b/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.cc @@ -59,7 +59,6 @@ const int kRegistrationAnnouncementTimeoutSeconds = 5; const int kInitialRequeryTimeSeconds = 1; const int kMaxRequeryTimeSeconds = 2; // Time for last requery -const int kRequeryExpontentialGrowthBase = 2; int g_num_visible = 0; diff --git a/chrome/browser/ui/webui/media/webrtc_logs_ui.cc b/chrome/browser/ui/webui/media/webrtc_logs_ui.cc index 54de606df1..ce676eb0d4 100644 --- a/chrome/browser/ui/webui/media/webrtc_logs_ui.cc +++ b/chrome/browser/ui/webui/media/webrtc_logs_ui.cc @@ -54,9 +54,6 @@ content::WebUIDataSource* CreateWebRtcLogsUIHTMLSource() { source->AddLocalizedString("bugLinkText", IDS_WEBRTC_LOGS_BUG_LINK_LABEL); source->AddLocalizedString("noLogsMessage", IDS_WEBRTC_LOGS_NO_LOGS_MESSAGE); - source->AddLocalizedString("disabledHeader", IDS_WEBRTC_LOGS_DISABLED_HEADER); - source->AddLocalizedString("disabledMessage", - IDS_WEBRTC_LOGS_DISABLED_MESSAGE); source->SetJsonPath("strings.js"); source->AddResourcePath("webrtc_logs.js", IDR_WEBRTC_LOGS_JS); source->SetDefaultResource(IDR_WEBRTC_LOGS_HTML); @@ -73,7 +70,7 @@ content::WebUIDataSource* CreateWebRtcLogsUIHTMLSource() { class WebRtcLogsDOMHandler : public WebUIMessageHandler, public WebRtcLogUploadList::Delegate { public: - explicit WebRtcLogsDOMHandler(); + explicit WebRtcLogsDOMHandler(Profile* profile); virtual ~WebRtcLogsDOMHandler(); // WebUIMessageHandler implementation. @@ -102,9 +99,9 @@ class WebRtcLogsDOMHandler : public WebUIMessageHandler, DISALLOW_COPY_AND_ASSIGN(WebRtcLogsDOMHandler); }; -WebRtcLogsDOMHandler::WebRtcLogsDOMHandler() +WebRtcLogsDOMHandler::WebRtcLogsDOMHandler(Profile* profile) : list_available_(false), js_request_pending_(false) { - upload_list_ = WebRtcLogUploadList::Create(this); + upload_list_ = WebRtcLogUploadList::Create(this, profile); } WebRtcLogsDOMHandler::~WebRtcLogsDOMHandler() { @@ -120,7 +117,7 @@ void WebRtcLogsDOMHandler::RegisterMessages() { } void WebRtcLogsDOMHandler::HandleRequestWebRtcLogs(const ListValue* args) { - if (!WebRtcLogsUI::WebRtcLogsUIEnabled() || list_available_) + if (list_available_) UpdateUI(); else js_request_pending_ = true; @@ -133,28 +130,22 @@ void WebRtcLogsDOMHandler::OnUploadListAvailable() { } void WebRtcLogsDOMHandler::UpdateUI() { - bool webrtc_logs_enabled = WebRtcLogsUI::WebRtcLogsUIEnabled(); - ListValue upload_list; + std::vector<WebRtcLogUploadList::UploadInfo> uploads; + upload_list_->GetUploads(50, &uploads); - if (webrtc_logs_enabled) { - std::vector<WebRtcLogUploadList::UploadInfo> uploads; - upload_list_->GetUploads(50, &uploads); - - for (std::vector<WebRtcLogUploadList::UploadInfo>::iterator i = - uploads.begin(); i != uploads.end(); ++i) { - DictionaryValue* upload = new DictionaryValue(); - upload->SetString("id", i->id); - upload->SetString("time", base::TimeFormatFriendlyDateAndTime(i->time)); - upload_list.Append(upload); - } + ListValue upload_list; + for (std::vector<WebRtcLogUploadList::UploadInfo>::iterator i = + uploads.begin(); i != uploads.end(); ++i) { + DictionaryValue* upload = new DictionaryValue(); + upload->SetString("id", i->id); + upload->SetString("time", base::TimeFormatFriendlyDateAndTime(i->time)); + upload_list.Append(upload); } - base::FundamentalValue enabled(webrtc_logs_enabled); - const chrome::VersionInfo version_info; base::StringValue version(version_info.Version()); - web_ui()->CallJavascriptFunction("updateWebRtcLogsList", enabled, upload_list, + web_ui()->CallJavascriptFunction("updateWebRtcLogsList", upload_list, version); } @@ -167,30 +158,9 @@ void WebRtcLogsDOMHandler::UpdateUI() { /////////////////////////////////////////////////////////////////////////////// WebRtcLogsUI::WebRtcLogsUI(content::WebUI* web_ui) : WebUIController(web_ui) { - web_ui->AddMessageHandler(new WebRtcLogsDOMHandler()); + Profile* profile = Profile::FromWebUI(web_ui); + web_ui->AddMessageHandler(new WebRtcLogsDOMHandler(profile)); // Set up the chrome://webrtc-logs/ source. - Profile* profile = Profile::FromWebUI(web_ui); content::WebUIDataSource::Add(profile, CreateWebRtcLogsUIHTMLSource()); } - -// static -bool WebRtcLogsUI::WebRtcLogsUIEnabled() { -#if defined(GOOGLE_CHROME_BUILD) -#if defined(OS_CHROMEOS) - bool reporting_enabled = false; - chromeos::CrosSettings::Get()->GetBoolean(chromeos::kStatsReportingPref, - &reporting_enabled); - return reporting_enabled; -#elif defined(OS_ANDROID) - // Android has it's own setings for metrics / crash uploading. - PrefService* prefs = g_browser_process->local_state(); - return prefs->GetBoolean(prefs::kCrashReportingEnabled); -#else - PrefService* prefs = g_browser_process->local_state(); - return prefs->GetBoolean(prefs::kMetricsReportingEnabled); -#endif -#else - return false; -#endif -} diff --git a/chrome/browser/ui/webui/media/webrtc_logs_ui.h b/chrome/browser/ui/webui/media/webrtc_logs_ui.h index 3c375483d9..c2e7ee1dc3 100644 --- a/chrome/browser/ui/webui/media/webrtc_logs_ui.h +++ b/chrome/browser/ui/webui/media/webrtc_logs_ui.h @@ -17,9 +17,6 @@ class WebRtcLogsUI : public content::WebUIController { public: explicit WebRtcLogsUI(content::WebUI* web_ui); - // Whether WebRTC logs UI has been enabled. - static bool WebRtcLogsUIEnabled(); - private: DISALLOW_COPY_AND_ASSIGN(WebRtcLogsUI); }; diff --git a/chrome/browser/ui/webui/net_internals/net_internals_ui.cc b/chrome/browser/ui/webui/net_internals/net_internals_ui.cc index 3f55e34ad4..cee252a653 100644 --- a/chrome/browser/ui/webui/net_internals/net_internals_ui.cc +++ b/chrome/browser/ui/webui/net_internals/net_internals_ui.cc @@ -1552,9 +1552,14 @@ void NetInternalsMessageHandler::OnImportONCFile(const ListValue* list) { onc::ONCSource onc_source = onc::ONC_SOURCE_USER_IMPORT; base::ListValue network_configs; + base::DictionaryValue global_network_config; base::ListValue certificates; - if (!chromeos::onc::ParseAndValidateOncForImport( - onc_blob, onc_source, passcode, &network_configs, &certificates)) { + if (!chromeos::onc::ParseAndValidateOncForImport(onc_blob, + onc_source, + passcode, + &network_configs, + &global_network_config, + &certificates)) { error = "Errors occurred during the ONC parsing. "; } diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc index 2420bc5b81..9be9f3005c 100644 --- a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc +++ b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc @@ -27,6 +27,7 @@ #include "chrome/browser/favicon/favicon_service_factory.h" #include "chrome/browser/prefs/scoped_user_pref_update.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser_dialogs.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/browser_window.h" @@ -566,8 +567,8 @@ void AppLauncherHandler::HandleUninstallApp(const ListValue* args) { std::string extension_id; CHECK(args->GetString(0, &extension_id)); - const Extension* extension = extension_service_->GetExtensionById( - extension_id, true); + const Extension* extension = extension_service_->GetInstalledExtension( + extension_id); if (!extension) return; @@ -602,8 +603,9 @@ void AppLauncherHandler::HandleCreateAppShortcut(const ListValue* args) { Browser* browser = chrome::FindBrowserWithWebContents( web_ui()->GetWebContents()); - browser->window()->ShowCreateChromeAppShortcutsDialog( - browser->profile(), extension); + chrome::ShowCreateChromeAppShortcutsDialog( + browser->window()->GetNativeWindow(), browser->profile(), extension, + base::Closure()); } void AppLauncherHandler::HandleReorderApps(const ListValue* args) { @@ -790,7 +792,7 @@ void AppLauncherHandler::ExtensionUninstallAccepted() { // The extension can be uninstalled in another window while the UI was // showing. Do nothing in that case. const Extension* extension = - extension_service_->GetExtensionById(extension_id_prompting_, true); + extension_service_->GetInstalledExtension(extension_id_prompting_); if (!extension) return; diff --git a/chrome/browser/ui/webui/options/browser_options_handler.cc b/chrome/browser/ui/webui/options/browser_options_handler.cc index 27ba092e0e..03d3d1fa66 100644 --- a/chrome/browser/ui/webui/options/browser_options_handler.cc +++ b/chrome/browser/ui/webui/options/browser_options_handler.cc @@ -95,6 +95,7 @@ #endif #if defined(OS_CHROMEOS) +#include "ash/ash_switches.h" #include "ash/magnifier/magnifier_constants.h" #include "chrome/browser/chromeos/accessibility/accessibility_util.h" #include "chrome/browser/chromeos/extensions/wallpaper_manager_util.h" @@ -346,6 +347,20 @@ void BrowserOptionsHandler::GetLocalizedValues(DictionaryValue* values) { IDS_OPTIONS_SETTINGS_ACCESSIBILITY_VIRTUAL_KEYBOARD_DESCRIPTION }, { "accessibilityAlwaysShowMenu", IDS_OPTIONS_SETTINGS_ACCESSIBILITY_SHOULD_ALWAYS_SHOW_MENU }, + { "accessibilityAutoclick", + IDS_OPTIONS_SETTINGS_ACCESSIBILITY_AUTOCLICK_DESCRIPTION }, + { "accessibilityAutoclickDropdown", + IDS_OPTIONS_SETTINGS_ACCESSIBILITY_AUTOCLICK_DROPDOWN_DESCRIPTION }, + { "autoclickDelayExtremelyShort", + IDS_OPTIONS_SETTINGS_ACCESSIBILITY_AUTOCLICK_DELAY_EXTREMELY_SHORT }, + { "autoclickDelayVeryShort", + IDS_OPTIONS_SETTINGS_ACCESSIBILITY_AUTOCLICK_DELAY_VERY_SHORT }, + { "autoclickDelayShort", + IDS_OPTIONS_SETTINGS_ACCESSIBILITY_AUTOCLICK_DELAY_SHORT }, + { "autoclickDelayLong", + IDS_OPTIONS_SETTINGS_ACCESSIBILITY_AUTOCLICK_DELAY_LONG }, + { "autoclickDelayVeryLong", + IDS_OPTIONS_SETTINGS_ACCESSIBILITY_AUTOCLICK_DELAY_VERY_LONG }, { "enableContentProtectionAttestation", IDS_OPTIONS_ENABLE_CONTENT_PROTECTION_ATTESTATION }, { "factoryResetHeading", IDS_OPTIONS_FACTORY_RESET_HEADING }, @@ -492,6 +507,9 @@ void BrowserOptionsHandler::GetLocalizedValues(DictionaryValue* values) { const CommandLine& command_line = *CommandLine::ForCurrentProcess(); values->SetBoolean("enableStickyKeys", command_line.HasSwitch(switches::kEnableStickyKeys)); + values->SetBoolean("enableAutoclick", + command_line.HasSwitch( + ash::switches::kAshEnableAutoclick)); #endif #if defined(OS_MACOSX) @@ -655,6 +673,10 @@ void BrowserOptionsHandler::RegisterMessages() { #endif } +void BrowserOptionsHandler::Uninitialize() { + registrar_.RemoveAll(); +} + void BrowserOptionsHandler::OnStateChanged() { UpdateSyncState(); } @@ -996,13 +1018,7 @@ void BrowserOptionsHandler::Observe( break; #endif case chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED: - // If the browser shuts down during supervised-profile creation, deleting - // the unregistered supervised-user profile triggers this notification, - // but the RenderViewHost the profile info would be sent to has already - // been destroyed. - if (!web_ui()->GetWebContents()->GetRenderViewHost()) - return; - SendProfilesInfo(); + SendProfilesInfo(); break; case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL: case chrome::NOTIFICATION_GOOGLE_SIGNED_OUT: diff --git a/chrome/browser/ui/webui/options/browser_options_handler.h b/chrome/browser/ui/webui/options/browser_options_handler.h index 40fc417d7c..8b8d5ec667 100644 --- a/chrome/browser/ui/webui/options/browser_options_handler.h +++ b/chrome/browser/ui/webui/options/browser_options_handler.h @@ -53,6 +53,7 @@ class BrowserOptionsHandler virtual void InitializeHandler() OVERRIDE; virtual void InitializePage() OVERRIDE; virtual void RegisterMessages() OVERRIDE; + virtual void Uninitialize() OVERRIDE; // ProfileSyncServiceObserver implementation. virtual void OnStateChanged() OVERRIDE; diff --git a/chrome/browser/ui/webui/options/chromeos/internet_options_handler.cc b/chrome/browser/ui/webui/options/chromeos/internet_options_handler.cc index 8d4045dc91..5fc6abccbc 100644 --- a/chrome/browser/ui/webui/options/chromeos/internet_options_handler.cc +++ b/chrome/browser/ui/webui/options/chromeos/internet_options_handler.cc @@ -160,7 +160,6 @@ const char kShowMorePlanInfoMessage[] = "showMorePlanInfo"; // These are strings used to communicate with JavaScript. const char kTagActivate[] = "activate"; -const char kTagActivated[] = "activated"; const char kTagActivationState[] = "activationState"; const char kTagAddConnection[] = "add"; const char kTagApn[] = "apn"; @@ -178,7 +177,6 @@ const char kTagConnected[] = "connected"; const char kTagConnecting[] = "connecting"; const char kTagConnectionState[] = "connectionState"; const char kTagControlledBy[] = "controlledBy"; -const char kTagDataRemaining[] = "dataRemaining"; const char kTagDeviceConnected[] = "deviceConnected"; const char kTagDisableConnectButton[] = "disableConnectButton"; const char kTagDisconnect[] = "disconnect"; @@ -231,7 +229,6 @@ const char kTagServiceName[] = "serviceName"; const char kTagServicePath[] = "servicePath"; const char kTagShared[] = "shared"; const char kTagShowActivateButton[] = "showActivateButton"; -const char kTagShowBuyButton[] = "showBuyButton"; const char kTagShowPreferred[] = "showPreferred"; const char kTagShowProxy[] = "showProxy"; const char kTagShowStaticIPConfig[] = "showStaticIPConfig"; @@ -246,7 +243,6 @@ const char kTagUsername[] = "username"; const char kTagValue[] = "value"; const char kTagVpn[] = "vpn"; const char kTagVpnList[] = "vpnList"; -const char kTagWarning[] = "warning"; const char kTagWifi[] = "wifi"; const char kTagWifiAvailable[] = "wifiAvailable"; const char kTagWifiEnabled[] = "wifiEnabled"; @@ -547,18 +543,32 @@ base::DictionaryValue* BuildIPInfoDictionary( return ip_info_dict.release(); } -static bool CanForgetNetworkType(const std::string& type) { +bool CanForgetNetworkType(const std::string& type) { return type == shill::kTypeWifi || type == shill::kTypeWimax || type == shill::kTypeVPN; } -static bool CanAddNetworkType(const std::string& type) { +bool CanAddNetworkType(const std::string& type) { return type == shill::kTypeWifi || type == shill::kTypeVPN || type == shill::kTypeCellular; } +// Decorate dictionary |value_dict| with policy information from |ui_data|. +void DecorateValueDictionary(const NetworkPropertyUIData& ui_data, + const base::Value& value, + base::DictionaryValue* value_dict) { + const base::Value* recommended_value = ui_data.default_value(); + if (ui_data.IsManaged()) + value_dict->SetString(kTagControlledBy, kTagPolicy); + else if (recommended_value && recommended_value->Equals(&value)) + value_dict->SetString(kTagControlledBy, kTagRecommended); + + if (recommended_value) + value_dict->Set(kTagRecommendedValue, recommended_value->DeepCopy()); +} + // Decorate pref value as CoreOptionsHandler::CreateValueForPref() does and // store it under |key| in |settings|. Takes ownership of |value|. void SetValueDictionary(base::DictionaryValue* settings, @@ -568,15 +578,34 @@ void SetValueDictionary(base::DictionaryValue* settings, base::DictionaryValue* dict = new base::DictionaryValue(); // DictionaryValue::Set() takes ownership of |value|. dict->Set(kTagValue, value); - const base::Value* recommended_value = ui_data.default_value(); - if (ui_data.IsManaged()) - dict->SetString(kTagControlledBy, kTagPolicy); - else if (recommended_value && recommended_value->Equals(value)) - dict->SetString(kTagControlledBy, kTagRecommended); - - if (recommended_value) - dict->Set(kTagRecommendedValue, recommended_value->DeepCopy()); settings->Set(key, dict); + DecorateValueDictionary(ui_data, *value, dict); +} + +// Creates a decorated dictionary like SetValueDictionary does, but extended for +// the Autoconnect property, which respects additionally global network policy. +void SetAutoconnectValueDictionary(bool network_is_private, + ::onc::ONCSource onc_source, + bool current_autoconnect, + const NetworkPropertyUIData& ui_data, + base::DictionaryValue* settings) { + base::DictionaryValue* dict = new base::DictionaryValue(); + base::Value* value = new base::FundamentalValue(current_autoconnect); + // DictionaryValue::Set() takes ownership of |value|. + dict->Set(kTagValue, value); + settings->Set(kTagAutoConnect, dict); + if (onc_source != ::onc::ONC_SOURCE_USER_POLICY && + onc_source != ::onc::ONC_SOURCE_DEVICE_POLICY) { + // Autoconnect can be controlled by the GlobalNetworkConfiguration of the + // ONC policy. + bool only_policy_autoconnect = + onc::PolicyAllowsOnlyPolicyNetworksToAutoconnect(network_is_private); + if (only_policy_autoconnect) { + dict->SetString(kTagControlledBy, kTagPolicy); + return; + } + } + DecorateValueDictionary(ui_data, *value, dict); } std::string CopyStringFromDictionary(const base::DictionaryValue& source, @@ -1588,9 +1617,11 @@ void InternetOptionsHandler::PopulateDictionaryDetailsCallback( bool auto_connect = false; shill_properties.GetBooleanWithoutPathExpansion( shill::kAutoConnectProperty, &auto_connect); - SetValueDictionary(&dictionary, kTagAutoConnect, - new base::FundamentalValue(auto_connect), - auto_connect_ui_data); + SetAutoconnectValueDictionary(network->IsPrivate(), + onc_source, + auto_connect, + auto_connect_ui_data, + &dictionary); PopulateConnectionDetails(network, shill_properties, &dictionary); diff --git a/chrome/browser/ui/webui/options/manage_profile_handler.cc b/chrome/browser/ui/webui/options/manage_profile_handler.cc index 345e58bd06..ada4acddf8 100644 --- a/chrome/browser/ui/webui/options/manage_profile_handler.cc +++ b/chrome/browser/ui/webui/options/manage_profile_handler.cc @@ -195,17 +195,15 @@ void ManageProfileHandler::RegisterMessages() { base::Unretained(this))); } +void ManageProfileHandler::Uninitialize() { + registrar_.RemoveAll(); +} + void ManageProfileHandler::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (type == chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED) { - // If the browser shuts down during supervised-profile creation, deleting - // the unregistered supervised-user profile triggers this notification, - // but the RenderViewHost the profile info would be sent to has already been - // destroyed. - if (!web_ui()->GetWebContents()->GetRenderViewHost()) - return; SendProfileNames(); base::StringValue value(kManageProfileIconGridName); SendProfileIcons(value); diff --git a/chrome/browser/ui/webui/options/manage_profile_handler.h b/chrome/browser/ui/webui/options/manage_profile_handler.h index e4988fa393..90c9820810 100644 --- a/chrome/browser/ui/webui/options/manage_profile_handler.h +++ b/chrome/browser/ui/webui/options/manage_profile_handler.h @@ -30,6 +30,7 @@ class ManageProfileHandler : public OptionsPageUIHandler, base::DictionaryValue* localized_strings) OVERRIDE; virtual void InitializeHandler() OVERRIDE; virtual void InitializePage() OVERRIDE; + virtual void Uninitialize() OVERRIDE; // WebUIMessageHandler: virtual void RegisterMessages() OVERRIDE; diff --git a/chrome/browser/ui/webui/options/options_ui.cc b/chrome/browser/ui/webui/options/options_ui.cc index cf3e584df1..61bd0f6477 100644 --- a/chrome/browser/ui/webui/options/options_ui.cc +++ b/chrome/browser/ui/webui/options/options_ui.cc @@ -7,6 +7,7 @@ #include <algorithm> #include <vector> +#include "base/basictypes.h" #include "base/callback.h" #include "base/command_line.h" #include "base/memory/ref_counted_memory.h" @@ -64,6 +65,7 @@ #include "ui/base/resource/resource_bundle.h" #include "ui/base/webui/jstemplate_builder.h" #include "ui/base/webui/web_ui_util.h" +#include "url/gurl.h" #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/system/pointer_device_observer.h" @@ -235,6 +237,7 @@ void OptionsPageUIHandler::RegisterTitle(DictionaryValue* localized_strings, OptionsUI::OptionsUI(content::WebUI* web_ui) : WebUIController(web_ui), + WebContentsObserver(web_ui->GetWebContents()), initialized_handlers_(false) { DictionaryValue* localized_strings = new DictionaryValue(); localized_strings->Set(OptionsPageUIHandler::kSettingsAppKey, @@ -376,6 +379,21 @@ base::RefCountedMemory* OptionsUI::GetFaviconResourceBytes( LoadDataResourceBytesForScale(IDR_SETTINGS_FAVICON, scale_factor); } +void OptionsUI::DidStartProvisionalLoadForFrame( + int64 frame_id, + int64 parent_frame_id, + bool is_main_frame, + const GURL& validated_url, + bool is_error_page, + bool is_iframe_srcdoc, + content::RenderViewHost* render_view_host) { + if (render_view_host == web_ui()->GetWebContents()->GetRenderViewHost() && + validated_url.host() == chrome::kChromeUISettingsFrameHost) { + for (size_t i = 0; i < handlers_.size(); ++i) + handlers_[i]->PageLoadStarted(); + } +} + void OptionsUI::InitializeHandlers() { Profile* profile = Profile::FromWebUI(web_ui()); DCHECK(!profile->IsOffTheRecord() || profile->IsGuestSession()); @@ -405,18 +423,6 @@ void OptionsUI::InitializeHandlers() { "BrowserOptions.notifyInitializationComplete"); } -void OptionsUI::RenderViewCreated(content::RenderViewHost* render_view_host) { - content::WebUIController::RenderViewCreated(render_view_host); - for (size_t i = 0; i < handlers_.size(); ++i) - handlers_[i]->PageLoadStarted(); -} - -void OptionsUI::RenderViewReused(content::RenderViewHost* render_view_host) { - content::WebUIController::RenderViewReused(render_view_host); - for (size_t i = 0; i < handlers_.size(); ++i) - handlers_[i]->PageLoadStarted(); -} - void OptionsUI::AddOptionsPageUIHandler(DictionaryValue* localized_strings, OptionsPageUIHandler* handler_raw) { scoped_ptr<OptionsPageUIHandler> handler(handler_raw); diff --git a/chrome/browser/ui/webui/options/options_ui.h b/chrome/browser/ui/webui/options/options_ui.h index ac2a98b658..8117ed6a62 100644 --- a/chrome/browser/ui/webui/options/options_ui.h +++ b/chrome/browser/ui/webui/options/options_ui.h @@ -13,6 +13,7 @@ #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" #include "content/public/browser/notification_types.h" +#include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_ui_controller.h" #include "content/public/browser/web_ui_message_handler.h" #include "ui/base/layout.h" @@ -114,6 +115,7 @@ class OptionsPageUIHandlerHost { // The WebUI for chrome:settings-frame. class OptionsUI : public content::WebUIController, + public content::WebContentsObserver, public OptionsPageUIHandlerHost { public: explicit OptionsUI(content::WebUI* web_ui); @@ -128,15 +130,19 @@ class OptionsUI : public content::WebUIController, static base::RefCountedMemory* GetFaviconResourceBytes( ui::ScaleFactor scale_factor); + // Overridden from content::WebContentsObserver: + virtual void DidStartProvisionalLoadForFrame( + int64 frame_id, + int64 parent_frame_id, + bool is_main_frame, + const GURL& validated_url, + bool is_error_page, + bool is_iframe_srcdoc, + content::RenderViewHost* render_view_host) OVERRIDE; + // Overridden from OptionsPageUIHandlerHost: virtual void InitializeHandlers() OVERRIDE; - // Overridden from content::WebUIController: - virtual void RenderViewCreated(content::RenderViewHost* render_view_host) - OVERRIDE; - virtual void RenderViewReused(content::RenderViewHost* render_view_host) - OVERRIDE; - private: // Adds OptionsPageUiHandler to the handlers list if handler is enabled. void AddOptionsPageUIHandler(base::DictionaryValue* localized_strings, diff --git a/chrome/browser/ui/webui/options/options_ui_browsertest.cc b/chrome/browser/ui/webui/options/options_ui_browsertest.cc index e23ec2133f..f185076d43 100644 --- a/chrome/browser/ui/webui/options/options_ui_browsertest.cc +++ b/chrome/browser/ui/webui/options/options_ui_browsertest.cc @@ -15,8 +15,38 @@ #include "grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" +#if !defined(OS_CHROMEOS) +#include <string> + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/callback.h" +#include "base/files/file_path.h" +#include "base/run_loop.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/ui/browser_commands.h" +#include "content/public/test/test_navigation_observer.h" +#include "ui/base/window_open_disposition.h" +#include "url/gurl.h" +#endif + namespace options { +namespace { + +#if !defined(OS_CHROMEOS) +void RunClosureWhenProfileInitialized(const base::Closure& closure, + Profile* profile, + Profile::CreateStatus status) { + if (status == Profile::CREATE_STATUS_INITIALIZED) + closure.Run(); +} +#endif + +} // namespace + OptionsUIBrowserTest::OptionsUIBrowserTest() { } @@ -49,4 +79,54 @@ IN_PROC_BROWSER_TEST_F(OptionsUIBrowserTest, DISABLED_LoadOptionsByURL) { VerifyNavbar(); } +#if !defined(OS_CHROMEOS) +// Regression test for http://crbug.com/301436, excluded on Chrome OS because +// profile management in the settings UI exists on desktop platforms only. +IN_PROC_BROWSER_TEST_F(OptionsUIBrowserTest, NavigateBackFromOverlayDialog) { + // Navigate to the settings page. + ui_test_utils::NavigateToURL(browser(), GURL("chrome://settings-frame")); + + // Click a button that opens an overlay dialog. + content::WebContents* contents = + browser()->tab_strip_model()->GetActiveWebContents(); + ASSERT_TRUE(content::ExecuteScript( + contents, "$('manage-default-search-engines').click();")); + + // Go back to the settings page. + content::TestNavigationObserver observer(contents); + chrome::GoBack(browser(), CURRENT_TAB); + observer.Wait(); + + // Verify that the settings page lists one profile. + const char javascript[] = + "domAutomationController.send(" + " document.querySelectorAll('list#profiles-list > div[role=listitem]')" + " .length);"; + int profiles; + ASSERT_TRUE(content::ExecuteScriptAndExtractInt( + contents, javascript, &profiles)); + EXPECT_EQ(1, profiles); + + // Create a second profile. + ProfileManager* profile_manager = g_browser_process->profile_manager(); + const base::FilePath profile_path = + profile_manager->GenerateNextProfileDirectoryPath(); + + base::RunLoop run_loop; + profile_manager->CreateProfileAsync( + profile_manager->GenerateNextProfileDirectoryPath(), + base::Bind(&RunClosureWhenProfileInitialized, + run_loop.QuitClosure()), + string16(), + string16(), + std::string()); + run_loop.Run(); + + // Verify that the settings page has updated and lists two profiles. + ASSERT_TRUE(content::ExecuteScriptAndExtractInt( + contents, javascript, &profiles)); + EXPECT_EQ(2, profiles); +} +#endif + } // namespace options diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc index 28e2e5fbc8..962e93b6c2 100644 --- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc +++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc @@ -173,6 +173,10 @@ const char kPrinterId[] = "printerId"; const char kDisableColorOption[] = "disableColorOption"; const char kSetDuplexAsDefault[] = "setDuplexAsDefault"; const char kPrinterDefaultDuplexValue[] = "printerDefaultDuplexValue"; +#if defined(USE_CUPS) +const char kCUPSsColorModel[] = "cupsColorModel"; +const char kCUPSsBWModel[] = "cupsBWModel"; +#endif // Get the print job settings dictionary from |args|. The caller takes // ownership of the returned DictionaryValue. Returns NULL on failure. @@ -328,6 +332,11 @@ void GetPrinterCapabilitiesOnFileThread( settings_info->SetBoolean(kDisableColorOption, !info.color_changeable); settings_info->SetBoolean(printing::kSettingSetColorAsDefault, info.color_default); +#if defined(USE_CUPS) + settings_info->SetInteger(kCUPSsColorModel, info.color_model); + settings_info->SetInteger(kCUPSsBWModel, info.bw_model); +#endif + // TODO(gene): Make new capabilities format for Print Preview // that will suit semantic capabiltities better. // Refactor pld API code below @@ -352,6 +361,14 @@ printing::StickySettings* GetStickySettings() { } // namespace +#if defined(USE_CUPS) +struct PrintPreviewHandler::CUPSPrinterColorModels { + std::string printer_name; + printing::ColorModel color_model; + printing::ColorModel bw_model; +}; +#endif + class PrintPreviewHandler::AccessTokenService : public OAuth2TokenService::Consumer { public: @@ -658,6 +675,10 @@ void PrintPreviewHandler::HandlePrint(const ListValue* args) { // Remove selection only flag for the same reason. settings->Remove(printing::kSettingShouldPrintSelectionOnly, NULL); +#if defined(USE_CUPS) + ConvertColorSettingToCUPSColorModel(settings.get()); +#endif + // Set ID to know whether printing is for preview. settings->SetInteger(printing::kPreviewUIID, print_preview_ui->GetIDForPrintPreviewUI()); @@ -992,6 +1013,11 @@ void PrintPreviewHandler::SendAccessToken(const std::string& type, void PrintPreviewHandler::SendPrinterCapabilities( const DictionaryValue* settings_info) { VLOG(1) << "Get printer capabilities finished"; + +#if defined(USE_CUPS) + SaveCUPSColorSetting(settings_info); +#endif + web_ui()->CallJavascriptFunction("updateWithPrinterCapabilities", *settings_info); } @@ -1171,3 +1197,48 @@ bool PrintPreviewHandler::GetPreviewDataAndTitle( *title = print_preview_ui->initiator_title(); return true; } + +#if defined(USE_CUPS) +void PrintPreviewHandler::SaveCUPSColorSetting( + const base::DictionaryValue* settings) { + cups_printer_color_models_.reset(new CUPSPrinterColorModels); + settings->GetString(kPrinterId, &cups_printer_color_models_->printer_name); + settings->GetInteger( + kCUPSsColorModel, + reinterpret_cast<int*>(&cups_printer_color_models_->color_model)); + settings->GetInteger( + kCUPSsBWModel, + reinterpret_cast<int*>(&cups_printer_color_models_->bw_model)); +} + +void PrintPreviewHandler::ConvertColorSettingToCUPSColorModel( + base::DictionaryValue* settings) const { + if (!cups_printer_color_models_) + return; + + // Sanity check the printer name. + std::string printer_name; + if (!settings->GetString(printing::kSettingDeviceName, &printer_name) || + printer_name != cups_printer_color_models_->printer_name) { + NOTREACHED(); + return; + } + + int color; + if (!settings->GetInteger(printing::kSettingColor, &color)) { + NOTREACHED(); + return; + } + + printing::ColorModel bw_model = cups_printer_color_models_->bw_model; + if (color == printing::GRAY && bw_model != printing::UNKNOWN_COLOR_MODEL) { + settings->SetInteger(printing::kSettingColor, bw_model); + return; + } + DCHECK_EQ(printing::COLOR, color); + printing::ColorModel color_model = cups_printer_color_models_->color_model; + if (color_model != printing::UNKNOWN_COLOR_MODEL) + settings->SetInteger(printing::kSettingColor, color_model); +} + +#endif diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.h b/chrome/browser/ui/webui/print_preview/print_preview_handler.h index 452f311559..5500c32143 100644 --- a/chrome/browser/ui/webui/print_preview/print_preview_handler.h +++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.h @@ -67,6 +67,9 @@ class PrintPreviewHandler : public content::WebUIMessageHandler, void ShowSystemDialog(); private: + class AccessTokenService; + struct CUPSPrinterColorModels; + content::WebContents* preview_web_contents() const; // Gets the list of printers. |args| is unused. @@ -191,6 +194,13 @@ class PrintPreviewHandler : public content::WebUIMessageHandler, bool GetPreviewDataAndTitle(scoped_refptr<base::RefCountedBytes>* data, string16* title) const; +#if defined(USE_CUPS) + void SaveCUPSColorSetting(const base::DictionaryValue* settings); + + void ConvertColorSettingToCUPSColorModel( + base::DictionaryValue* settings) const; +#endif + // Pointer to current print system. scoped_refptr<printing::PrintBackend> print_backend_; @@ -216,9 +226,13 @@ class PrintPreviewHandler : public content::WebUIMessageHandler, scoped_ptr<base::FilePath> print_to_pdf_path_; // Holds token service to get OAuth2 access tokens. - class AccessTokenService; scoped_ptr<AccessTokenService> token_service_; +#if defined(USE_CUPS) + // The color capabilities from the last printer queried. + scoped_ptr<CUPSPrinterColorModels> cups_printer_color_models_; +#endif + base::WeakPtrFactory<PrintPreviewHandler> weak_factory_; DISALLOW_COPY_AND_ASSIGN(PrintPreviewHandler); diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc index a6352a679b..d01fffa40a 100644 --- a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc +++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc @@ -46,7 +46,7 @@ namespace { #if defined(OS_MACOSX) // U+0028 U+21E7 U+2318 U+0050 U+0029 in UTF8 const char kAdvancedPrintShortcut[] = "\x28\xE2\x8c\xA5\xE2\x8C\x98\x50\x29"; -#elif defined(OS_WIN) +#elif defined(OS_WIN) || defined(OS_CHROMEOS) const char kAdvancedPrintShortcut[] = "(Ctrl+Shift+P)"; #else const char kAdvancedPrintShortcut[] = "(Shift+Ctrl+P)"; diff --git a/chrome/browser/ui/window_sizer/window_sizer_ash_uitest.cc b/chrome/browser/ui/window_sizer/window_sizer_ash_uitest.cc index 65b40962e3..cd38b3ab0a 100644 --- a/chrome/browser/ui/window_sizer/window_sizer_ash_uitest.cc +++ b/chrome/browser/ui/window_sizer/window_sizer_ash_uitest.cc @@ -3,7 +3,7 @@ // found in the LICENSE file. #include "ash/launcher/launcher.h" -#include "ash/launcher/launcher_view.h" +#include "ash/shelf/shelf_view.h" #include "ash/shell.h" #include "ash/test/launcher_test_api.h" #include "base/command_line.h" @@ -45,9 +45,9 @@ void CloseBrowser(Browser* browser) { gfx::Rect GetChromeIconBoundsForRootWindow(aura::RootWindow* root_window) { ash::Launcher* launcher = ash::Launcher::ForWindow(root_window); - const ash::internal::LauncherView* launcher_view = - ash::test::LauncherTestAPI(launcher).launcher_view(); - const views::ViewModel* view_model = launcher_view->view_model_for_test(); + const ash::internal::ShelfView* shelf_view = + ash::test::LauncherTestAPI(launcher).shelf_view(); + const views::ViewModel* view_model = shelf_view->view_model_for_test(); EXPECT_EQ(2, view_model->view_size()); return view_model->view_at(1)->GetBoundsInScreen(); diff --git a/chrome/browser/unload_browsertest.cc b/chrome/browser/unload_browsertest.cc index 2d4917e5d4..433d20601f 100644 --- a/chrome/browser/unload_browsertest.cc +++ b/chrome/browser/unload_browsertest.cc @@ -406,11 +406,10 @@ IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseTabWhenOtherTabHasListener) { load_stop_observer.Wait(); CheckTitle("popup"); - content::WindowedNotificationObserver tab_close_observer( - content::NOTIFICATION_WEB_CONTENTS_DESTROYED, - content::NotificationService::AllSources()); + content::WebContentsDestroyedWatcher destroyed_watcher( + browser()->tab_strip_model()->GetActiveWebContents()); chrome::CloseTab(browser()); - tab_close_observer.Wait(); + destroyed_watcher.Wait(); CheckTitle("only_one_unload"); } @@ -494,6 +493,9 @@ IN_PROC_BROWSER_TEST_F(FastUnloadTest, MAYBE_UnloadHidden) { NavigateToPageInNewTab("unload_sets_cookie"); EXPECT_EQ("", GetCookies("no_listeners")); + content::WebContentsDestroyedWatcher destroyed_watcher( + browser()->tab_strip_model()->GetActiveWebContents()); + { base::RunLoop run_loop; FastTabCloseTabStripModelObserver observer( @@ -506,16 +508,10 @@ IN_PROC_BROWSER_TEST_F(FastUnloadTest, MAYBE_UnloadHidden) { CheckTitle("no_listeners"); EXPECT_EQ(1, browser()->tab_strip_model()->count()); - // Show that the web contents to go away after the was removed. - // Without unload-detached, this times-out because it happens earlier. - content::WindowedNotificationObserver contents_destroyed_observer( - content::NOTIFICATION_WEB_CONTENTS_DESTROYED, - content::NotificationService::AllSources()); - contents_destroyed_observer.Wait(); + // Wait for the actual destruction. + destroyed_watcher.Wait(); - // Browser still has the same tab. - CheckTitle("no_listeners"); - EXPECT_EQ(1, browser()->tab_strip_model()->count()); + // Verify that the destruction had the desired effect. EXPECT_EQ("unloaded=ohyeah", GetCookies("no_listeners")); } diff --git a/chrome/browser/web_applications/web_app_mac.mm b/chrome/browser/web_applications/web_app_mac.mm index dad22c57a4..e5b5c2c5ba 100644 --- a/chrome/browser/web_applications/web_app_mac.mm +++ b/chrome/browser/web_applications/web_app_mac.mm @@ -24,6 +24,8 @@ #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" #import "chrome/browser/mac/dock.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/web_applications/web_app_ui.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" @@ -133,12 +135,17 @@ bool AddGfxImageToIconFamily(IconFamilyHandle icon_family, base::FilePath GetWritableApplicationsDirectory() { base::FilePath path; - if (base::mac::GetLocalDirectory(NSApplicationDirectory, &path) && - base::PathIsWritable(path)) { - return path; + if (base::mac::GetUserDirectory(NSApplicationDirectory, &path)) { + if (!base::DirectoryExists(path)) { + if (!file_util::CreateDirectory(path)) + return base::FilePath(); + + // Create a zero-byte ".localized" file to inherit localizations from OSX + // for folders that have special meaning. + file_util::WriteFile(path.Append(".localized"), NULL, 0); + } + return base::PathIsWritable(path) ? path : base::FilePath(); } - if (base::mac::GetUserDirectory(NSApplicationDirectory, &path)) - return path; return base::FilePath(); } @@ -338,8 +345,34 @@ ShellIntegration::ShortcutInfo BuildShortcutInfoFromBundle( return shortcut_info; } +void CreateShortcutsAndRunCallback( + const base::Closure& close_callback, + const ShellIntegration::ShortcutInfo& shortcut_info) { + // creation_locations will be ignored by CreatePlatformShortcuts on Mac. + ShellIntegration::ShortcutLocations creation_locations; + web_app::CreateShortcuts(shortcut_info, creation_locations, + web_app::SHORTCUT_CREATION_BY_USER); + if (!close_callback.is_null()) + close_callback.Run(); +} + } // namespace +namespace chrome { + +void ShowCreateChromeAppShortcutsDialog(gfx::NativeWindow /*parent_window*/, + Profile* profile, + const extensions::Extension* app, + const base::Closure& close_callback) { + // Normally we would show a dialog, but since we always create the app + // shortcut in /Applications there are no options for the user to choose. + web_app::UpdateShortcutInfoAndIconForApp( + *app, profile, + base::Bind(&CreateShortcutsAndRunCallback, close_callback)); +} + +} // namespace chrome + namespace web_app { WebAppShortcutCreator::WebAppShortcutCreator( |