From 758b0b7056fc7b518147fd0546b3f615d9b6851a Mon Sep 17 00:00:00 2001 From: "ricow@chromium.org" Date: Thu, 10 Jan 2013 12:19:33 +0000 Subject: Revert revision 176015 This is a manual revert, drover did not like the deleted files > Run the JSON Schema Compiler's bundle compilation on JSON files. Previously it > was only run on IDL files. Clean up all the code which that simplifies. > > TBR=isherman@chromium.org,battre@chromium.org,akalin@chromium.org > BUG=141318 > > Review URL: https://chromiumcodereview.appspot.com/11747025 TBR=isherman@chromium.org,battre@chromium.org,akalin@chromium.org Review URL: https://codereview.chromium.org/11778097 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@176054 0039d316-1c4b-4281-b951-d872f2087c98 --- build/json_schema_bundle_compile.gypi | 6 +- build/json_schema_compile.gypi | 3 +- .../extensions/file_browser_handler_api.cc | 60 +- .../chromeos/extensions/file_browser_handler_api.h | 35 +- .../extensions/file_browser_handler_api_test.cc | 50 +- .../api/autotest_private/autotest_private_api.cc | 6 + .../extensions/api/bluetooth/bluetooth_apitest.cc | 4 +- .../bookmark_manager_private_api.h | 2 +- .../extensions/api/bookmarks/bookmark_api.cc | 996 +++++++++ .../extensions/api/bookmarks/bookmark_api.h | 321 +++ .../api/bookmarks/bookmark_api_factory.cc | 2 +- .../extensions/api/bookmarks/bookmarks_api.cc | 996 --------- .../extensions/api/bookmarks/bookmarks_api.h | 321 --- .../cloud_print_private/cloud_print_private_api.cc | 28 +- .../cloud_print_private/cloud_print_private_api.h | 18 +- .../api/content_settings/content_settings_api.cc | 19 +- .../api/content_settings/content_settings_api.h | 23 +- .../content_settings/content_settings_apitest.cc | 6 +- .../api/context_menu/context_menu_api.cc | 398 ++++ .../extensions/api/context_menu/context_menu_api.h | 58 + .../api/context_menu/context_menu_apitest.cc | 53 + .../api/context_menus/context_menu_apitest.cc | 53 - .../api/context_menus/context_menus_api.cc | 398 ---- .../api/context_menus/context_menus_api.h | 58 - .../browser/extensions/api/cookies/cookies_api.cc | 81 +- .../browser/extensions/api/cookies/cookies_api.h | 30 +- .../extensions/api/debugger/debugger_api.cc | 28 +- .../browser/extensions/api/debugger/debugger_api.h | 18 +- .../extensions/api/declarative/declarative_api.cc | 6 +- .../extensions/api/declarative/declarative_api.h | 18 +- .../api/font_settings/font_settings_api.cc | 45 +- .../api/font_settings/font_settings_api.h | 66 +- .../browser/extensions/api/history/history_api.cc | 34 +- .../browser/extensions/api/history/history_api.h | 28 +- chrome/browser/extensions/api/i18n/i18n_api.cc | 4 +- chrome/browser/extensions/api/i18n/i18n_api.h | 10 +- .../extensions/api/management/management_api.cc | 49 +- .../extensions/api/management/management_api.h | 33 +- .../api/management/management_api_browsertest.cc | 22 +- .../extensions/api/permissions/permissions_api.cc | 20 +- .../extensions/api/permissions/permissions_api.h | 18 +- .../api/permissions/permissions_apitest.cc | 16 +- .../extensions/api/processes/processes_api.cc | 1 + chrome/browser/extensions/api/record/record_api.cc | 30 +- chrome/browser/extensions/api/record/record_api.h | 18 +- .../extensions/api/record/record_api_test.cc | 14 +- .../storage/leveldb_settings_storage_factory.cc | 18 - .../api/storage/leveldb_settings_storage_factory.h | 25 - .../api/storage/managed_value_store_cache.cc | 250 --- .../api/storage/managed_value_store_cache.h | 154 -- .../extensions/api/storage/policy_value_store.cc | 163 -- .../extensions/api/storage/policy_value_store.h | 75 - .../api/storage/policy_value_store_unittest.cc | 217 -- .../extensions/api/storage/setting_sync_data.cc | 102 - .../extensions/api/storage/setting_sync_data.h | 91 - .../extensions/api/storage/settings_apitest.cc | 518 ----- .../extensions/api/storage/settings_backend.cc | 285 --- .../extensions/api/storage/settings_backend.h | 116 -- .../extensions/api/storage/settings_frontend.cc | 218 -- .../extensions/api/storage/settings_frontend.h | 93 - .../api/storage/settings_frontend_unittest.cc | 288 --- .../extensions/api/storage/settings_namespace.cc | 42 - .../extensions/api/storage/settings_namespace.h | 34 - .../extensions/api/storage/settings_observer.h | 30 - .../api/storage/settings_quota_unittest.cc | 594 ------ .../api/storage/settings_storage_factory.h | 36 - .../api/storage/settings_storage_quota_enforcer.cc | 235 --- .../api/storage/settings_storage_quota_enforcer.h | 69 - .../api/storage/settings_sync_processor.cc | 110 - .../api/storage/settings_sync_processor.h | 67 - .../api/storage/settings_sync_unittest.cc | 1425 ------------- .../extensions/api/storage/settings_sync_util.cc | 110 - .../extensions/api/storage/settings_sync_util.h | 51 - .../extensions/api/storage/settings_test_util.cc | 165 -- .../extensions/api/storage/settings_test_util.h | 117 -- .../browser/extensions/api/storage/storage_api.cc | 286 --- .../browser/extensions/api/storage/storage_api.h | 124 -- .../api/storage/sync_or_local_value_store_cache.cc | 115 -- .../api/storage/sync_or_local_value_store_cache.h | 62 - .../api/storage/syncable_settings_storage.cc | 384 ---- .../api/storage/syncable_settings_storage.h | 111 - .../extensions/api/storage/value_store_cache.cc | 13 - .../extensions/api/storage/value_store_cache.h | 57 - .../api/storage/weak_unlimited_settings_storage.cc | 67 - .../api/storage/weak_unlimited_settings_storage.h | 49 - .../api/tabs/execute_code_in_tab_function.cc | 277 +++ .../api/tabs/execute_code_in_tab_function.h | 94 + chrome/browser/extensions/api/tabs/tabs.cc | 1886 +++++++++++++++++ chrome/browser/extensions/api/tabs/tabs.h | 208 ++ chrome/browser/extensions/api/tabs/tabs_api.cc | 2136 -------------------- chrome/browser/extensions/api/tabs/tabs_api.h | 290 --- .../extensions/api/tabs/tabs_interactive_test.cc | 15 +- chrome/browser/extensions/api/tabs/tabs_test.cc | 70 +- .../api/web_navigation/web_navigation_api.cc | 6 +- .../api/web_navigation/web_navigation_api.h | 8 +- .../extensions/api/web_request/web_request_api.cc | 8 +- .../extensions/api/web_request/web_request_api.h | 5 +- chrome/browser/extensions/browser_event_router.h | 2 +- .../browser/extensions/content_script_apitest.cc | 4 +- chrome/browser/extensions/data_deleter.cc | 2 +- .../extensions/extension_function_registry.cc | 180 +- chrome/browser/extensions/extension_service.cc | 2 +- .../browser/extensions/platform_app_browsertest.cc | 4 +- .../extensions/platform_app_browsertest_util.cc | 6 +- .../settings/leveldb_settings_storage_factory.cc | 18 + .../settings/leveldb_settings_storage_factory.h | 25 + .../settings/managed_value_store_cache.cc | 250 +++ .../settings/managed_value_store_cache.h | 154 ++ .../extensions/settings/policy_value_store.cc | 163 ++ .../extensions/settings/policy_value_store.h | 75 + .../settings/policy_value_store_unittest.cc | 217 ++ .../extensions/settings/setting_sync_data.cc | 102 + .../extensions/settings/setting_sync_data.h | 91 + chrome/browser/extensions/settings/settings_api.cc | 286 +++ chrome/browser/extensions/settings/settings_api.h | 129 ++ .../extensions/settings/settings_apitest.cc | 518 +++++ .../extensions/settings/settings_backend.cc | 285 +++ .../browser/extensions/settings/settings_backend.h | 116 ++ .../extensions/settings/settings_frontend.cc | 218 ++ .../extensions/settings/settings_frontend.h | 93 + .../settings/settings_frontend_unittest.cc | 289 +++ .../extensions/settings/settings_namespace.cc | 42 + .../extensions/settings/settings_namespace.h | 34 + .../extensions/settings/settings_observer.h | 30 + .../extensions/settings/settings_quota_unittest.cc | 595 ++++++ .../extensions/settings/settings_storage_factory.h | 36 + .../settings/settings_storage_quota_enforcer.cc | 235 +++ .../settings/settings_storage_quota_enforcer.h | 69 + .../extensions/settings/settings_sync_processor.cc | 110 + .../extensions/settings/settings_sync_processor.h | 67 + .../extensions/settings/settings_sync_unittest.cc | 1426 +++++++++++++ .../extensions/settings/settings_sync_util.cc | 110 + .../extensions/settings/settings_sync_util.h | 51 + .../extensions/settings/settings_test_util.cc | 165 ++ .../extensions/settings/settings_test_util.h | 117 ++ .../settings/sync_or_local_value_store_cache.cc | 115 ++ .../settings/sync_or_local_value_store_cache.h | 62 + .../settings/syncable_settings_storage.cc | 384 ++++ .../settings/syncable_settings_storage.h | 111 + .../extensions/settings/value_store_cache.cc | 13 + .../extensions/settings/value_store_cache.h | 57 + .../settings/weak_unlimited_settings_storage.cc | 67 + .../settings/weak_unlimited_settings_storage.h | 49 + .../performance_monitor/performance_monitor.gyp | 4 +- chrome/browser/prefs/browser_prefs.cc | 4 +- .../glue/chrome_extensions_activity_monitor.cc | 2 +- .../chrome_extensions_activity_monitor_unittest.cc | 34 +- .../sync/profile_sync_components_factory_impl.cc | 4 +- .../test/integration/extension_settings_helper.cc | 4 +- .../ui/cocoa/location_bar/location_bar_view_mac.mm | 2 +- chrome/chrome_browser_extensions.gypi | 87 +- chrome/chrome_tests.gypi | 4 +- chrome/chrome_tests_unit.gypi | 14 +- chrome/common/extensions/api/api.gyp | 55 +- chrome/common/extensions/api/content_settings.json | 3 - chrome/common/extensions/api/events.json | 4 - chrome/common/extensions/api/extension_api.cc | 40 + .../api/file_browser_handler_internal.json | 3 - chrome/common/extensions/api/i18n.json | 1 - chrome/common/extensions/api/storage.json | 3 - chrome/common/extensions/api/windows.json | 3 - .../extensions/docs/server2/api_data_source.py | 7 +- chrome/common/extensions_api_resources.grd | 20 + tools/json_schema_compiler/code.py | 8 +- tools/json_schema_compiler/compiler.py | 11 +- tools/json_schema_compiler/model.py | 76 +- .../schema_bundle_generator.py | 56 +- .../test/json_schema_compiler_tests.gyp | 15 +- 168 files changed, 12123 insertions(+), 11987 deletions(-) create mode 100644 chrome/browser/extensions/api/bookmarks/bookmark_api.cc create mode 100644 chrome/browser/extensions/api/bookmarks/bookmark_api.h delete mode 100644 chrome/browser/extensions/api/bookmarks/bookmarks_api.cc delete mode 100644 chrome/browser/extensions/api/bookmarks/bookmarks_api.h create mode 100644 chrome/browser/extensions/api/context_menu/context_menu_api.cc create mode 100644 chrome/browser/extensions/api/context_menu/context_menu_api.h create mode 100644 chrome/browser/extensions/api/context_menu/context_menu_apitest.cc delete mode 100644 chrome/browser/extensions/api/context_menus/context_menu_apitest.cc delete mode 100644 chrome/browser/extensions/api/context_menus/context_menus_api.cc delete mode 100644 chrome/browser/extensions/api/context_menus/context_menus_api.h delete mode 100644 chrome/browser/extensions/api/storage/leveldb_settings_storage_factory.cc delete mode 100644 chrome/browser/extensions/api/storage/leveldb_settings_storage_factory.h delete mode 100644 chrome/browser/extensions/api/storage/managed_value_store_cache.cc delete mode 100644 chrome/browser/extensions/api/storage/managed_value_store_cache.h delete mode 100644 chrome/browser/extensions/api/storage/policy_value_store.cc delete mode 100644 chrome/browser/extensions/api/storage/policy_value_store.h delete mode 100644 chrome/browser/extensions/api/storage/policy_value_store_unittest.cc delete mode 100644 chrome/browser/extensions/api/storage/setting_sync_data.cc delete mode 100644 chrome/browser/extensions/api/storage/setting_sync_data.h delete mode 100644 chrome/browser/extensions/api/storage/settings_apitest.cc delete mode 100644 chrome/browser/extensions/api/storage/settings_backend.cc delete mode 100644 chrome/browser/extensions/api/storage/settings_backend.h delete mode 100644 chrome/browser/extensions/api/storage/settings_frontend.cc delete mode 100644 chrome/browser/extensions/api/storage/settings_frontend.h delete mode 100644 chrome/browser/extensions/api/storage/settings_frontend_unittest.cc delete mode 100644 chrome/browser/extensions/api/storage/settings_namespace.cc delete mode 100644 chrome/browser/extensions/api/storage/settings_namespace.h delete mode 100644 chrome/browser/extensions/api/storage/settings_observer.h delete mode 100644 chrome/browser/extensions/api/storage/settings_quota_unittest.cc delete mode 100644 chrome/browser/extensions/api/storage/settings_storage_factory.h delete mode 100644 chrome/browser/extensions/api/storage/settings_storage_quota_enforcer.cc delete mode 100644 chrome/browser/extensions/api/storage/settings_storage_quota_enforcer.h delete mode 100644 chrome/browser/extensions/api/storage/settings_sync_processor.cc delete mode 100644 chrome/browser/extensions/api/storage/settings_sync_processor.h delete mode 100644 chrome/browser/extensions/api/storage/settings_sync_unittest.cc delete mode 100644 chrome/browser/extensions/api/storage/settings_sync_util.cc delete mode 100644 chrome/browser/extensions/api/storage/settings_sync_util.h delete mode 100644 chrome/browser/extensions/api/storage/settings_test_util.cc delete mode 100644 chrome/browser/extensions/api/storage/settings_test_util.h delete mode 100644 chrome/browser/extensions/api/storage/storage_api.cc delete mode 100644 chrome/browser/extensions/api/storage/storage_api.h delete mode 100644 chrome/browser/extensions/api/storage/sync_or_local_value_store_cache.cc delete mode 100644 chrome/browser/extensions/api/storage/sync_or_local_value_store_cache.h delete mode 100644 chrome/browser/extensions/api/storage/syncable_settings_storage.cc delete mode 100644 chrome/browser/extensions/api/storage/syncable_settings_storage.h delete mode 100644 chrome/browser/extensions/api/storage/value_store_cache.cc delete mode 100644 chrome/browser/extensions/api/storage/value_store_cache.h delete mode 100644 chrome/browser/extensions/api/storage/weak_unlimited_settings_storage.cc delete mode 100644 chrome/browser/extensions/api/storage/weak_unlimited_settings_storage.h create mode 100644 chrome/browser/extensions/api/tabs/execute_code_in_tab_function.cc create mode 100644 chrome/browser/extensions/api/tabs/execute_code_in_tab_function.h create mode 100644 chrome/browser/extensions/api/tabs/tabs.cc create mode 100644 chrome/browser/extensions/api/tabs/tabs.h delete mode 100644 chrome/browser/extensions/api/tabs/tabs_api.cc delete mode 100644 chrome/browser/extensions/api/tabs/tabs_api.h create mode 100644 chrome/browser/extensions/settings/leveldb_settings_storage_factory.cc create mode 100644 chrome/browser/extensions/settings/leveldb_settings_storage_factory.h create mode 100644 chrome/browser/extensions/settings/managed_value_store_cache.cc create mode 100644 chrome/browser/extensions/settings/managed_value_store_cache.h create mode 100644 chrome/browser/extensions/settings/policy_value_store.cc create mode 100644 chrome/browser/extensions/settings/policy_value_store.h create mode 100644 chrome/browser/extensions/settings/policy_value_store_unittest.cc create mode 100644 chrome/browser/extensions/settings/setting_sync_data.cc create mode 100644 chrome/browser/extensions/settings/setting_sync_data.h create mode 100644 chrome/browser/extensions/settings/settings_api.cc create mode 100644 chrome/browser/extensions/settings/settings_api.h create mode 100644 chrome/browser/extensions/settings/settings_apitest.cc create mode 100644 chrome/browser/extensions/settings/settings_backend.cc create mode 100644 chrome/browser/extensions/settings/settings_backend.h create mode 100644 chrome/browser/extensions/settings/settings_frontend.cc create mode 100644 chrome/browser/extensions/settings/settings_frontend.h create mode 100644 chrome/browser/extensions/settings/settings_frontend_unittest.cc create mode 100644 chrome/browser/extensions/settings/settings_namespace.cc create mode 100644 chrome/browser/extensions/settings/settings_namespace.h create mode 100644 chrome/browser/extensions/settings/settings_observer.h create mode 100644 chrome/browser/extensions/settings/settings_quota_unittest.cc create mode 100644 chrome/browser/extensions/settings/settings_storage_factory.h create mode 100644 chrome/browser/extensions/settings/settings_storage_quota_enforcer.cc create mode 100644 chrome/browser/extensions/settings/settings_storage_quota_enforcer.h create mode 100644 chrome/browser/extensions/settings/settings_sync_processor.cc create mode 100644 chrome/browser/extensions/settings/settings_sync_processor.h create mode 100644 chrome/browser/extensions/settings/settings_sync_unittest.cc create mode 100644 chrome/browser/extensions/settings/settings_sync_util.cc create mode 100644 chrome/browser/extensions/settings/settings_sync_util.h create mode 100644 chrome/browser/extensions/settings/settings_test_util.cc create mode 100644 chrome/browser/extensions/settings/settings_test_util.h create mode 100644 chrome/browser/extensions/settings/sync_or_local_value_store_cache.cc create mode 100644 chrome/browser/extensions/settings/sync_or_local_value_store_cache.h create mode 100644 chrome/browser/extensions/settings/syncable_settings_storage.cc create mode 100644 chrome/browser/extensions/settings/syncable_settings_storage.h create mode 100644 chrome/browser/extensions/settings/value_store_cache.cc create mode 100644 chrome/browser/extensions/settings/value_store_cache.h create mode 100644 chrome/browser/extensions/settings/weak_unlimited_settings_storage.cc create mode 100644 chrome/browser/extensions/settings/weak_unlimited_settings_storage.h diff --git a/build/json_schema_bundle_compile.gypi b/build/json_schema_bundle_compile.gypi index 11a6a28..ecefe41 100644 --- a/build/json_schema_bundle_compile.gypi +++ b/build/json_schema_bundle_compile.gypi @@ -5,7 +5,7 @@ { 'variables': { # When including this gypi, the following variables must be set: - # schema_files: an array of json or idl files that comprise the api model. + # idl_schema_files: an array of idl files that comprise the api model. # cc_dir: path to generated files # root_namespace: the C++ namespace that all generated files go under # Functions and namespaces can be excluded by setting "nocompile" to true. @@ -27,7 +27,7 @@ '<(api_gen_dir)/model.py', '<(api_gen_dir)/schema_bundle_generator.py', '<(api_gen_dir)/util_cc_helper.py', - '<@(schema_files)', + '<@(idl_schema_files)', ], 'outputs': [ '<(SHARED_INTERMEDIATE_DIR)/<(cc_dir)/generated_api.h', @@ -41,7 +41,7 @@ '--destdir=<(SHARED_INTERMEDIATE_DIR)', '--namespace=<(root_namespace)', '--bundle', - '<@(schema_files)', + '<@(idl_schema_files)', ], 'message': 'Generating C++ API bundle code', 'process_outputs_as_sources': 1, diff --git a/build/json_schema_compile.gypi b/build/json_schema_compile.gypi index 6da8961..6c8f69c 100644 --- a/build/json_schema_compile.gypi +++ b/build/json_schema_compile.gypi @@ -5,7 +5,8 @@ { 'variables': { # When including this gypi, the following variables must be set: - # schema_files: a list of json or IDL files that comprise the api model. + # json_schema_files: a list of json files that comprise the api model. + # idl_schema_files: a list of IDL files that comprise the api model. # cc_dir: path to generated files # root_namespace: the C++ namespace that all generated files go under # Functions and namespaces can be excluded by setting "nocompile" to true. diff --git a/chrome/browser/chromeos/extensions/file_browser_handler_api.cc b/chrome/browser/chromeos/extensions/file_browser_handler_api.cc index 45e8161..940a04f 100644 --- a/chrome/browser/chromeos/extensions/file_browser_handler_api.cc +++ b/chrome/browser/chromeos/extensions/file_browser_handler_api.cc @@ -10,10 +10,10 @@ // - Display 'save as' dialog using FileSelectorImpl which waits for the user // feedback. // - Once the user selects the file path (or cancels the selection), -// FileSelectorImpl notifies FileBrowserHandlerInternalSelectFileFunction of -// the selection result by calling FileHandlerSelectFile::OnFilePathSelected. -// - If the selection was canceled, -// FileBrowserHandlerInternalSelectFileFunction returns reporting failure. +// FileSelectorImpl notifies FileHandlerSelectFileFunction of the selection +// result by calling FileHandlerSelectFile::OnFilePathSelected. +// - If the selection was canceled, FileHandlerSelectFileFunction returns +// reporting failure. // - If the file path was selected, the function opens external file system // needed to create FileEntry object for the selected path // (opening file system will create file system name and root url for the @@ -119,11 +119,10 @@ class FileSelectorImpl : public FileSelector, // After this method is called, the selector implementation should not be // deleted by the caller. It will delete itself after it receives response // from SelectFielDialog. - virtual void SelectFile( - const FilePath& suggested_name, - const std::vector& allowed_extensions, - Browser* browser, - FileBrowserHandlerInternalSelectFileFunction* function) OVERRIDE; + virtual void SelectFile(const FilePath& suggested_name, + const std::vector& allowed_extensions, + Browser* browser, + FileHandlerSelectFileFunction* function) OVERRIDE; // ui::SelectFileDialog::Listener overrides. virtual void FileSelected(const FilePath& path, @@ -159,7 +158,7 @@ class FileSelectorImpl : public FileSelector, scoped_refptr dialog_; // Extension function that uses the selector. - scoped_refptr function_; + scoped_refptr function_; DISALLOW_COPY_AND_ASSIGN(FileSelectorImpl); }; @@ -178,7 +177,7 @@ void FileSelectorImpl::SelectFile( const FilePath& suggested_name, const std::vector& allowed_extensions, Browser* browser, - FileBrowserHandlerInternalSelectFileFunction* function) { + FileHandlerSelectFileFunction* function) { // We will hold reference to the function until it is notified of selection // result. function_ = function; @@ -290,25 +289,22 @@ void RunOpenFileSystemCallback( } // namespace -FileBrowserHandlerInternalSelectFileFunction:: - FileBrowserHandlerInternalSelectFileFunction() - : file_selector_factory_(new FileSelectorFactoryImpl()), - user_gesture_check_enabled_(true) { +FileHandlerSelectFileFunction::FileHandlerSelectFileFunction() + : file_selector_factory_(new FileSelectorFactoryImpl()), + user_gesture_check_enabled_(true) { } -FileBrowserHandlerInternalSelectFileFunction:: - FileBrowserHandlerInternalSelectFileFunction( - FileSelectorFactory* file_selector_factory, - bool enable_user_gesture_check) - : file_selector_factory_(file_selector_factory), - user_gesture_check_enabled_(enable_user_gesture_check) { +FileHandlerSelectFileFunction::FileHandlerSelectFileFunction( + FileSelectorFactory* file_selector_factory, + bool enable_user_gesture_check) + : file_selector_factory_(file_selector_factory), + user_gesture_check_enabled_(enable_user_gesture_check) { DCHECK(file_selector_factory); } -FileBrowserHandlerInternalSelectFileFunction:: - ~FileBrowserHandlerInternalSelectFileFunction() {} +FileHandlerSelectFileFunction::~FileHandlerSelectFileFunction() {} -bool FileBrowserHandlerInternalSelectFileFunction::RunImpl() { +bool FileHandlerSelectFileFunction::RunImpl() { scoped_ptr params(SelectFile::Params::Create(*args_)); FilePath suggested_name(params->selection_params.suggested_name); @@ -329,7 +325,7 @@ bool FileBrowserHandlerInternalSelectFileFunction::RunImpl() { return true; } -void FileBrowserHandlerInternalSelectFileFunction::OnFilePathSelected( +void FileHandlerSelectFileFunction::OnFilePathSelected( bool success, const FilePath& full_path) { if (!success) { @@ -346,12 +342,11 @@ void FileBrowserHandlerInternalSelectFileFunction::OnFilePathSelected( source_url_.GetOrigin(), fileapi::kFileSystemTypeExternal, false, base::Bind( &RunOpenFileSystemCallback, - base::Bind(&FileBrowserHandlerInternalSelectFileFunction:: - OnFileSystemOpened, + base::Bind(&FileHandlerSelectFileFunction::OnFileSystemOpened, this))); }; -void FileBrowserHandlerInternalSelectFileFunction::OnFileSystemOpened( +void FileHandlerSelectFileFunction::OnFileSystemOpened( bool success, const std::string& file_system_name, const GURL& file_system_root) { @@ -369,7 +364,7 @@ void FileBrowserHandlerInternalSelectFileFunction::OnFileSystemOpened( GrantPermissions(); } -void FileBrowserHandlerInternalSelectFileFunction::GrantPermissions() { +void FileHandlerSelectFileFunction::GrantPermissions() { fileapi::ExternalFileSystemMountPointProvider* external_provider = BrowserContext::GetDefaultStoragePartition(profile_)-> GetFileSystemContext()->external_provider(); @@ -405,12 +400,11 @@ void FileBrowserHandlerInternalSelectFileFunction::GrantPermissions() { profile(), gdata_paths.Pass(), &permissions_to_grant_, - base::Bind(&FileBrowserHandlerInternalSelectFileFunction:: - OnGotPermissionsToGrant, + base::Bind(&FileHandlerSelectFileFunction::OnGotPermissionsToGrant, this)); } -void FileBrowserHandlerInternalSelectFileFunction::OnGotPermissionsToGrant() { +void FileHandlerSelectFileFunction::OnGotPermissionsToGrant() { // At this point all needed permissions should be collected, so let's grant // them. for (size_t i = 0; i < permissions_to_grant_.size(); i++) { @@ -423,7 +417,7 @@ void FileBrowserHandlerInternalSelectFileFunction::OnGotPermissionsToGrant() { Respond(true); } -void FileBrowserHandlerInternalSelectFileFunction::Respond(bool success) { +void FileHandlerSelectFileFunction::Respond(bool success) { scoped_ptr result( new SelectFile::Results::Result()); result->success = success; diff --git a/chrome/browser/chromeos/extensions/file_browser_handler_api.h b/chrome/browser/chromeos/extensions/file_browser_handler_api.h index db9a8db..a4a125d 100644 --- a/chrome/browser/chromeos/extensions/file_browser_handler_api.h +++ b/chrome/browser/chromeos/extensions/file_browser_handler_api.h @@ -19,14 +19,14 @@ #include "chrome/browser/extensions/extension_function.h" class Browser; -class FileBrowserHandlerInternalSelectFileFunction; +class FileHandlerSelectFileFunction; namespace file_handler { -// Interface that is used by FileBrowserHandlerInternalSelectFileFunction to -// select the file path that should be reported back to the extension function -// caller. Nobody will take the ownership of the interface implementation, so -// it should delete itself once it's done. +// Interface that is used by FileHandlerSelectFileFunction to select the file +// path that should be reported back to the extension function caller. +// Nobody will take the ownership of the interface implementation, so it should +// delete itself once it's done. class FileSelector { public: virtual ~FileSelector() {} @@ -48,21 +48,19 @@ class FileSelector { // |SelectFile| will be called at most once by a single extension function. // The interface implementation should delete itself after the extension // function is notified of file selection result. - virtual void SelectFile( - const FilePath& suggested_name, - const std::vector& allowed_extensions, - Browser* browser, - FileBrowserHandlerInternalSelectFileFunction* function) = 0; + virtual void SelectFile(const FilePath& suggested_name, + const std::vector& allowed_extensions, + Browser* browser, + FileHandlerSelectFileFunction* function) = 0; }; -// Interface that is used by FileBrowserHandlerInternalSelectFileFunction to -// create a FileSelector it can use to select a file path. +// Interface that is used by FileHandlerSelectFileFunction to create a +// FileSelector it can use to select a file path. class FileSelectorFactory { public: virtual ~FileSelectorFactory() {} - // Creates a FileSelector instance for the - // FileBrowserHandlerInternalSelectFileFunction. + // Creates a FileSelector instance for the FileHandlerSelectFileFunction. virtual FileSelector* CreateFileSelector() const = 0; }; @@ -70,19 +68,18 @@ class FileSelectorFactory { // The fileBrowserHandlerInternal.selectFile extension function implementation. // See the file description for more info. -class FileBrowserHandlerInternalSelectFileFunction - : public AsyncExtensionFunction { +class FileHandlerSelectFileFunction : public AsyncExtensionFunction { public: // Default constructor used in production code. // It will create its own FileSelectorFactory implementation, and set the // value of |user_gesture_check_enabled| to true. - FileBrowserHandlerInternalSelectFileFunction(); + FileHandlerSelectFileFunction(); // This constructor should be used only in tests to inject test file selector // factory and to allow extension function to run even if it hasn't been // invoked by user gesture. // Created object will take the ownership of the |file_selector_factory|. - FileBrowserHandlerInternalSelectFileFunction( + FileHandlerSelectFileFunction( file_handler::FileSelectorFactory* file_selector_factory, bool enable_user_gesture_check); @@ -96,7 +93,7 @@ class FileBrowserHandlerInternalSelectFileFunction protected: // The class is ref counted, so destructor should not be public. - virtual ~FileBrowserHandlerInternalSelectFileFunction(); + virtual ~FileHandlerSelectFileFunction() OVERRIDE; // AsyncExtensionFunction implementation. // Runs the extension function implementation. diff --git a/chrome/browser/chromeos/extensions/file_browser_handler_api_test.cc b/chrome/browser/chromeos/extensions/file_browser_handler_api_test.cc index c605ab7..5689100 100644 --- a/chrome/browser/chromeos/extensions/file_browser_handler_api_test.cc +++ b/chrome/browser/chromeos/extensions/file_browser_handler_api_test.cc @@ -64,7 +64,7 @@ void ExpectFileContentEquals(const FilePath& selected_path, EXPECT_EQ(expected_contents, test_file_contents); } -// Mocks FileSelector used by FileBrowserHandlerInternalSelectFileFunction. +// Mocks FileSelector used by FileHandlerSelectFileFunction. // When |SelectFile| is called, it will check that file name suggestion is as // expected, and respond to the extension function with specified selection // results. @@ -83,11 +83,10 @@ class MockFileSelector : public file_handler::FileSelector { // file_handler::FileSelector implementation. // |browser| is not used. - virtual void SelectFile( - const FilePath& suggested_name, - const std::vector& allowed_extensions, - Browser* browser, - FileBrowserHandlerInternalSelectFileFunction* function) OVERRIDE { + virtual void SelectFile(const FilePath& suggested_name, + const std::vector& allowed_extensions, + Browser* browser, + FileHandlerSelectFileFunction* function) OVERRIDE { // Confirm that the function suggested us the right name. EXPECT_EQ(suggested_name_, suggested_name); // Confirm that the function allowed us the right extensions. @@ -101,9 +100,8 @@ class MockFileSelector : public file_handler::FileSelector { // Send response to the extension function. // The callback will take a reference to the function and keep it alive. base::MessageLoopProxy::current()->PostTask(FROM_HERE, - base::Bind(&FileBrowserHandlerInternalSelectFileFunction:: - OnFilePathSelected, - function, success_, selected_path_)); + base::Bind(&FileHandlerSelectFileFunction::OnFilePathSelected, + function, success_, selected_path_)); delete this; } @@ -184,10 +182,10 @@ class FileBrowserHandlerExtensionTest : public ExtensionApiTest { return tmp_mount_point_.Append(relative_path); } - // Creates a new FileBrowserHandlerInternalSelectFileFunction to be used in - // the test. This function will be called from ExtensionFunctinoDispatcher - // whenever an extension function for fileBrowserHandlerInternal.selectFile - // will be needed. + // Creates a new FileHandlerSelectFileFunction to be used in the test. + // This function will be called from ExtensionFunctinoDispatcher whenever + // an extension function for fileBrowserHandlerInternal.selectFile will be + // needed. static ExtensionFunction* TestSelectFileFunctionFactory() { EXPECT_TRUE(test_cases_); EXPECT_TRUE(current_test_case_ < test_cases_->size()); @@ -195,15 +193,14 @@ class FileBrowserHandlerExtensionTest : public ExtensionApiTest { // If this happens, test failed. But, we still don't want to crash, so // return valid extension function. if (!test_cases_ && current_test_case_ >= test_cases_->size()) - return new FileBrowserHandlerInternalSelectFileFunction(); + return new FileHandlerSelectFileFunction(); // Create file creator factory for the current test case. MockFileSelectorFactory* mock_factory = new MockFileSelectorFactory(test_cases_->at(current_test_case_)); current_test_case_++; - return new FileBrowserHandlerInternalSelectFileFunction( - mock_factory, false); + return new FileHandlerSelectFileFunction(mock_factory, false); } // Sets up test parameters for extension function invocations that will be @@ -292,9 +289,8 @@ IN_PROC_BROWSER_TEST_F(FileBrowserHandlerExtensionTest, EndToEnd) { // Tests that verifies the fileBrowserHandlerInternal.selectFile function fails // when invoked without user gesture. IN_PROC_BROWSER_TEST_F(FileBrowserHandlerExtensionTest, NoUserGesture) { - scoped_refptr - select_file_function( - new FileBrowserHandlerInternalSelectFileFunction()); + scoped_refptr select_file_function( + new FileHandlerSelectFileFunction()); std::string error = utils::RunFunctionAndReturnError( @@ -317,11 +313,9 @@ IN_PROC_BROWSER_TEST_F(FileBrowserHandlerExtensionTest, SelectionFailed) { false, FilePath()); - scoped_refptr - select_file_function( - new FileBrowserHandlerInternalSelectFileFunction( - new MockFileSelectorFactory(test_case), - false)); + scoped_refptr select_file_function( + new FileHandlerSelectFileFunction(new MockFileSelectorFactory(test_case), + false)); select_file_function->set_has_callback(true); select_file_function->set_user_gesture(true); @@ -346,11 +340,9 @@ IN_PROC_BROWSER_TEST_F(FileBrowserHandlerExtensionTest, SuggestedFullPath) { false, FilePath()); - scoped_refptr - select_file_function( - new FileBrowserHandlerInternalSelectFileFunction( - new MockFileSelectorFactory(test_case), - false)); + scoped_refptr select_file_function( + new FileHandlerSelectFileFunction(new MockFileSelectorFactory(test_case), + false)); select_file_function->set_has_callback(true); select_file_function->set_user_gesture(true); diff --git a/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc b/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc index 8f736a5..6e48b67 100644 --- a/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc +++ b/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc @@ -88,6 +88,12 @@ bool AutotestPrivateLoginStatusFunction::RunImpl() { } AutotestPrivateAPI::AutotestPrivateAPI() : test_mode_(false) { + ExtensionFunctionRegistry* registry = + ExtensionFunctionRegistry::GetInstance(); + registry->RegisterFunction(); + registry->RegisterFunction(); + registry->RegisterFunction(); + registry->RegisterFunction(); } AutotestPrivateAPI::~AutotestPrivateAPI() { diff --git a/chrome/browser/extensions/api/bluetooth/bluetooth_apitest.cc b/chrome/browser/extensions/api/bluetooth/bluetooth_apitest.cc index a245283..c3e3a65 100644 --- a/chrome/browser/extensions/api/bluetooth/bluetooth_apitest.cc +++ b/chrome/browser/extensions/api/bluetooth/bluetooth_apitest.cc @@ -449,8 +449,8 @@ IN_PROC_BROWSER_TEST_F(BluetoothApiTest, GetDevicesError) { } IN_PROC_BROWSER_TEST_F(BluetoothApiTest, Permissions) { - PermissionsRequestFunction::SetAutoConfirmForTests(true); - PermissionsRequestFunction::SetIgnoreUserGestureForTests(true); + RequestPermissionsFunction::SetAutoConfirmForTests(true); + RequestPermissionsFunction::SetIgnoreUserGestureForTests(true); EXPECT_CALL(*mock_adapter_, GetDevice(device1_->address())) .WillOnce(testing::Return(device1_.get())); diff --git a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.h b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.h index 77b5850..0ed0bb2 100644 --- a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.h +++ b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.h @@ -7,7 +7,7 @@ #include "base/values.h" #include "chrome/browser/bookmarks/bookmark_node_data.h" -#include "chrome/browser/extensions/api/bookmarks/bookmarks_api.h" +#include "chrome/browser/extensions/api/bookmarks/bookmark_api.h" #include "chrome/browser/extensions/extension_function.h" #include "chrome/browser/ui/bookmarks/bookmark_tab_helper.h" diff --git a/chrome/browser/extensions/api/bookmarks/bookmark_api.cc b/chrome/browser/extensions/api/bookmarks/bookmark_api.cc new file mode 100644 index 0000000..c076813 --- /dev/null +++ b/chrome/browser/extensions/api/bookmarks/bookmark_api.cc @@ -0,0 +1,996 @@ +// 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/api/bookmarks/bookmark_api.h" + +#include "base/bind.h" +#include "base/file_path.h" +#include "base/i18n/file_util_icu.h" +#include "base/i18n/time_formatting.h" +#include "base/json/json_writer.h" +#include "base/memory/scoped_ptr.h" +#include "base/path_service.h" +#include "base/prefs/public/pref_service_base.h" +#include "base/sha1.h" +#include "base/stl_util.h" +#include "base/string16.h" +#include "base/string_number_conversions.h" +#include "base/string_util.h" +#include "base/time.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/bookmarks/bookmark_codec.h" +#include "chrome/browser/bookmarks/bookmark_html_writer.h" +#include "chrome/browser/bookmarks/bookmark_model.h" +#include "chrome/browser/bookmarks/bookmark_model_factory.h" +#include "chrome/browser/bookmarks/bookmark_utils.h" +#include "chrome/browser/extensions/api/bookmarks/bookmark_api_constants.h" +#include "chrome/browser/extensions/api/bookmarks/bookmark_api_helpers.h" +#include "chrome/browser/extensions/event_router.h" +#include "chrome/browser/extensions/extension_function_dispatcher.h" +#include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extensions_quota_service.h" +#include "chrome/browser/importer/importer_data_types.h" +#include "chrome/browser/importer/importer_host.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/chrome_select_file_policy.h" +#include "chrome/common/chrome_notification_types.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/extensions/api/bookmarks.h" +#include "chrome/common/pref_names.h" +#include "content/public/browser/notification_service.h" +#include "grit/generated_resources.h" +#include "ui/base/l10n/l10n_util.h" + +namespace extensions { + +namespace keys = bookmark_api_constants; +namespace bookmarks = api::bookmarks; + +using base::TimeDelta; +using bookmarks::BookmarkTreeNode; +using content::BrowserThread; +using content::WebContents; + +typedef QuotaLimitHeuristic::Bucket Bucket; +typedef QuotaLimitHeuristic::Config Config; +typedef QuotaLimitHeuristic::BucketList BucketList; +typedef ExtensionsQuotaService::TimedLimit TimedLimit; +typedef ExtensionsQuotaService::SustainedLimit SustainedLimit; +typedef QuotaLimitHeuristic::BucketMapper BucketMapper; + +namespace { + +// Generates a default path (including a default filename) that will be +// used for pre-populating the "Export Bookmarks" file chooser dialog box. +FilePath GetDefaultFilepathForBookmarkExport() { + base::Time time = base::Time::Now(); + + // Concatenate a date stamp to the filename. +#if defined(OS_POSIX) + FilePath::StringType filename = + l10n_util::GetStringFUTF8(IDS_EXPORT_BOOKMARKS_DEFAULT_FILENAME, + base::TimeFormatShortDateNumeric(time)); +#elif defined(OS_WIN) + FilePath::StringType filename = + l10n_util::GetStringFUTF16(IDS_EXPORT_BOOKMARKS_DEFAULT_FILENAME, + base::TimeFormatShortDateNumeric(time)); +#endif + + file_util::ReplaceIllegalCharactersInPath(&filename, '_'); + + FilePath default_path; + PathService::Get(chrome::DIR_USER_DOCUMENTS, &default_path); + return default_path.Append(filename); +} + +} // namespace + +void BookmarksFunction::Run() { + BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); + if (!model->IsLoaded()) { + // Bookmarks are not ready yet. We'll wait. + registrar_.Add( + this, chrome::NOTIFICATION_BOOKMARK_MODEL_LOADED, + content::NotificationService::AllBrowserContextsAndSources()); + AddRef(); // Balanced in Observe(). + return; + } + + bool success = RunImpl(); + if (success) { + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_EXTENSION_BOOKMARKS_API_INVOKED, + content::Source(GetExtension()), + content::Details(this)); + } + SendResponse(success); +} + +bool BookmarksFunction::GetBookmarkIdAsInt64( + const std::string& id_string, int64* id) { + if (base::StringToInt64(id_string, id)) + return true; + + error_ = keys::kInvalidIdError; + return false; +} + +bool BookmarksFunction::EditBookmarksEnabled() { + PrefServiceBase* prefs = PrefServiceBase::FromBrowserContext(profile_); + if (prefs->GetBoolean(prefs::kEditBookmarksEnabled)) + return true; + error_ = keys::kEditBookmarksDisabled; + return false; +} + +void BookmarksFunction::Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + DCHECK(type == chrome::NOTIFICATION_BOOKMARK_MODEL_LOADED); + Profile* source_profile = content::Source(source).ptr(); + if (!source_profile || !source_profile->IsSameProfile(profile())) + return; + + DCHECK(BookmarkModelFactory::GetForProfile(profile())->IsLoaded()); + Run(); + Release(); // Balanced in Run(). +} + +BookmarkEventRouter::BookmarkEventRouter(BookmarkModel* model) : model_(model) { + model_->AddObserver(this); +} + +BookmarkEventRouter::~BookmarkEventRouter() { + if (model_) { + model_->RemoveObserver(this); + } +} + +void BookmarkEventRouter::DispatchEvent( + Profile* profile, + const char* event_name, + scoped_ptr event_args) { + if (extensions::ExtensionSystem::Get(profile)->event_router()) { + extensions::ExtensionSystem::Get(profile)->event_router()->BroadcastEvent( + make_scoped_ptr(new extensions::Event(event_name, event_args.Pass()))); + } +} + +void BookmarkEventRouter::Loaded(BookmarkModel* model, bool ids_reassigned) { + // TODO(erikkay): Perhaps we should send this event down to the extension + // so they know when it's safe to use the API? +} + +void BookmarkEventRouter::BookmarkModelBeingDeleted(BookmarkModel* model) { + model_ = NULL; +} + +void BookmarkEventRouter::BookmarkNodeMoved(BookmarkModel* model, + const BookmarkNode* old_parent, + int old_index, + const BookmarkNode* new_parent, + int new_index) { + scoped_ptr args(new ListValue()); + const BookmarkNode* node = new_parent->GetChild(new_index); + args->Append(new StringValue(base::Int64ToString(node->id()))); + DictionaryValue* object_args = new DictionaryValue(); + object_args->SetString(keys::kParentIdKey, + base::Int64ToString(new_parent->id())); + object_args->SetInteger(keys::kIndexKey, new_index); + object_args->SetString(keys::kOldParentIdKey, + base::Int64ToString(old_parent->id())); + object_args->SetInteger(keys::kOldIndexKey, old_index); + args->Append(object_args); + + DispatchEvent(model->profile(), keys::kOnBookmarkMoved, args.Pass()); +} + +void BookmarkEventRouter::BookmarkNodeAdded(BookmarkModel* model, + const BookmarkNode* parent, + int index) { + scoped_ptr args(new ListValue()); + const BookmarkNode* node = parent->GetChild(index); + args->Append(new StringValue(base::Int64ToString(node->id()))); + scoped_ptr tree_node( + bookmark_api_helpers::GetBookmarkTreeNode(node, false, false)); + args->Append(tree_node->ToValue().release()); + + DispatchEvent(model->profile(), keys::kOnBookmarkCreated, args.Pass()); +} + +void BookmarkEventRouter::BookmarkNodeRemoved(BookmarkModel* model, + const BookmarkNode* parent, + int index, + const BookmarkNode* node) { + scoped_ptr args(new ListValue()); + args->Append(new StringValue(base::Int64ToString(node->id()))); + DictionaryValue* object_args = new DictionaryValue(); + object_args->SetString(keys::kParentIdKey, + base::Int64ToString(parent->id())); + object_args->SetInteger(keys::kIndexKey, index); + args->Append(object_args); + + DispatchEvent(model->profile(), keys::kOnBookmarkRemoved, args.Pass()); +} + +void BookmarkEventRouter::BookmarkNodeChanged(BookmarkModel* model, + const BookmarkNode* node) { + scoped_ptr args(new ListValue()); + args->Append(new StringValue(base::Int64ToString(node->id()))); + + // TODO(erikkay) The only three things that BookmarkModel sends this + // notification for are title, url and favicon. Since we're currently + // ignoring favicon and since the notification doesn't say which one anyway, + // for now we only include title and url. The ideal thing would be to change + // BookmarkModel to indicate what changed. + DictionaryValue* object_args = new DictionaryValue(); + object_args->SetString(keys::kTitleKey, node->GetTitle()); + if (node->is_url()) + object_args->SetString(keys::kUrlKey, node->url().spec()); + args->Append(object_args); + + DispatchEvent(model->profile(), keys::kOnBookmarkChanged, args.Pass()); +} + +void BookmarkEventRouter::BookmarkNodeFaviconChanged(BookmarkModel* model, + const BookmarkNode* node) { + // TODO(erikkay) anything we should do here? +} + +void BookmarkEventRouter::BookmarkNodeChildrenReordered( + BookmarkModel* model, + const BookmarkNode* node) { + scoped_ptr args(new ListValue()); + args->Append(new StringValue(base::Int64ToString(node->id()))); + int childCount = node->child_count(); + ListValue* children = new ListValue(); + for (int i = 0; i < childCount; ++i) { + const BookmarkNode* child = node->GetChild(i); + Value* child_id = new StringValue(base::Int64ToString(child->id())); + children->Append(child_id); + } + DictionaryValue* reorder_info = new DictionaryValue(); + reorder_info->Set(keys::kChildIdsKey, children); + args->Append(reorder_info); + + DispatchEvent(model->profile(), keys::kOnBookmarkChildrenReordered, + args.Pass()); +} + +void BookmarkEventRouter::ExtensiveBookmarkChangesBeginning( + BookmarkModel* model) { + scoped_ptr args(new ListValue()); + DispatchEvent(model->profile(), + keys::kOnBookmarkImportBegan, + args.Pass()); +} + +void BookmarkEventRouter::ExtensiveBookmarkChangesEnded(BookmarkModel* model) { + scoped_ptr args(new ListValue()); + DispatchEvent(model->profile(), + keys::kOnBookmarkImportEnded, + args.Pass()); +} + +BookmarkAPI::BookmarkAPI(Profile* profile) : profile_(profile) { + ExtensionSystem::Get(profile_)->event_router()->RegisterObserver( + this, keys::kOnBookmarkCreated); + ExtensionSystem::Get(profile_)->event_router()->RegisterObserver( + this, keys::kOnBookmarkRemoved); + ExtensionSystem::Get(profile_)->event_router()->RegisterObserver( + this, keys::kOnBookmarkChanged); + ExtensionSystem::Get(profile_)->event_router()->RegisterObserver( + this, keys::kOnBookmarkMoved); + ExtensionSystem::Get(profile_)->event_router()->RegisterObserver( + this, keys::kOnBookmarkChildrenReordered); + ExtensionSystem::Get(profile_)->event_router()->RegisterObserver( + this, keys::kOnBookmarkImportBegan); + ExtensionSystem::Get(profile_)->event_router()->RegisterObserver( + this, keys::kOnBookmarkImportEnded); +} + +BookmarkAPI::~BookmarkAPI() { +} + +void BookmarkAPI::Shutdown() { + ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this); +} + +void BookmarkAPI::OnListenerAdded(const EventListenerInfo& details) { + bookmark_event_router_.reset(new BookmarkEventRouter( + BookmarkModelFactory::GetForProfile(profile_))); + ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this); +} + +bool GetBookmarksFunction::RunImpl() { + scoped_ptr params( + bookmarks::Get::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + std::vector > nodes; + BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); + if (params->id_or_id_list_type == + bookmarks::Get::Params::ID_OR_ID_LIST_ARRAY) { + std::vector* ids = params->id_or_id_list_array.get(); + size_t count = ids->size(); + EXTENSION_FUNCTION_VALIDATE(count > 0); + for (size_t i = 0; i < count; ++i) { + int64 id; + if (!GetBookmarkIdAsInt64(ids->at(i), &id)) + return false; + const BookmarkNode* node = model->GetNodeByID(id); + if (!node) { + error_ = keys::kNoNodeError; + return false; + } else { + bookmark_api_helpers::AddNode(node, &nodes, false); + } + } + } else { + int64 id; + if (!GetBookmarkIdAsInt64(*params->id_or_id_list_string, &id)) + return false; + const BookmarkNode* node = model->GetNodeByID(id); + if (!node) { + error_ = keys::kNoNodeError; + return false; + } + bookmark_api_helpers::AddNode(node, &nodes, false); + } + + results_ = bookmarks::Get::Results::Create(nodes); + return true; +} + +bool GetBookmarkChildrenFunction::RunImpl() { + scoped_ptr params( + bookmarks::GetChildren::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + int64 id; + if (!GetBookmarkIdAsInt64(params->id, &id)) + return false; + + std::vector > nodes; + const BookmarkNode* node = + BookmarkModelFactory::GetForProfile(profile())->GetNodeByID(id); + if (!node) { + error_ = keys::kNoNodeError; + return false; + } + int child_count = node->child_count(); + for (int i = 0; i < child_count; ++i) { + const BookmarkNode* child = node->GetChild(i); + bookmark_api_helpers::AddNode(child, &nodes, false); + } + + results_ = bookmarks::GetChildren::Results::Create(nodes); + return true; +} + +bool GetBookmarkRecentFunction::RunImpl() { + scoped_ptr params( + bookmarks::GetRecent::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + if (params->number_of_items < 1) + return false; + + std::vector nodes; + bookmark_utils::GetMostRecentlyAddedEntries( + BookmarkModelFactory::GetForProfile(profile()), + params->number_of_items, + &nodes); + + std::vector > tree_nodes; + std::vector::iterator i = nodes.begin(); + for (; i != nodes.end(); ++i) { + const BookmarkNode* node = *i; + bookmark_api_helpers::AddNode(node, &tree_nodes, false); + } + + results_ = bookmarks::GetRecent::Results::Create(tree_nodes); + return true; +} + +bool GetBookmarkTreeFunction::RunImpl() { + std::vector > nodes; + const BookmarkNode* node = + BookmarkModelFactory::GetForProfile(profile())->root_node(); + bookmark_api_helpers::AddNode(node, &nodes, true); + results_ = bookmarks::GetTree::Results::Create(nodes); + return true; +} + +bool GetBookmarkSubTreeFunction::RunImpl() { + scoped_ptr params( + bookmarks::GetSubTree::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + int64 id; + if (!GetBookmarkIdAsInt64(params->id, &id)) + return false; + + const BookmarkNode* node = + BookmarkModelFactory::GetForProfile(profile())->GetNodeByID(id); + if (!node) { + error_ = keys::kNoNodeError; + return false; + } + + std::vector > nodes; + bookmark_api_helpers::AddNode(node, &nodes, true); + results_ = bookmarks::GetSubTree::Results::Create(nodes); + return true; +} + +bool SearchBookmarksFunction::RunImpl() { + scoped_ptr params( + bookmarks::Search::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + PrefServiceBase* prefs = PrefServiceBase::FromBrowserContext(profile_); + std::string lang = prefs->GetString(prefs::kAcceptLanguages); + std::vector nodes; + bookmark_utils::GetBookmarksContainingText( + BookmarkModelFactory::GetForProfile(profile()), + UTF8ToUTF16(params->query), + std::numeric_limits::max(), + lang, + &nodes); + + std::vector > tree_nodes; + for (std::vector::iterator node_iter = nodes.begin(); + node_iter != nodes.end(); ++node_iter) { + bookmark_api_helpers::AddNode(*node_iter, &tree_nodes, false); + } + + results_ = bookmarks::Search::Results::Create(tree_nodes); + return true; +} + +// static +bool RemoveBookmarkFunction::ExtractIds(const ListValue* args, + std::list* ids, + bool* invalid_id) { + std::string id_string; + if (!args->GetString(0, &id_string)) + return false; + int64 id; + if (base::StringToInt64(id_string, &id)) + ids->push_back(id); + else + *invalid_id = true; + return true; +} + +bool RemoveBookmarkFunction::RunImpl() { + if (!EditBookmarksEnabled()) + return false; + + scoped_ptr params( + bookmarks::Remove::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + int64 id; + if (!base::StringToInt64(params->id, &id)) { + error_ = keys::kInvalidIdError; + return false; + } + + bool recursive = false; + if (name() == RemoveTreeBookmarkFunction::function_name()) + recursive = true; + + BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); + if (!bookmark_api_helpers::RemoveNode(model, id, recursive, &error_)) + return false; + + return true; +} + +bool CreateBookmarkFunction::RunImpl() { + if (!EditBookmarksEnabled()) + return false; + + scoped_ptr params( + bookmarks::Create::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); + int64 parentId; + + if (!params->bookmark.parent_id.get()) { + // Optional, default to "other bookmarks". + parentId = model->other_node()->id(); + } else { + if (!GetBookmarkIdAsInt64(*params->bookmark.parent_id, &parentId)) + return false; + } + const BookmarkNode* parent = model->GetNodeByID(parentId); + if (!parent) { + error_ = keys::kNoParentError; + return false; + } + if (parent->is_root()) { // Can't create children of the root. + error_ = keys::kModifySpecialError; + return false; + } + + int index; + if (!params->bookmark.index.get()) { // Optional (defaults to end). + index = parent->child_count(); + } else { + index = *params->bookmark.index; + if (index > parent->child_count() || index < 0) { + error_ = keys::kInvalidIndexError; + return false; + } + } + + string16 title; // Optional. + if (params->bookmark.title.get()) + title = UTF8ToUTF16(*params->bookmark.title.get()); + + std::string url_string; // Optional. + if (params->bookmark.url.get()) + url_string = *params->bookmark.url.get(); + + GURL url(url_string); + if (!url_string.empty() && !url.is_valid()) { + error_ = keys::kInvalidUrlError; + return false; + } + + const BookmarkNode* node; + if (url_string.length()) + node = model->AddURL(parent, index, title, url); + else + node = model->AddFolder(parent, index, title); + DCHECK(node); + if (!node) { + error_ = keys::kNoNodeError; + return false; + } + + scoped_ptr ret( + bookmark_api_helpers::GetBookmarkTreeNode(node, false, false)); + results_ = bookmarks::Create::Results::Create(*ret); + + return true; +} + +// static +bool MoveBookmarkFunction::ExtractIds(const ListValue* args, + std::list* ids, + bool* invalid_id) { + // For now, Move accepts ID parameters in the same way as an Update. + return UpdateBookmarkFunction::ExtractIds(args, ids, invalid_id); +} + +bool MoveBookmarkFunction::RunImpl() { + if (!EditBookmarksEnabled()) + return false; + + scoped_ptr params( + bookmarks::Move::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + int64 id; + if (!base::StringToInt64(params->id, &id)) { + error_ = keys::kInvalidIdError; + return false; + } + + BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); + const BookmarkNode* node = model->GetNodeByID(id); + if (!node) { + error_ = keys::kNoNodeError; + return false; + } + if (model->is_permanent_node(node)) { + error_ = keys::kModifySpecialError; + return false; + } + + const BookmarkNode* parent = NULL; + if (!params->destination.parent_id.get()) { + // Optional, defaults to current parent. + parent = node->parent(); + } else { + int64 parentId; + if (!GetBookmarkIdAsInt64(*params->destination.parent_id, &parentId)) + return false; + + parent = model->GetNodeByID(parentId); + } + if (!parent) { + error_ = keys::kNoParentError; + // TODO(erikkay) return an error message. + return false; + } + if (parent == model->root_node()) { + error_ = keys::kModifySpecialError; + return false; + } + + int index; + if (params->destination.index.get()) { // Optional (defaults to end). + index = *params->destination.index; + if (index > parent->child_count() || index < 0) { + error_ = keys::kInvalidIndexError; + return false; + } + } else { + index = parent->child_count(); + } + + model->Move(node, parent, index); + + scoped_ptr tree_node( + bookmark_api_helpers::GetBookmarkTreeNode(node, false, false)); + results_ = bookmarks::Move::Results::Create(*tree_node); + + return true; +} + +// static +bool UpdateBookmarkFunction::ExtractIds(const ListValue* args, + std::list* ids, + bool* invalid_id) { + // For now, Update accepts ID parameters in the same way as an Remove. + return RemoveBookmarkFunction::ExtractIds(args, ids, invalid_id); +} + +bool UpdateBookmarkFunction::RunImpl() { + if (!EditBookmarksEnabled()) + return false; + + scoped_ptr params( + bookmarks::Update::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + int64 id; + if (!base::StringToInt64(params->id, &id)) { + error_ = keys::kInvalidIdError; + return false; + } + + BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); + + // Optional but we need to distinguish non present from an empty title. + string16 title; + bool has_title = false; + if (params->changes.title.get()) { + title = UTF8ToUTF16(*params->changes.title); + has_title = true; + } + + // Optional. + std::string url_string; + if (params->changes.url.get()) + url_string = *params->changes.url; + GURL url(url_string); + if (!url_string.empty() && !url.is_valid()) { + error_ = keys::kInvalidUrlError; + return false; + } + + const BookmarkNode* node = model->GetNodeByID(id); + if (!node) { + error_ = keys::kNoNodeError; + return false; + } + if (model->is_permanent_node(node)) { + error_ = keys::kModifySpecialError; + return false; + } + if (has_title) + model->SetTitle(node, title); + if (!url.is_empty()) + model->SetURL(node, url); + + scoped_ptr tree_node( + bookmark_api_helpers::GetBookmarkTreeNode(node, false, false)); + results_ = bookmarks::Update::Results::Create(*tree_node); + return true; +} + +// Mapper superclass for BookmarkFunctions. +template +class BookmarkBucketMapper : public BucketMapper { + public: + virtual ~BookmarkBucketMapper() { STLDeleteValues(&buckets_); } + protected: + Bucket* GetBucket(const BucketIdType& id) { + Bucket* b = buckets_[id]; + if (b == NULL) { + b = new Bucket(); + buckets_[id] = b; + } + return b; + } + private: + std::map buckets_; +}; + +// Mapper for 'bookmarks.create'. Maps "same input to bookmarks.create" to a +// unique bucket. +class CreateBookmarkBucketMapper : public BookmarkBucketMapper { + public: + explicit CreateBookmarkBucketMapper(Profile* profile) : profile_(profile) {} + // TODO(tim): This should share code with CreateBookmarkFunction::RunImpl, + // but I can't figure out a good way to do that with all the macros. + virtual void GetBucketsForArgs(const ListValue* args, BucketList* buckets) { + const DictionaryValue* json; + if (!args->GetDictionary(0, &json)) + return; + + std::string parent_id; + if (json->HasKey(keys::kParentIdKey)) { + if (!json->GetString(keys::kParentIdKey, &parent_id)) + return; + } + BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile_); + + int64 parent_id_int64; + base::StringToInt64(parent_id, &parent_id_int64); + const BookmarkNode* parent = model->GetNodeByID(parent_id_int64); + if (!parent) + return; + + std::string bucket_id = UTF16ToUTF8(parent->GetTitle()); + std::string title; + json->GetString(keys::kTitleKey, &title); + std::string url_string; + json->GetString(keys::kUrlKey, &url_string); + + bucket_id += title; + bucket_id += url_string; + // 20 bytes (SHA1 hash length) is very likely less than most of the + // |bucket_id| strings we construct here, so we hash it to save space. + buckets->push_back(GetBucket(base::SHA1HashString(bucket_id))); + } + private: + Profile* profile_; +}; + +// Mapper for 'bookmarks.remove'. +class RemoveBookmarksBucketMapper : public BookmarkBucketMapper { + public: + explicit RemoveBookmarksBucketMapper(Profile* profile) : profile_(profile) {} + virtual void GetBucketsForArgs(const ListValue* args, BucketList* buckets) { + typedef std::list IdList; + IdList ids; + bool invalid_id = false; + if (!RemoveBookmarkFunction::ExtractIds(args, &ids, &invalid_id) || + invalid_id) { + return; + } + + for (IdList::iterator it = ids.begin(); it != ids.end(); ++it) { + BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile_); + const BookmarkNode* node = model->GetNodeByID(*it); + if (!node || node->is_root()) + return; + + std::string bucket_id; + bucket_id += UTF16ToUTF8(node->parent()->GetTitle()); + bucket_id += UTF16ToUTF8(node->GetTitle()); + bucket_id += node->url().spec(); + buckets->push_back(GetBucket(base::SHA1HashString(bucket_id))); + } + } + private: + Profile* profile_; +}; + +// Mapper for any bookmark function accepting bookmark IDs as parameters, where +// a distinct ID corresponds to a single item in terms of quota limiting. This +// is inappropriate for bookmarks.remove, for example, since repeated removals +// of the same item will actually have a different ID each time. +template +class BookmarkIdMapper : public BookmarkBucketMapper { + public: + typedef std::list IdList; + virtual void GetBucketsForArgs(const ListValue* args, BucketList* buckets) { + IdList ids; + bool invalid_id = false; + if (!FunctionType::ExtractIds(args, &ids, &invalid_id) || invalid_id) + return; + for (IdList::iterator it = ids.begin(); it != ids.end(); ++it) + buckets->push_back(GetBucket(*it)); + } +}; + +// Builds heuristics for all BookmarkFunctions using specialized BucketMappers. +class BookmarksQuotaLimitFactory { + public: + // For id-based bookmark functions. + template + static void Build(QuotaLimitHeuristics* heuristics) { + BuildWithMappers(heuristics, new BookmarkIdMapper(), + new BookmarkIdMapper()); + } + + // For bookmarks.create. + static void BuildForCreate(QuotaLimitHeuristics* heuristics, + Profile* profile) { + BuildWithMappers(heuristics, new CreateBookmarkBucketMapper(profile), + new CreateBookmarkBucketMapper(profile)); + } + + // For bookmarks.remove. + static void BuildForRemove(QuotaLimitHeuristics* heuristics, + Profile* profile) { + BuildWithMappers(heuristics, new RemoveBookmarksBucketMapper(profile), + new RemoveBookmarksBucketMapper(profile)); + } + + private: + static void BuildWithMappers(QuotaLimitHeuristics* heuristics, + BucketMapper* short_mapper, BucketMapper* long_mapper) { + const Config kSustainedLimitConfig = { + // See bookmarks.json for current value. + bookmarks::MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE, + TimeDelta::FromMinutes(1) + }; + heuristics->push_back(new SustainedLimit( + TimeDelta::FromMinutes(10), + kSustainedLimitConfig, + short_mapper, + "MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE")); + + const Config kTimedLimitConfig = { + // See bookmarks.json for current value. + bookmarks::MAX_WRITE_OPERATIONS_PER_HOUR, + TimeDelta::FromHours(1) + }; + heuristics->push_back(new TimedLimit( + kTimedLimitConfig, + long_mapper, + "MAX_WRITE_OPERATIONS_PER_HOUR")); + } + + DISALLOW_IMPLICIT_CONSTRUCTORS(BookmarksQuotaLimitFactory); +}; + +// And finally, building the individual heuristics for each function. +void RemoveBookmarkFunction::GetQuotaLimitHeuristics( + QuotaLimitHeuristics* heuristics) const { + BookmarksQuotaLimitFactory::BuildForRemove(heuristics, profile()); +} + +void MoveBookmarkFunction::GetQuotaLimitHeuristics( + QuotaLimitHeuristics* heuristics) const { + BookmarksQuotaLimitFactory::Build(heuristics); +} + +void UpdateBookmarkFunction::GetQuotaLimitHeuristics( + QuotaLimitHeuristics* heuristics) const { + BookmarksQuotaLimitFactory::Build(heuristics); +}; + +void CreateBookmarkFunction::GetQuotaLimitHeuristics( + QuotaLimitHeuristics* heuristics) const { + BookmarksQuotaLimitFactory::BuildForCreate(heuristics, profile()); +} + +BookmarksIOFunction::BookmarksIOFunction() {} + +BookmarksIOFunction::~BookmarksIOFunction() { + // There may be pending file dialogs, we need to tell them that we've gone + // away so they don't try and call back to us. + if (select_file_dialog_.get()) + select_file_dialog_->ListenerDestroyed(); +} + +void BookmarksIOFunction::SelectFile(ui::SelectFileDialog::Type type) { + // GetDefaultFilepathForBookmarkExport() might have to touch the filesystem + // (stat or access, for example), so this requires a thread with IO allowed. + if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, + base::Bind(&BookmarksIOFunction::SelectFile, this, type)); + return; + } + + // Pre-populating the filename field in case this is a SELECT_SAVEAS_FILE + // dialog. If not, there is no filename field in the dialog box. + FilePath default_path; + if (type == ui::SelectFileDialog::SELECT_SAVEAS_FILE) + default_path = GetDefaultFilepathForBookmarkExport(); + else + DCHECK(type == ui::SelectFileDialog::SELECT_OPEN_FILE); + + // After getting the |default_path|, ask the UI to display the file dialog. + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&BookmarksIOFunction::ShowSelectFileDialog, this, + type, default_path)); +} + +void BookmarksIOFunction::ShowSelectFileDialog(ui::SelectFileDialog::Type type, + const FilePath& default_path) { + // Balanced in one of the three callbacks of SelectFileDialog: + // either FileSelectionCanceled, MultiFilesSelected, or FileSelected + AddRef(); + + WebContents* web_contents = dispatcher()->delegate()-> + GetAssociatedWebContents(); + + select_file_dialog_ = ui::SelectFileDialog::Create( + this, new ChromeSelectFilePolicy(web_contents)); + ui::SelectFileDialog::FileTypeInfo file_type_info; + file_type_info.extensions.resize(1); + file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("html")); + // TODO(kinaba): http://crbug.com/140425. Turn file_type_info.support_gdata + // on for saving once Google Drive client on ChromeOS supports it. + if (type == ui::SelectFileDialog::SELECT_OPEN_FILE) + file_type_info.support_gdata = true; + // |web_contents| can be NULL (for background pages), which is fine. In such + // a case if file-selection dialogs are forbidden by policy, we will not + // show an InfoBar, which is better than letting one appear out of the blue. + select_file_dialog_->SelectFile(type, + string16(), + default_path, + &file_type_info, + 0, + FILE_PATH_LITERAL(""), + NULL, + NULL); +} + +void BookmarksIOFunction::FileSelectionCanceled(void* params) { + Release(); // Balanced in BookmarksIOFunction::SelectFile() +} + +void BookmarksIOFunction::MultiFilesSelected( + const std::vector& files, void* params) { + Release(); // Balanced in BookmarsIOFunction::SelectFile() + NOTREACHED() << "Should not be able to select multiple files"; +} + +bool ImportBookmarksFunction::RunImpl() { + if (!EditBookmarksEnabled()) + return false; + SelectFile(ui::SelectFileDialog::SELECT_OPEN_FILE); + return true; +} + +void ImportBookmarksFunction::FileSelected(const FilePath& path, + int index, + void* params) { +#if !defined(OS_ANDROID) + // Android does not have support for the standard importers. + // TODO(jgreenwald): remove ifdef once extensions are no longer built on + // Android. + scoped_refptr importer_host(new ImporterHost); + importer::SourceProfile source_profile; + source_profile.importer_type = importer::TYPE_BOOKMARKS_FILE; + source_profile.source_path = path; + importer_host->StartImportSettings(source_profile, + profile(), + importer::FAVORITES, + new ProfileWriter(profile()), + true); +#endif + Release(); // Balanced in BookmarksIOFunction::SelectFile() +} + +bool ExportBookmarksFunction::RunImpl() { + SelectFile(ui::SelectFileDialog::SELECT_SAVEAS_FILE); + return true; +} + +void ExportBookmarksFunction::FileSelected(const FilePath& path, + int index, + void* params) { +#if !defined(OS_ANDROID) + // Android does not have support for the standard exporter. + // TODO(jgreenwald): remove ifdef once extensions are no longer built on + // Android. + bookmark_html_writer::WriteBookmarks(profile(), path, NULL); +#endif + Release(); // Balanced in BookmarksIOFunction::SelectFile() +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/bookmarks/bookmark_api.h b/chrome/browser/extensions/api/bookmarks/bookmark_api.h new file mode 100644 index 0000000..c3008246 --- /dev/null +++ b/chrome/browser/extensions/api/bookmarks/bookmark_api.h @@ -0,0 +1,321 @@ +// 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_API_BOOKMARKS_BOOKMARK_API_H_ +#define CHROME_BROWSER_EXTENSIONS_API_BOOKMARKS_BOOKMARK_API_H_ + +#include +#include +#include + +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "chrome/browser/bookmarks/bookmark_model_observer.h" +#include "chrome/browser/extensions/event_router.h" +#include "chrome/browser/extensions/extension_function.h" +#include "chrome/browser/profiles/profile_keyed_service.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" +#include "ui/base/dialogs/select_file_dialog.h" + +class FilePath; + +namespace base { +class ListValue; +} + +namespace extensions { + +// Observes BookmarkModel and then routes the notifications as events to +// the extension system. +class BookmarkEventRouter : public BookmarkModelObserver { + public: + explicit BookmarkEventRouter(BookmarkModel* model); + virtual ~BookmarkEventRouter(); + + // BookmarkModelObserver: + 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 BookmarkNodeRemoved(BookmarkModel* model, + const BookmarkNode* parent, + int old_index, + const BookmarkNode* node) OVERRIDE; + virtual void BookmarkNodeChanged(BookmarkModel* model, + const BookmarkNode* node) OVERRIDE; + virtual void BookmarkNodeFaviconChanged(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; + + private: + // Helper to actually dispatch an event to extension listeners. + void DispatchEvent(Profile* profile, + const char* event_name, + scoped_ptr event_args); + + BookmarkModel* model_; + + DISALLOW_COPY_AND_ASSIGN(BookmarkEventRouter); +}; + +class BookmarkAPI : public ProfileKeyedService, + public EventRouter::Observer { + public: + explicit BookmarkAPI(Profile* profile); + virtual ~BookmarkAPI(); + + // ProfileKeyedService implementation. + virtual void Shutdown() OVERRIDE; + + // EventRouter::Observer implementation. + virtual void OnListenerAdded(const EventListenerInfo& details) + OVERRIDE; + + private: + Profile* profile_; + + // Created lazily upon OnListenerAdded. + scoped_ptr bookmark_event_router_; +}; + +class BookmarksFunction : public AsyncExtensionFunction, + public content::NotificationObserver { + public: + // AsyncExtensionFunction: + virtual void Run() OVERRIDE; + + protected: + virtual ~BookmarksFunction() {} + + // Helper to get the bookmark id as int64 from the given string id. + // Sets error_ to an error string if the given id string can't be parsed + // as an int64. In case of error, doesn't change id and returns false. + bool GetBookmarkIdAsInt64(const std::string& id_string, int64* id); + + // Helper that checks if bookmark editing is enabled. If it's not, this sets + // error_ to the appropriate error string. + bool EditBookmarksEnabled(); + + private: + // content::NotificationObserver: + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE; + + content::NotificationRegistrar registrar_; +}; + +class GetBookmarksFunction : public BookmarksFunction { + public: + DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.get") + + protected: + virtual ~GetBookmarksFunction() {} + + // ExtensionFunction: + virtual bool RunImpl() OVERRIDE; +}; + +class GetBookmarkChildrenFunction : public BookmarksFunction { + public: + DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.getChildren") + + protected: + virtual ~GetBookmarkChildrenFunction() {} + + // ExtensionFunction: + virtual bool RunImpl() OVERRIDE; +}; + +class GetBookmarkRecentFunction : public BookmarksFunction { + public: + DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.getRecent") + + protected: + virtual ~GetBookmarkRecentFunction() {} + + // ExtensionFunction: + virtual bool RunImpl() OVERRIDE; +}; + +class GetBookmarkTreeFunction : public BookmarksFunction { + public: + DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.getTree") + + protected: + virtual ~GetBookmarkTreeFunction() {} + + // ExtensionFunction: + virtual bool RunImpl() OVERRIDE; +}; + +class GetBookmarkSubTreeFunction : public BookmarksFunction { + public: + DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.getSubTree") + + protected: + virtual ~GetBookmarkSubTreeFunction() {} + + // ExtensionFunction: + virtual bool RunImpl() OVERRIDE; +}; + +class SearchBookmarksFunction : public BookmarksFunction { + public: + DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.search") + + protected: + virtual ~SearchBookmarksFunction() {} + + // ExtensionFunction: + virtual bool RunImpl() OVERRIDE; +}; + +class RemoveBookmarkFunction : public BookmarksFunction { + public: + DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.remove") + + // Returns true on successful parse and sets invalid_id to true if conversion + // from id string to int64 failed. + static bool ExtractIds(const base::ListValue* args, std::list* ids, + bool* invalid_id); + // ExtensionFunction: + virtual void GetQuotaLimitHeuristics( + QuotaLimitHeuristics* heuristics) const OVERRIDE; + + protected: + virtual ~RemoveBookmarkFunction() {} + + // ExtensionFunction: + virtual bool RunImpl() OVERRIDE; +}; + +class RemoveTreeBookmarkFunction : public RemoveBookmarkFunction { + public: + DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.removeTree") + + protected: + virtual ~RemoveTreeBookmarkFunction() {} +}; + +class CreateBookmarkFunction : public BookmarksFunction { + public: + DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.create") + + // ExtensionFunction: + virtual void GetQuotaLimitHeuristics( + QuotaLimitHeuristics* heuristics) const OVERRIDE; + + protected: + virtual ~CreateBookmarkFunction() {} + + // ExtensionFunction: + virtual bool RunImpl() OVERRIDE; +}; + +class MoveBookmarkFunction : public BookmarksFunction { + public: + DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.move") + + static bool ExtractIds(const base::ListValue* args, std::list* ids, + bool* invalid_id); + + // ExtensionFunction: + virtual void GetQuotaLimitHeuristics( + QuotaLimitHeuristics* heuristics) const OVERRIDE; + + protected: + virtual ~MoveBookmarkFunction() {} + + // ExtensionFunction: + virtual bool RunImpl() OVERRIDE; +}; + +class UpdateBookmarkFunction : public BookmarksFunction { + public: + DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.update") + + static bool ExtractIds(const base::ListValue* args, std::list* ids, + bool* invalid_id); + + // ExtensionFunction: + virtual void GetQuotaLimitHeuristics( + QuotaLimitHeuristics* heuristics) const OVERRIDE; + + protected: + virtual ~UpdateBookmarkFunction() {} + + // ExtensionFunction: + virtual bool RunImpl() OVERRIDE; +}; + +class BookmarksIOFunction : public BookmarksFunction, + public ui::SelectFileDialog::Listener { + public: + BookmarksIOFunction(); + + virtual void FileSelected(const FilePath& path, int index, void* params) = 0; + + // ui::SelectFileDialog::Listener: + virtual void MultiFilesSelected(const std::vector& files, + void* params) OVERRIDE; + virtual void FileSelectionCanceled(void* params) OVERRIDE; + + void SelectFile(ui::SelectFileDialog::Type type); + + protected: + virtual ~BookmarksIOFunction(); + + private: + void ShowSelectFileDialog( + ui::SelectFileDialog::Type type, + const FilePath& default_path); + + protected: + scoped_refptr select_file_dialog_; +}; + +class ImportBookmarksFunction : public BookmarksIOFunction { + public: + DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.import"); + + // BookmarkManagerIOFunction: + virtual void FileSelected(const FilePath& path, int index, void* params) + OVERRIDE; + + private: + virtual ~ImportBookmarksFunction() {} + + // ExtensionFunction: + virtual bool RunImpl() OVERRIDE; +}; + +class ExportBookmarksFunction : public BookmarksIOFunction { + public: + DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.export"); + + // BookmarkManagerIOFunction: + virtual void FileSelected(const FilePath& path, int index, void* params) + OVERRIDE; + + private: + virtual ~ExportBookmarksFunction() {} + + // ExtensionFunction: + virtual bool RunImpl() OVERRIDE; +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_BOOKMARKS_BOOKMARK_API_H_ diff --git a/chrome/browser/extensions/api/bookmarks/bookmark_api_factory.cc b/chrome/browser/extensions/api/bookmarks/bookmark_api_factory.cc index 9c649bb..3f20629 100644 --- a/chrome/browser/extensions/api/bookmarks/bookmark_api_factory.cc +++ b/chrome/browser/extensions/api/bookmarks/bookmark_api_factory.cc @@ -4,7 +4,7 @@ #include "chrome/browser/extensions/api/bookmarks/bookmark_api_factory.h" -#include "chrome/browser/extensions/api/bookmarks/bookmarks_api.h" +#include "chrome/browser/extensions/api/bookmarks/bookmark_api.h" #include "chrome/browser/extensions/extension_system_factory.h" #include "chrome/browser/profiles/profile_dependency_manager.h" diff --git a/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc b/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc deleted file mode 100644 index 08fa4ab3..0000000 --- a/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc +++ /dev/null @@ -1,996 +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/api/bookmarks/bookmarks_api.h" - -#include "base/bind.h" -#include "base/file_path.h" -#include "base/i18n/file_util_icu.h" -#include "base/i18n/time_formatting.h" -#include "base/json/json_writer.h" -#include "base/memory/scoped_ptr.h" -#include "base/path_service.h" -#include "base/prefs/public/pref_service_base.h" -#include "base/sha1.h" -#include "base/stl_util.h" -#include "base/string16.h" -#include "base/string_number_conversions.h" -#include "base/string_util.h" -#include "base/time.h" -#include "base/utf_string_conversions.h" -#include "chrome/browser/bookmarks/bookmark_codec.h" -#include "chrome/browser/bookmarks/bookmark_html_writer.h" -#include "chrome/browser/bookmarks/bookmark_model.h" -#include "chrome/browser/bookmarks/bookmark_model_factory.h" -#include "chrome/browser/bookmarks/bookmark_utils.h" -#include "chrome/browser/extensions/api/bookmarks/bookmark_api_constants.h" -#include "chrome/browser/extensions/api/bookmarks/bookmark_api_helpers.h" -#include "chrome/browser/extensions/event_router.h" -#include "chrome/browser/extensions/extension_function_dispatcher.h" -#include "chrome/browser/extensions/extension_system.h" -#include "chrome/browser/extensions/extensions_quota_service.h" -#include "chrome/browser/importer/importer_data_types.h" -#include "chrome/browser/importer/importer_host.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/chrome_select_file_policy.h" -#include "chrome/common/chrome_notification_types.h" -#include "chrome/common/chrome_paths.h" -#include "chrome/common/extensions/api/bookmarks.h" -#include "chrome/common/pref_names.h" -#include "content/public/browser/notification_service.h" -#include "grit/generated_resources.h" -#include "ui/base/l10n/l10n_util.h" - -namespace extensions { - -namespace keys = bookmark_api_constants; -namespace bookmarks = api::bookmarks; - -using base::TimeDelta; -using bookmarks::BookmarkTreeNode; -using content::BrowserThread; -using content::WebContents; - -typedef QuotaLimitHeuristic::Bucket Bucket; -typedef QuotaLimitHeuristic::Config Config; -typedef QuotaLimitHeuristic::BucketList BucketList; -typedef ExtensionsQuotaService::TimedLimit TimedLimit; -typedef ExtensionsQuotaService::SustainedLimit SustainedLimit; -typedef QuotaLimitHeuristic::BucketMapper BucketMapper; - -namespace { - -// Generates a default path (including a default filename) that will be -// used for pre-populating the "Export Bookmarks" file chooser dialog box. -FilePath GetDefaultFilepathForBookmarkExport() { - base::Time time = base::Time::Now(); - - // Concatenate a date stamp to the filename. -#if defined(OS_POSIX) - FilePath::StringType filename = - l10n_util::GetStringFUTF8(IDS_EXPORT_BOOKMARKS_DEFAULT_FILENAME, - base::TimeFormatShortDateNumeric(time)); -#elif defined(OS_WIN) - FilePath::StringType filename = - l10n_util::GetStringFUTF16(IDS_EXPORT_BOOKMARKS_DEFAULT_FILENAME, - base::TimeFormatShortDateNumeric(time)); -#endif - - file_util::ReplaceIllegalCharactersInPath(&filename, '_'); - - FilePath default_path; - PathService::Get(chrome::DIR_USER_DOCUMENTS, &default_path); - return default_path.Append(filename); -} - -} // namespace - -void BookmarksFunction::Run() { - BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); - if (!model->IsLoaded()) { - // Bookmarks are not ready yet. We'll wait. - registrar_.Add( - this, chrome::NOTIFICATION_BOOKMARK_MODEL_LOADED, - content::NotificationService::AllBrowserContextsAndSources()); - AddRef(); // Balanced in Observe(). - return; - } - - bool success = RunImpl(); - if (success) { - content::NotificationService::current()->Notify( - chrome::NOTIFICATION_EXTENSION_BOOKMARKS_API_INVOKED, - content::Source(GetExtension()), - content::Details(this)); - } - SendResponse(success); -} - -bool BookmarksFunction::GetBookmarkIdAsInt64( - const std::string& id_string, int64* id) { - if (base::StringToInt64(id_string, id)) - return true; - - error_ = keys::kInvalidIdError; - return false; -} - -bool BookmarksFunction::EditBookmarksEnabled() { - PrefServiceBase* prefs = PrefServiceBase::FromBrowserContext(profile_); - if (prefs->GetBoolean(prefs::kEditBookmarksEnabled)) - return true; - error_ = keys::kEditBookmarksDisabled; - return false; -} - -void BookmarksFunction::Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) { - DCHECK(type == chrome::NOTIFICATION_BOOKMARK_MODEL_LOADED); - Profile* source_profile = content::Source(source).ptr(); - if (!source_profile || !source_profile->IsSameProfile(profile())) - return; - - DCHECK(BookmarkModelFactory::GetForProfile(profile())->IsLoaded()); - Run(); - Release(); // Balanced in Run(). -} - -BookmarkEventRouter::BookmarkEventRouter(BookmarkModel* model) : model_(model) { - model_->AddObserver(this); -} - -BookmarkEventRouter::~BookmarkEventRouter() { - if (model_) { - model_->RemoveObserver(this); - } -} - -void BookmarkEventRouter::DispatchEvent( - Profile* profile, - const char* event_name, - scoped_ptr event_args) { - if (extensions::ExtensionSystem::Get(profile)->event_router()) { - extensions::ExtensionSystem::Get(profile)->event_router()->BroadcastEvent( - make_scoped_ptr(new extensions::Event(event_name, event_args.Pass()))); - } -} - -void BookmarkEventRouter::Loaded(BookmarkModel* model, bool ids_reassigned) { - // TODO(erikkay): Perhaps we should send this event down to the extension - // so they know when it's safe to use the API? -} - -void BookmarkEventRouter::BookmarkModelBeingDeleted(BookmarkModel* model) { - model_ = NULL; -} - -void BookmarkEventRouter::BookmarkNodeMoved(BookmarkModel* model, - const BookmarkNode* old_parent, - int old_index, - const BookmarkNode* new_parent, - int new_index) { - scoped_ptr args(new ListValue()); - const BookmarkNode* node = new_parent->GetChild(new_index); - args->Append(new StringValue(base::Int64ToString(node->id()))); - DictionaryValue* object_args = new DictionaryValue(); - object_args->SetString(keys::kParentIdKey, - base::Int64ToString(new_parent->id())); - object_args->SetInteger(keys::kIndexKey, new_index); - object_args->SetString(keys::kOldParentIdKey, - base::Int64ToString(old_parent->id())); - object_args->SetInteger(keys::kOldIndexKey, old_index); - args->Append(object_args); - - DispatchEvent(model->profile(), keys::kOnBookmarkMoved, args.Pass()); -} - -void BookmarkEventRouter::BookmarkNodeAdded(BookmarkModel* model, - const BookmarkNode* parent, - int index) { - scoped_ptr args(new ListValue()); - const BookmarkNode* node = parent->GetChild(index); - args->Append(new StringValue(base::Int64ToString(node->id()))); - scoped_ptr tree_node( - bookmark_api_helpers::GetBookmarkTreeNode(node, false, false)); - args->Append(tree_node->ToValue().release()); - - DispatchEvent(model->profile(), keys::kOnBookmarkCreated, args.Pass()); -} - -void BookmarkEventRouter::BookmarkNodeRemoved(BookmarkModel* model, - const BookmarkNode* parent, - int index, - const BookmarkNode* node) { - scoped_ptr args(new ListValue()); - args->Append(new StringValue(base::Int64ToString(node->id()))); - DictionaryValue* object_args = new DictionaryValue(); - object_args->SetString(keys::kParentIdKey, - base::Int64ToString(parent->id())); - object_args->SetInteger(keys::kIndexKey, index); - args->Append(object_args); - - DispatchEvent(model->profile(), keys::kOnBookmarkRemoved, args.Pass()); -} - -void BookmarkEventRouter::BookmarkNodeChanged(BookmarkModel* model, - const BookmarkNode* node) { - scoped_ptr args(new ListValue()); - args->Append(new StringValue(base::Int64ToString(node->id()))); - - // TODO(erikkay) The only three things that BookmarkModel sends this - // notification for are title, url and favicon. Since we're currently - // ignoring favicon and since the notification doesn't say which one anyway, - // for now we only include title and url. The ideal thing would be to change - // BookmarkModel to indicate what changed. - DictionaryValue* object_args = new DictionaryValue(); - object_args->SetString(keys::kTitleKey, node->GetTitle()); - if (node->is_url()) - object_args->SetString(keys::kUrlKey, node->url().spec()); - args->Append(object_args); - - DispatchEvent(model->profile(), keys::kOnBookmarkChanged, args.Pass()); -} - -void BookmarkEventRouter::BookmarkNodeFaviconChanged(BookmarkModel* model, - const BookmarkNode* node) { - // TODO(erikkay) anything we should do here? -} - -void BookmarkEventRouter::BookmarkNodeChildrenReordered( - BookmarkModel* model, - const BookmarkNode* node) { - scoped_ptr args(new ListValue()); - args->Append(new StringValue(base::Int64ToString(node->id()))); - int childCount = node->child_count(); - ListValue* children = new ListValue(); - for (int i = 0; i < childCount; ++i) { - const BookmarkNode* child = node->GetChild(i); - Value* child_id = new StringValue(base::Int64ToString(child->id())); - children->Append(child_id); - } - DictionaryValue* reorder_info = new DictionaryValue(); - reorder_info->Set(keys::kChildIdsKey, children); - args->Append(reorder_info); - - DispatchEvent(model->profile(), keys::kOnBookmarkChildrenReordered, - args.Pass()); -} - -void BookmarkEventRouter::ExtensiveBookmarkChangesBeginning( - BookmarkModel* model) { - scoped_ptr args(new ListValue()); - DispatchEvent(model->profile(), - keys::kOnBookmarkImportBegan, - args.Pass()); -} - -void BookmarkEventRouter::ExtensiveBookmarkChangesEnded(BookmarkModel* model) { - scoped_ptr args(new ListValue()); - DispatchEvent(model->profile(), - keys::kOnBookmarkImportEnded, - args.Pass()); -} - -BookmarkAPI::BookmarkAPI(Profile* profile) : profile_(profile) { - ExtensionSystem::Get(profile_)->event_router()->RegisterObserver( - this, keys::kOnBookmarkCreated); - ExtensionSystem::Get(profile_)->event_router()->RegisterObserver( - this, keys::kOnBookmarkRemoved); - ExtensionSystem::Get(profile_)->event_router()->RegisterObserver( - this, keys::kOnBookmarkChanged); - ExtensionSystem::Get(profile_)->event_router()->RegisterObserver( - this, keys::kOnBookmarkMoved); - ExtensionSystem::Get(profile_)->event_router()->RegisterObserver( - this, keys::kOnBookmarkChildrenReordered); - ExtensionSystem::Get(profile_)->event_router()->RegisterObserver( - this, keys::kOnBookmarkImportBegan); - ExtensionSystem::Get(profile_)->event_router()->RegisterObserver( - this, keys::kOnBookmarkImportEnded); -} - -BookmarkAPI::~BookmarkAPI() { -} - -void BookmarkAPI::Shutdown() { - ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this); -} - -void BookmarkAPI::OnListenerAdded(const EventListenerInfo& details) { - bookmark_event_router_.reset(new BookmarkEventRouter( - BookmarkModelFactory::GetForProfile(profile_))); - ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this); -} - -bool BookmarksGetTreeFunction::RunImpl() { - scoped_ptr params( - bookmarks::Get::Params::Create(*args_)); - EXTENSION_FUNCTION_VALIDATE(params.get()); - - std::vector > nodes; - BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); - if (params->id_or_id_list_type == - bookmarks::Get::Params::ID_OR_ID_LIST_ARRAY) { - std::vector* ids = params->id_or_id_list_array.get(); - size_t count = ids->size(); - EXTENSION_FUNCTION_VALIDATE(count > 0); - for (size_t i = 0; i < count; ++i) { - int64 id; - if (!GetBookmarkIdAsInt64(ids->at(i), &id)) - return false; - const BookmarkNode* node = model->GetNodeByID(id); - if (!node) { - error_ = keys::kNoNodeError; - return false; - } else { - bookmark_api_helpers::AddNode(node, &nodes, false); - } - } - } else { - int64 id; - if (!GetBookmarkIdAsInt64(*params->id_or_id_list_string, &id)) - return false; - const BookmarkNode* node = model->GetNodeByID(id); - if (!node) { - error_ = keys::kNoNodeError; - return false; - } - bookmark_api_helpers::AddNode(node, &nodes, false); - } - - results_ = bookmarks::Get::Results::Create(nodes); - return true; -} - -bool BookmarksGetChildrenFunction::RunImpl() { - scoped_ptr params( - bookmarks::GetChildren::Params::Create(*args_)); - EXTENSION_FUNCTION_VALIDATE(params.get()); - - int64 id; - if (!GetBookmarkIdAsInt64(params->id, &id)) - return false; - - std::vector > nodes; - const BookmarkNode* node = - BookmarkModelFactory::GetForProfile(profile())->GetNodeByID(id); - if (!node) { - error_ = keys::kNoNodeError; - return false; - } - int child_count = node->child_count(); - for (int i = 0; i < child_count; ++i) { - const BookmarkNode* child = node->GetChild(i); - bookmark_api_helpers::AddNode(child, &nodes, false); - } - - results_ = bookmarks::GetChildren::Results::Create(nodes); - return true; -} - -bool BookmarksGetFunction::RunImpl() { - scoped_ptr params( - bookmarks::GetRecent::Params::Create(*args_)); - EXTENSION_FUNCTION_VALIDATE(params.get()); - if (params->number_of_items < 1) - return false; - - std::vector nodes; - bookmark_utils::GetMostRecentlyAddedEntries( - BookmarkModelFactory::GetForProfile(profile()), - params->number_of_items, - &nodes); - - std::vector > tree_nodes; - std::vector::iterator i = nodes.begin(); - for (; i != nodes.end(); ++i) { - const BookmarkNode* node = *i; - bookmark_api_helpers::AddNode(node, &tree_nodes, false); - } - - results_ = bookmarks::GetRecent::Results::Create(tree_nodes); - return true; -} - -bool BookmarksGetSubTreeFunction::RunImpl() { - std::vector > nodes; - const BookmarkNode* node = - BookmarkModelFactory::GetForProfile(profile())->root_node(); - bookmark_api_helpers::AddNode(node, &nodes, true); - results_ = bookmarks::GetTree::Results::Create(nodes); - return true; -} - -bool BookmarksGetRecentFunction::RunImpl() { - scoped_ptr params( - bookmarks::GetSubTree::Params::Create(*args_)); - EXTENSION_FUNCTION_VALIDATE(params.get()); - - int64 id; - if (!GetBookmarkIdAsInt64(params->id, &id)) - return false; - - const BookmarkNode* node = - BookmarkModelFactory::GetForProfile(profile())->GetNodeByID(id); - if (!node) { - error_ = keys::kNoNodeError; - return false; - } - - std::vector > nodes; - bookmark_api_helpers::AddNode(node, &nodes, true); - results_ = bookmarks::GetSubTree::Results::Create(nodes); - return true; -} - -bool BookmarksSearchFunction::RunImpl() { - scoped_ptr params( - bookmarks::Search::Params::Create(*args_)); - EXTENSION_FUNCTION_VALIDATE(params.get()); - - PrefServiceBase* prefs = PrefServiceBase::FromBrowserContext(profile_); - std::string lang = prefs->GetString(prefs::kAcceptLanguages); - std::vector nodes; - bookmark_utils::GetBookmarksContainingText( - BookmarkModelFactory::GetForProfile(profile()), - UTF8ToUTF16(params->query), - std::numeric_limits::max(), - lang, - &nodes); - - std::vector > tree_nodes; - for (std::vector::iterator node_iter = nodes.begin(); - node_iter != nodes.end(); ++node_iter) { - bookmark_api_helpers::AddNode(*node_iter, &tree_nodes, false); - } - - results_ = bookmarks::Search::Results::Create(tree_nodes); - return true; -} - -// static -bool BookmarksRemoveFunction::ExtractIds(const ListValue* args, - std::list* ids, - bool* invalid_id) { - std::string id_string; - if (!args->GetString(0, &id_string)) - return false; - int64 id; - if (base::StringToInt64(id_string, &id)) - ids->push_back(id); - else - *invalid_id = true; - return true; -} - -bool BookmarksRemoveFunction::RunImpl() { - if (!EditBookmarksEnabled()) - return false; - - scoped_ptr params( - bookmarks::Remove::Params::Create(*args_)); - EXTENSION_FUNCTION_VALIDATE(params.get()); - - int64 id; - if (!base::StringToInt64(params->id, &id)) { - error_ = keys::kInvalidIdError; - return false; - } - - bool recursive = false; - if (name() == BookmarksRemoveTreeFunction::function_name()) - recursive = true; - - BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); - if (!bookmark_api_helpers::RemoveNode(model, id, recursive, &error_)) - return false; - - return true; -} - -bool BookmarksCreateFunction::RunImpl() { - if (!EditBookmarksEnabled()) - return false; - - scoped_ptr params( - bookmarks::Create::Params::Create(*args_)); - EXTENSION_FUNCTION_VALIDATE(params.get()); - - BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); - int64 parentId; - - if (!params->bookmark.parent_id.get()) { - // Optional, default to "other bookmarks". - parentId = model->other_node()->id(); - } else { - if (!GetBookmarkIdAsInt64(*params->bookmark.parent_id, &parentId)) - return false; - } - const BookmarkNode* parent = model->GetNodeByID(parentId); - if (!parent) { - error_ = keys::kNoParentError; - return false; - } - if (parent->is_root()) { // Can't create children of the root. - error_ = keys::kModifySpecialError; - return false; - } - - int index; - if (!params->bookmark.index.get()) { // Optional (defaults to end). - index = parent->child_count(); - } else { - index = *params->bookmark.index; - if (index > parent->child_count() || index < 0) { - error_ = keys::kInvalidIndexError; - return false; - } - } - - string16 title; // Optional. - if (params->bookmark.title.get()) - title = UTF8ToUTF16(*params->bookmark.title.get()); - - std::string url_string; // Optional. - if (params->bookmark.url.get()) - url_string = *params->bookmark.url.get(); - - GURL url(url_string); - if (!url_string.empty() && !url.is_valid()) { - error_ = keys::kInvalidUrlError; - return false; - } - - const BookmarkNode* node; - if (url_string.length()) - node = model->AddURL(parent, index, title, url); - else - node = model->AddFolder(parent, index, title); - DCHECK(node); - if (!node) { - error_ = keys::kNoNodeError; - return false; - } - - scoped_ptr ret( - bookmark_api_helpers::GetBookmarkTreeNode(node, false, false)); - results_ = bookmarks::Create::Results::Create(*ret); - - return true; -} - -// static -bool BookmarksMoveFunction::ExtractIds(const ListValue* args, - std::list* ids, - bool* invalid_id) { - // For now, Move accepts ID parameters in the same way as an Update. - return BookmarksUpdateFunction::ExtractIds(args, ids, invalid_id); -} - -bool BookmarksMoveFunction::RunImpl() { - if (!EditBookmarksEnabled()) - return false; - - scoped_ptr params( - bookmarks::Move::Params::Create(*args_)); - EXTENSION_FUNCTION_VALIDATE(params.get()); - - int64 id; - if (!base::StringToInt64(params->id, &id)) { - error_ = keys::kInvalidIdError; - return false; - } - - BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); - const BookmarkNode* node = model->GetNodeByID(id); - if (!node) { - error_ = keys::kNoNodeError; - return false; - } - if (model->is_permanent_node(node)) { - error_ = keys::kModifySpecialError; - return false; - } - - const BookmarkNode* parent = NULL; - if (!params->destination.parent_id.get()) { - // Optional, defaults to current parent. - parent = node->parent(); - } else { - int64 parentId; - if (!GetBookmarkIdAsInt64(*params->destination.parent_id, &parentId)) - return false; - - parent = model->GetNodeByID(parentId); - } - if (!parent) { - error_ = keys::kNoParentError; - // TODO(erikkay) return an error message. - return false; - } - if (parent == model->root_node()) { - error_ = keys::kModifySpecialError; - return false; - } - - int index; - if (params->destination.index.get()) { // Optional (defaults to end). - index = *params->destination.index; - if (index > parent->child_count() || index < 0) { - error_ = keys::kInvalidIndexError; - return false; - } - } else { - index = parent->child_count(); - } - - model->Move(node, parent, index); - - scoped_ptr tree_node( - bookmark_api_helpers::GetBookmarkTreeNode(node, false, false)); - results_ = bookmarks::Move::Results::Create(*tree_node); - - return true; -} - -// static -bool BookmarksUpdateFunction::ExtractIds(const ListValue* args, - std::list* ids, - bool* invalid_id) { - // For now, Update accepts ID parameters in the same way as an Remove. - return BookmarksRemoveFunction::ExtractIds(args, ids, invalid_id); -} - -bool BookmarksUpdateFunction::RunImpl() { - if (!EditBookmarksEnabled()) - return false; - - scoped_ptr params( - bookmarks::Update::Params::Create(*args_)); - EXTENSION_FUNCTION_VALIDATE(params.get()); - - int64 id; - if (!base::StringToInt64(params->id, &id)) { - error_ = keys::kInvalidIdError; - return false; - } - - BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); - - // Optional but we need to distinguish non present from an empty title. - string16 title; - bool has_title = false; - if (params->changes.title.get()) { - title = UTF8ToUTF16(*params->changes.title); - has_title = true; - } - - // Optional. - std::string url_string; - if (params->changes.url.get()) - url_string = *params->changes.url; - GURL url(url_string); - if (!url_string.empty() && !url.is_valid()) { - error_ = keys::kInvalidUrlError; - return false; - } - - const BookmarkNode* node = model->GetNodeByID(id); - if (!node) { - error_ = keys::kNoNodeError; - return false; - } - if (model->is_permanent_node(node)) { - error_ = keys::kModifySpecialError; - return false; - } - if (has_title) - model->SetTitle(node, title); - if (!url.is_empty()) - model->SetURL(node, url); - - scoped_ptr tree_node( - bookmark_api_helpers::GetBookmarkTreeNode(node, false, false)); - results_ = bookmarks::Update::Results::Create(*tree_node); - return true; -} - -// Mapper superclass for BookmarkFunctions. -template -class BookmarkBucketMapper : public BucketMapper { - public: - virtual ~BookmarkBucketMapper() { STLDeleteValues(&buckets_); } - protected: - Bucket* GetBucket(const BucketIdType& id) { - Bucket* b = buckets_[id]; - if (b == NULL) { - b = new Bucket(); - buckets_[id] = b; - } - return b; - } - private: - std::map buckets_; -}; - -// Mapper for 'bookmarks.create'. Maps "same input to bookmarks.create" to a -// unique bucket. -class CreateBookmarkBucketMapper : public BookmarkBucketMapper { - public: - explicit CreateBookmarkBucketMapper(Profile* profile) : profile_(profile) {} - // TODO(tim): This should share code with BookmarksCreateFunction::RunImpl, - // but I can't figure out a good way to do that with all the macros. - virtual void GetBucketsForArgs(const ListValue* args, BucketList* buckets) { - const DictionaryValue* json; - if (!args->GetDictionary(0, &json)) - return; - - std::string parent_id; - if (json->HasKey(keys::kParentIdKey)) { - if (!json->GetString(keys::kParentIdKey, &parent_id)) - return; - } - BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile_); - - int64 parent_id_int64; - base::StringToInt64(parent_id, &parent_id_int64); - const BookmarkNode* parent = model->GetNodeByID(parent_id_int64); - if (!parent) - return; - - std::string bucket_id = UTF16ToUTF8(parent->GetTitle()); - std::string title; - json->GetString(keys::kTitleKey, &title); - std::string url_string; - json->GetString(keys::kUrlKey, &url_string); - - bucket_id += title; - bucket_id += url_string; - // 20 bytes (SHA1 hash length) is very likely less than most of the - // |bucket_id| strings we construct here, so we hash it to save space. - buckets->push_back(GetBucket(base::SHA1HashString(bucket_id))); - } - private: - Profile* profile_; -}; - -// Mapper for 'bookmarks.remove'. -class RemoveBookmarksBucketMapper : public BookmarkBucketMapper { - public: - explicit RemoveBookmarksBucketMapper(Profile* profile) : profile_(profile) {} - virtual void GetBucketsForArgs(const ListValue* args, BucketList* buckets) { - typedef std::list IdList; - IdList ids; - bool invalid_id = false; - if (!BookmarksRemoveFunction::ExtractIds(args, &ids, &invalid_id) || - invalid_id) { - return; - } - - for (IdList::iterator it = ids.begin(); it != ids.end(); ++it) { - BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile_); - const BookmarkNode* node = model->GetNodeByID(*it); - if (!node || node->is_root()) - return; - - std::string bucket_id; - bucket_id += UTF16ToUTF8(node->parent()->GetTitle()); - bucket_id += UTF16ToUTF8(node->GetTitle()); - bucket_id += node->url().spec(); - buckets->push_back(GetBucket(base::SHA1HashString(bucket_id))); - } - } - private: - Profile* profile_; -}; - -// Mapper for any bookmark function accepting bookmark IDs as parameters, where -// a distinct ID corresponds to a single item in terms of quota limiting. This -// is inappropriate for bookmarks.remove, for example, since repeated removals -// of the same item will actually have a different ID each time. -template -class BookmarkIdMapper : public BookmarkBucketMapper { - public: - typedef std::list IdList; - virtual void GetBucketsForArgs(const ListValue* args, BucketList* buckets) { - IdList ids; - bool invalid_id = false; - if (!FunctionType::ExtractIds(args, &ids, &invalid_id) || invalid_id) - return; - for (IdList::iterator it = ids.begin(); it != ids.end(); ++it) - buckets->push_back(GetBucket(*it)); - } -}; - -// Builds heuristics for all BookmarkFunctions using specialized BucketMappers. -class BookmarksQuotaLimitFactory { - public: - // For id-based bookmark functions. - template - static void Build(QuotaLimitHeuristics* heuristics) { - BuildWithMappers(heuristics, new BookmarkIdMapper(), - new BookmarkIdMapper()); - } - - // For bookmarks.create. - static void BuildForCreate(QuotaLimitHeuristics* heuristics, - Profile* profile) { - BuildWithMappers(heuristics, new CreateBookmarkBucketMapper(profile), - new CreateBookmarkBucketMapper(profile)); - } - - // For bookmarks.remove. - static void BuildForRemove(QuotaLimitHeuristics* heuristics, - Profile* profile) { - BuildWithMappers(heuristics, new RemoveBookmarksBucketMapper(profile), - new RemoveBookmarksBucketMapper(profile)); - } - - private: - static void BuildWithMappers(QuotaLimitHeuristics* heuristics, - BucketMapper* short_mapper, BucketMapper* long_mapper) { - const Config kSustainedLimitConfig = { - // See bookmarks.json for current value. - bookmarks::MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE, - TimeDelta::FromMinutes(1) - }; - heuristics->push_back(new SustainedLimit( - TimeDelta::FromMinutes(10), - kSustainedLimitConfig, - short_mapper, - "MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE")); - - const Config kTimedLimitConfig = { - // See bookmarks.json for current value. - bookmarks::MAX_WRITE_OPERATIONS_PER_HOUR, - TimeDelta::FromHours(1) - }; - heuristics->push_back(new TimedLimit( - kTimedLimitConfig, - long_mapper, - "MAX_WRITE_OPERATIONS_PER_HOUR")); - } - - DISALLOW_IMPLICIT_CONSTRUCTORS(BookmarksQuotaLimitFactory); -}; - -// And finally, building the individual heuristics for each function. -void BookmarksRemoveFunction::GetQuotaLimitHeuristics( - QuotaLimitHeuristics* heuristics) const { - BookmarksQuotaLimitFactory::BuildForRemove(heuristics, profile()); -} - -void BookmarksMoveFunction::GetQuotaLimitHeuristics( - QuotaLimitHeuristics* heuristics) const { - BookmarksQuotaLimitFactory::Build(heuristics); -} - -void BookmarksUpdateFunction::GetQuotaLimitHeuristics( - QuotaLimitHeuristics* heuristics) const { - BookmarksQuotaLimitFactory::Build(heuristics); -}; - -void BookmarksCreateFunction::GetQuotaLimitHeuristics( - QuotaLimitHeuristics* heuristics) const { - BookmarksQuotaLimitFactory::BuildForCreate(heuristics, profile()); -} - -BookmarksIOFunction::BookmarksIOFunction() {} - -BookmarksIOFunction::~BookmarksIOFunction() { - // There may be pending file dialogs, we need to tell them that we've gone - // away so they don't try and call back to us. - if (select_file_dialog_.get()) - select_file_dialog_->ListenerDestroyed(); -} - -void BookmarksIOFunction::SelectFile(ui::SelectFileDialog::Type type) { - // GetDefaultFilepathForBookmarkExport() might have to touch the filesystem - // (stat or access, for example), so this requires a thread with IO allowed. - if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { - BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, - base::Bind(&BookmarksIOFunction::SelectFile, this, type)); - return; - } - - // Pre-populating the filename field in case this is a SELECT_SAVEAS_FILE - // dialog. If not, there is no filename field in the dialog box. - FilePath default_path; - if (type == ui::SelectFileDialog::SELECT_SAVEAS_FILE) - default_path = GetDefaultFilepathForBookmarkExport(); - else - DCHECK(type == ui::SelectFileDialog::SELECT_OPEN_FILE); - - // After getting the |default_path|, ask the UI to display the file dialog. - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&BookmarksIOFunction::ShowSelectFileDialog, this, - type, default_path)); -} - -void BookmarksIOFunction::ShowSelectFileDialog(ui::SelectFileDialog::Type type, - const FilePath& default_path) { - // Balanced in one of the three callbacks of SelectFileDialog: - // either FileSelectionCanceled, MultiFilesSelected, or FileSelected - AddRef(); - - WebContents* web_contents = dispatcher()->delegate()-> - GetAssociatedWebContents(); - - select_file_dialog_ = ui::SelectFileDialog::Create( - this, new ChromeSelectFilePolicy(web_contents)); - ui::SelectFileDialog::FileTypeInfo file_type_info; - file_type_info.extensions.resize(1); - file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("html")); - // TODO(kinaba): http://crbug.com/140425. Turn file_type_info.support_gdata - // on for saving once Google Drive client on ChromeOS supports it. - if (type == ui::SelectFileDialog::SELECT_OPEN_FILE) - file_type_info.support_gdata = true; - // |web_contents| can be NULL (for background pages), which is fine. In such - // a case if file-selection dialogs are forbidden by policy, we will not - // show an InfoBar, which is better than letting one appear out of the blue. - select_file_dialog_->SelectFile(type, - string16(), - default_path, - &file_type_info, - 0, - FILE_PATH_LITERAL(""), - NULL, - NULL); -} - -void BookmarksIOFunction::FileSelectionCanceled(void* params) { - Release(); // Balanced in BookmarksIOFunction::SelectFile() -} - -void BookmarksIOFunction::MultiFilesSelected( - const std::vector& files, void* params) { - Release(); // Balanced in BookmarsIOFunction::SelectFile() - NOTREACHED() << "Should not be able to select multiple files"; -} - -bool BookmarksImportFunction::RunImpl() { - if (!EditBookmarksEnabled()) - return false; - SelectFile(ui::SelectFileDialog::SELECT_OPEN_FILE); - return true; -} - -void BookmarksImportFunction::FileSelected(const FilePath& path, - int index, - void* params) { -#if !defined(OS_ANDROID) - // Android does not have support for the standard importers. - // TODO(jgreenwald): remove ifdef once extensions are no longer built on - // Android. - scoped_refptr importer_host(new ImporterHost); - importer::SourceProfile source_profile; - source_profile.importer_type = importer::TYPE_BOOKMARKS_FILE; - source_profile.source_path = path; - importer_host->StartImportSettings(source_profile, - profile(), - importer::FAVORITES, - new ProfileWriter(profile()), - true); -#endif - Release(); // Balanced in BookmarksIOFunction::SelectFile() -} - -bool BookmarksExportFunction::RunImpl() { - SelectFile(ui::SelectFileDialog::SELECT_SAVEAS_FILE); - return true; -} - -void BookmarksExportFunction::FileSelected(const FilePath& path, - int index, - void* params) { -#if !defined(OS_ANDROID) - // Android does not have support for the standard exporter. - // TODO(jgreenwald): remove ifdef once extensions are no longer built on - // Android. - bookmark_html_writer::WriteBookmarks(profile(), path, NULL); -#endif - Release(); // Balanced in BookmarksIOFunction::SelectFile() -} - -} // namespace extensions diff --git a/chrome/browser/extensions/api/bookmarks/bookmarks_api.h b/chrome/browser/extensions/api/bookmarks/bookmarks_api.h deleted file mode 100644 index 0d2a240..0000000 --- a/chrome/browser/extensions/api/bookmarks/bookmarks_api.h +++ /dev/null @@ -1,321 +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_API_BOOKMARKS_BOOKMARKS_API_H_ -#define CHROME_BROWSER_EXTENSIONS_API_BOOKMARKS_BOOKMARKS_API_H_ - -#include -#include -#include - -#include "base/compiler_specific.h" -#include "base/memory/ref_counted.h" -#include "chrome/browser/bookmarks/bookmark_model_observer.h" -#include "chrome/browser/extensions/event_router.h" -#include "chrome/browser/extensions/extension_function.h" -#include "chrome/browser/profiles/profile_keyed_service.h" -#include "content/public/browser/notification_observer.h" -#include "content/public/browser/notification_registrar.h" -#include "ui/base/dialogs/select_file_dialog.h" - -class FilePath; - -namespace base { -class ListValue; -} - -namespace extensions { - -// Observes BookmarkModel and then routes the notifications as events to -// the extension system. -class BookmarkEventRouter : public BookmarkModelObserver { - public: - explicit BookmarkEventRouter(BookmarkModel* model); - virtual ~BookmarkEventRouter(); - - // BookmarkModelObserver: - 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 BookmarkNodeRemoved(BookmarkModel* model, - const BookmarkNode* parent, - int old_index, - const BookmarkNode* node) OVERRIDE; - virtual void BookmarkNodeChanged(BookmarkModel* model, - const BookmarkNode* node) OVERRIDE; - virtual void BookmarkNodeFaviconChanged(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; - - private: - // Helper to actually dispatch an event to extension listeners. - void DispatchEvent(Profile* profile, - const char* event_name, - scoped_ptr event_args); - - BookmarkModel* model_; - - DISALLOW_COPY_AND_ASSIGN(BookmarkEventRouter); -}; - -class BookmarkAPI : public ProfileKeyedService, - public EventRouter::Observer { - public: - explicit BookmarkAPI(Profile* profile); - virtual ~BookmarkAPI(); - - // ProfileKeyedService implementation. - virtual void Shutdown() OVERRIDE; - - // EventRouter::Observer implementation. - virtual void OnListenerAdded(const EventListenerInfo& details) - OVERRIDE; - - private: - Profile* profile_; - - // Created lazily upon OnListenerAdded. - scoped_ptr bookmark_event_router_; -}; - -class BookmarksFunction : public AsyncExtensionFunction, - public content::NotificationObserver { - public: - // AsyncExtensionFunction: - virtual void Run() OVERRIDE; - - protected: - virtual ~BookmarksFunction() {} - - // Helper to get the bookmark id as int64 from the given string id. - // Sets error_ to an error string if the given id string can't be parsed - // as an int64. In case of error, doesn't change id and returns false. - bool GetBookmarkIdAsInt64(const std::string& id_string, int64* id); - - // Helper that checks if bookmark editing is enabled. If it's not, this sets - // error_ to the appropriate error string. - bool EditBookmarksEnabled(); - - private: - // content::NotificationObserver: - virtual void Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) OVERRIDE; - - content::NotificationRegistrar registrar_; -}; - -class BookmarksGetTreeFunction : public BookmarksFunction { - public: - DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.get") - - protected: - virtual ~BookmarksGetTreeFunction() {} - - // ExtensionFunction: - virtual bool RunImpl() OVERRIDE; -}; - -class BookmarksGetChildrenFunction : public BookmarksFunction { - public: - DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.getChildren") - - protected: - virtual ~BookmarksGetChildrenFunction() {} - - // ExtensionFunction: - virtual bool RunImpl() OVERRIDE; -}; - -class BookmarksGetFunction : public BookmarksFunction { - public: - DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.getRecent") - - protected: - virtual ~BookmarksGetFunction() {} - - // ExtensionFunction: - virtual bool RunImpl() OVERRIDE; -}; - -class BookmarksGetSubTreeFunction : public BookmarksFunction { - public: - DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.getTree") - - protected: - virtual ~BookmarksGetSubTreeFunction() {} - - // ExtensionFunction: - virtual bool RunImpl() OVERRIDE; -}; - -class BookmarksGetRecentFunction : public BookmarksFunction { - public: - DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.getSubTree") - - protected: - virtual ~BookmarksGetRecentFunction() {} - - // ExtensionFunction: - virtual bool RunImpl() OVERRIDE; -}; - -class BookmarksSearchFunction : public BookmarksFunction { - public: - DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.search") - - protected: - virtual ~BookmarksSearchFunction() {} - - // ExtensionFunction: - virtual bool RunImpl() OVERRIDE; -}; - -class BookmarksRemoveFunction : public BookmarksFunction { - public: - DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.remove") - - // Returns true on successful parse and sets invalid_id to true if conversion - // from id string to int64 failed. - static bool ExtractIds(const base::ListValue* args, std::list* ids, - bool* invalid_id); - // ExtensionFunction: - virtual void GetQuotaLimitHeuristics( - QuotaLimitHeuristics* heuristics) const OVERRIDE; - - protected: - virtual ~BookmarksRemoveFunction() {} - - // ExtensionFunction: - virtual bool RunImpl() OVERRIDE; -}; - -class BookmarksRemoveTreeFunction : public BookmarksRemoveFunction { - public: - DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.removeTree") - - protected: - virtual ~BookmarksRemoveTreeFunction() {} -}; - -class BookmarksCreateFunction : public BookmarksFunction { - public: - DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.create") - - // ExtensionFunction: - virtual void GetQuotaLimitHeuristics( - QuotaLimitHeuristics* heuristics) const OVERRIDE; - - protected: - virtual ~BookmarksCreateFunction() {} - - // ExtensionFunction: - virtual bool RunImpl() OVERRIDE; -}; - -class BookmarksMoveFunction : public BookmarksFunction { - public: - DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.move") - - static bool ExtractIds(const base::ListValue* args, std::list* ids, - bool* invalid_id); - - // ExtensionFunction: - virtual void GetQuotaLimitHeuristics( - QuotaLimitHeuristics* heuristics) const OVERRIDE; - - protected: - virtual ~BookmarksMoveFunction() {} - - // ExtensionFunction: - virtual bool RunImpl() OVERRIDE; -}; - -class BookmarksUpdateFunction : public BookmarksFunction { - public: - DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.update") - - static bool ExtractIds(const base::ListValue* args, std::list* ids, - bool* invalid_id); - - // ExtensionFunction: - virtual void GetQuotaLimitHeuristics( - QuotaLimitHeuristics* heuristics) const OVERRIDE; - - protected: - virtual ~BookmarksUpdateFunction() {} - - // ExtensionFunction: - virtual bool RunImpl() OVERRIDE; -}; - -class BookmarksIOFunction : public BookmarksFunction, - public ui::SelectFileDialog::Listener { - public: - BookmarksIOFunction(); - - virtual void FileSelected(const FilePath& path, int index, void* params) = 0; - - // ui::SelectFileDialog::Listener: - virtual void MultiFilesSelected(const std::vector& files, - void* params) OVERRIDE; - virtual void FileSelectionCanceled(void* params) OVERRIDE; - - void SelectFile(ui::SelectFileDialog::Type type); - - protected: - virtual ~BookmarksIOFunction(); - - private: - void ShowSelectFileDialog( - ui::SelectFileDialog::Type type, - const FilePath& default_path); - - protected: - scoped_refptr select_file_dialog_; -}; - -class BookmarksImportFunction : public BookmarksIOFunction { - public: - DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.import"); - - // BookmarkManagerIOFunction: - virtual void FileSelected(const FilePath& path, int index, void* params) - OVERRIDE; - - private: - virtual ~BookmarksImportFunction() {} - - // ExtensionFunction: - virtual bool RunImpl() OVERRIDE; -}; - -class BookmarksExportFunction : public BookmarksIOFunction { - public: - DECLARE_EXTENSION_FUNCTION_NAME("bookmarks.export"); - - // BookmarkManagerIOFunction: - virtual void FileSelected(const FilePath& path, int index, void* params) - OVERRIDE; - - private: - virtual ~BookmarksExportFunction() {} - - // ExtensionFunction: - virtual bool RunImpl() OVERRIDE; -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_BOOKMARKS_BOOKMARKS_API_H_ diff --git a/chrome/browser/extensions/api/cloud_print_private/cloud_print_private_api.cc b/chrome/browser/extensions/api/cloud_print_private/cloud_print_private_api.cc index 0c77b09..2bb3a84 100644 --- a/chrome/browser/extensions/api/cloud_print_private/cloud_print_private_api.cc +++ b/chrome/browser/extensions/api/cloud_print_private/cloud_print_private_api.cc @@ -31,16 +31,14 @@ CloudPrintTestsDelegate::~CloudPrintTestsDelegate() { instance_ = NULL; } -CloudPrintPrivateSetupConnectorFunction:: - CloudPrintPrivateSetupConnectorFunction() { +CloudPrintSetupConnectorFunction::CloudPrintSetupConnectorFunction() { } -CloudPrintPrivateSetupConnectorFunction:: - ~CloudPrintPrivateSetupConnectorFunction() { +CloudPrintSetupConnectorFunction::~CloudPrintSetupConnectorFunction() { } -bool CloudPrintPrivateSetupConnectorFunction::RunImpl() { +bool CloudPrintSetupConnectorFunction::RunImpl() { using extensions::api::cloud_print_private::SetupConnector::Params; scoped_ptr params(Params::Create(*args_)); if (CloudPrintTestsDelegate::instance()) { @@ -62,13 +60,13 @@ bool CloudPrintPrivateSetupConnectorFunction::RunImpl() { return true; } -CloudPrintPrivateGetHostNameFunction::CloudPrintPrivateGetHostNameFunction() { +CloudPrintGetHostNameFunction::CloudPrintGetHostNameFunction() { } -CloudPrintPrivateGetHostNameFunction::~CloudPrintPrivateGetHostNameFunction() { +CloudPrintGetHostNameFunction::~CloudPrintGetHostNameFunction() { } -bool CloudPrintPrivateGetHostNameFunction::RunImpl() { +bool CloudPrintGetHostNameFunction::RunImpl() { SetResult(Value::CreateStringValue( CloudPrintTestsDelegate::instance() ? CloudPrintTestsDelegate::instance()->GetHostName() : @@ -77,19 +75,19 @@ bool CloudPrintPrivateGetHostNameFunction::RunImpl() { return true; } -CloudPrintPrivateGetPrintersFunction::CloudPrintPrivateGetPrintersFunction() { +CloudPrintGetPrintersFunction::CloudPrintGetPrintersFunction() { } -CloudPrintPrivateGetPrintersFunction::~CloudPrintPrivateGetPrintersFunction() { +CloudPrintGetPrintersFunction::~CloudPrintGetPrintersFunction() { } -void CloudPrintPrivateGetPrintersFunction::ReturnResult( +void CloudPrintGetPrintersFunction::ReturnResult( const base::ListValue* printers) { SetResult(printers->DeepCopy()); SendResponse(true); } -void CloudPrintPrivateGetPrintersFunction::CollectPrinters() { +void CloudPrintGetPrintersFunction::CollectPrinters() { scoped_ptr result(new base::ListValue()); if (CloudPrintTestsDelegate::instance()) { std::vector printers = @@ -106,14 +104,14 @@ void CloudPrintPrivateGetPrintersFunction::CollectPrinters() { result->Append(Value::CreateStringValue(printers[i].printer_name)); } content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, - base::Bind(&CloudPrintPrivateGetPrintersFunction::ReturnResult, this, + base::Bind(&CloudPrintGetPrintersFunction::ReturnResult, this, base::Owned(result.release()))); } -bool CloudPrintPrivateGetPrintersFunction::RunImpl() { +bool CloudPrintGetPrintersFunction::RunImpl() { content::BrowserThread::GetBlockingPool()->PostTask(FROM_HERE, - base::Bind(&CloudPrintPrivateGetPrintersFunction::CollectPrinters, this)); + base::Bind(&CloudPrintGetPrintersFunction::CollectPrinters, this)); return true; } diff --git a/chrome/browser/extensions/api/cloud_print_private/cloud_print_private_api.h b/chrome/browser/extensions/api/cloud_print_private/cloud_print_private_api.h index 030bf67..2a23b9e 100644 --- a/chrome/browser/extensions/api/cloud_print_private/cloud_print_private_api.h +++ b/chrome/browser/extensions/api/cloud_print_private/cloud_print_private_api.h @@ -36,40 +36,40 @@ class CloudPrintTestsDelegate { static CloudPrintTestsDelegate* instance_; }; -class CloudPrintPrivateSetupConnectorFunction : public AsyncExtensionFunction { +class CloudPrintSetupConnectorFunction : public AsyncExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("cloudPrintPrivate.setupConnector"); - CloudPrintPrivateSetupConnectorFunction(); + CloudPrintSetupConnectorFunction(); protected: - virtual ~CloudPrintPrivateSetupConnectorFunction(); + virtual ~CloudPrintSetupConnectorFunction(); // ExtensionFunction: virtual bool RunImpl() OVERRIDE; }; -class CloudPrintPrivateGetHostNameFunction : public AsyncExtensionFunction { +class CloudPrintGetHostNameFunction : public AsyncExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("cloudPrintPrivate.getHostName"); - CloudPrintPrivateGetHostNameFunction(); + CloudPrintGetHostNameFunction(); protected: - virtual ~CloudPrintPrivateGetHostNameFunction(); + virtual ~CloudPrintGetHostNameFunction(); // ExtensionFunction: virtual bool RunImpl() OVERRIDE; }; -class CloudPrintPrivateGetPrintersFunction : public AsyncExtensionFunction { +class CloudPrintGetPrintersFunction : public AsyncExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("cloudPrintPrivate.getPrinters"); - CloudPrintPrivateGetPrintersFunction(); + CloudPrintGetPrintersFunction(); protected: - virtual ~CloudPrintPrivateGetPrintersFunction(); + virtual ~CloudPrintGetPrintersFunction(); void CollectPrinters(); void ReturnResult(const base::ListValue* printers); diff --git a/chrome/browser/extensions/api/content_settings/content_settings_api.cc b/chrome/browser/extensions/api/content_settings/content_settings_api.cc index e1c4a08..87c1798 100644 --- a/chrome/browser/extensions/api/content_settings/content_settings_api.cc +++ b/chrome/browser/extensions/api/content_settings/content_settings_api.cc @@ -62,7 +62,7 @@ namespace extensions { namespace helpers = content_settings_helpers; namespace keys = content_settings_api_constants; -bool ContentSettingsClearFunction::RunImpl() { +bool ClearContentSettingsFunction::RunImpl() { ContentSettingsType content_type; EXTENSION_FUNCTION_VALIDATE(RemoveContentType(args_.get(), &content_type)); @@ -96,7 +96,7 @@ bool ContentSettingsClearFunction::RunImpl() { return true; } -bool ContentSettingsGetFunction::RunImpl() { +bool GetContentSettingFunction::RunImpl() { ContentSettingsType content_type; EXTENSION_FUNCTION_VALIDATE(RemoveContentType(args_.get(), &content_type)); @@ -169,7 +169,7 @@ bool ContentSettingsGetFunction::RunImpl() { return true; } -bool ContentSettingsSetFunction::RunImpl() { +bool SetContentSettingFunction::RunImpl() { ContentSettingsType content_type; EXTENSION_FUNCTION_VALIDATE(RemoveContentType(args_.get(), &content_type)); @@ -250,7 +250,7 @@ bool ContentSettingsSetFunction::RunImpl() { return true; } -bool ContentSettingsGetResourceIdentifiersFunction::RunImpl() { +bool GetResourceIdentifiersFunction::RunImpl() { ContentSettingsType content_type; EXTENSION_FUNCTION_VALIDATE(RemoveContentType(args_.get(), &content_type)); @@ -261,15 +261,14 @@ bool ContentSettingsGetResourceIdentifiersFunction::RunImpl() { if (!g_testing_plugins_) { PluginService::GetInstance()->GetPlugins( - base::Bind(&ContentSettingsGetResourceIdentifiersFunction::OnGotPlugins, - this)); + base::Bind(&GetResourceIdentifiersFunction::OnGotPlugins, this)); } else { OnGotPlugins(*g_testing_plugins_); } return true; } -void ContentSettingsGetResourceIdentifiersFunction::OnGotPlugins( +void GetResourceIdentifiersFunction::OnGotPlugins( const std::vector& plugins) { PluginFinder* finder = PluginFinder::GetInstance(); std::set group_identifiers; @@ -290,13 +289,11 @@ void ContentSettingsGetResourceIdentifiersFunction::OnGotPlugins( SetResult(list); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind( - &ContentSettingsGetResourceIdentifiersFunction::SendResponse, - this, - true)); + &GetResourceIdentifiersFunction::SendResponse, this, true)); } // static -void ContentSettingsGetResourceIdentifiersFunction::SetPluginsForTesting( +void GetResourceIdentifiersFunction::SetPluginsForTesting( const std::vector* plugins) { g_testing_plugins_ = plugins; } diff --git a/chrome/browser/extensions/api/content_settings/content_settings_api.h b/chrome/browser/extensions/api/content_settings/content_settings_api.h index aac0b81..20ffcc7 100644 --- a/chrome/browser/extensions/api/content_settings/content_settings_api.h +++ b/chrome/browser/extensions/api/content_settings/content_settings_api.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_EXTENSIONS_API_CONTENT_SETTINGS_CONTENT_SETTINGS_API_H_ -#define CHROME_BROWSER_EXTENSIONS_API_CONTENT_SETTINGS_CONTENT_SETTINGS_API_H_ +#ifndef CHROME_BROWSER_EXTENSIONS_API_CONTENT_SETTINGS_CONTENT_SETTINGS_API_H__ +#define CHROME_BROWSER_EXTENSIONS_API_CONTENT_SETTINGS_CONTENT_SETTINGS_API_H__ #include "chrome/browser/extensions/extension_function.h" @@ -15,46 +15,45 @@ struct WebPluginInfo; namespace extensions { -class ContentSettingsClearFunction : public SyncExtensionFunction { +class ClearContentSettingsFunction : public SyncExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("contentSettings.clear") protected: - virtual ~ContentSettingsClearFunction() {} + virtual ~ClearContentSettingsFunction() {} // ExtensionFunction: virtual bool RunImpl() OVERRIDE; }; -class ContentSettingsGetFunction : public SyncExtensionFunction { +class GetContentSettingFunction : public SyncExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("contentSettings.get") protected: - virtual ~ContentSettingsGetFunction() {} + virtual ~GetContentSettingFunction() {} // ExtensionFunction: virtual bool RunImpl() OVERRIDE; }; -class ContentSettingsSetFunction : public SyncExtensionFunction { +class SetContentSettingFunction : public SyncExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("contentSettings.set") protected: - virtual ~ContentSettingsSetFunction() {} + virtual ~SetContentSettingFunction() {} // ExtensionFunction: virtual bool RunImpl() OVERRIDE; }; -class ContentSettingsGetResourceIdentifiersFunction - : public AsyncExtensionFunction { +class GetResourceIdentifiersFunction : public AsyncExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("contentSettings.getResourceIdentifiers") protected: - virtual ~ContentSettingsGetResourceIdentifiersFunction() {} + virtual ~GetResourceIdentifiersFunction() {} // ExtensionFunction: virtual bool RunImpl() OVERRIDE; @@ -74,4 +73,4 @@ class ContentSettingsGetResourceIdentifiersFunction } // namespace extensions -#endif // CHROME_BROWSER_EXTENSIONS_API_CONTENT_SETTINGS_CONTENT_SETTINGS_API_H_ +#endif // CHROME_BROWSER_EXTENSIONS_API_CONTENT_SETTINGS_CONTENT_SETTINGS_API_H__ 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 381346e..69227d1 100644 --- a/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc +++ b/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc @@ -3,9 +3,9 @@ // found in the LICENSE file. #include "base/utf_string_conversions.h" +#include "chrome/browser/extensions/api/content_settings/content_settings_api.h" #include "chrome/browser/content_settings/cookie_settings.h" #include "chrome/browser/content_settings/host_content_settings_map.h" -#include "chrome/browser/extensions/api/content_settings/content_settings_api.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profiles/profile.h" @@ -120,12 +120,12 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, std::vector plugins; plugin_list.GetPlugins(&plugins); - ContentSettingsGetResourceIdentifiersFunction::SetPluginsForTesting(&plugins); + GetResourceIdentifiersFunction::SetPluginsForTesting(&plugins); EXPECT_TRUE(RunExtensionTest("content_settings/getresourceidentifiers")) << message_; - ContentSettingsGetResourceIdentifiersFunction::SetPluginsForTesting(NULL); + GetResourceIdentifiersFunction::SetPluginsForTesting(NULL); } } // namespace extensions diff --git a/chrome/browser/extensions/api/context_menu/context_menu_api.cc b/chrome/browser/extensions/api/context_menu/context_menu_api.cc new file mode 100644 index 0000000..0d98897 --- /dev/null +++ b/chrome/browser/extensions/api/context_menu/context_menu_api.cc @@ -0,0 +1,398 @@ +// 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/api/context_menu/context_menu_api.h" + +#include + +#include "base/string_number_conversions.h" +#include "base/string_util.h" +#include "base/values.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/menu_manager.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/extensions/api/context_menus.h" +#include "extensions/common/error_utils.h" +#include "extensions/common/url_pattern_set.h" + +using extensions::ErrorUtils; + +namespace { + +const char kGeneratedIdKey[] = "generatedId"; + +const char kCannotFindItemError[] = "Cannot find menu item with id *"; +const char kOnclickDisallowedError[] = "Extensions using event pages cannot " + "pass an onclick parameter to chrome.contextMenus.create. Instead, use " + "the chrome.contextMenus.onClicked event."; +const char kCheckedError[] = + "Only items with type \"radio\" or \"checkbox\" can be checked"; +const char kDuplicateIDError[] = + "Cannot create item with duplicate id *"; +const char kIdRequiredError[] = "Extensions using event pages must pass an " + "id parameter to chrome.contextMenus.create"; +const char kParentsMustBeNormalError[] = + "Parent items must have type \"normal\""; +const char kTitleNeededError[] = + "All menu items except for separators must have a title"; +const char kLauncherNotAllowedError[] = + "Only packaged apps are allowed to use 'launcher' context"; + +std::string GetIDString(const extensions::MenuItem::Id& id) { + if (id.uid == 0) + return id.string_uid; + else + return base::IntToString(id.uid); +} + +template +extensions::MenuItem::ContextList GetContexts( + const PropertyWithEnumT& property) { + extensions::MenuItem::ContextList contexts; + for (size_t i = 0; i < property.contexts->size(); ++i) { + switch (property.contexts->at(i)) { + case PropertyWithEnumT::CONTEXTS_ELEMENT_ALL: + contexts.Add(extensions::MenuItem::ALL); + break; + case PropertyWithEnumT::CONTEXTS_ELEMENT_PAGE: + contexts.Add(extensions::MenuItem::PAGE); + break; + case PropertyWithEnumT::CONTEXTS_ELEMENT_SELECTION: + contexts.Add(extensions::MenuItem::SELECTION); + break; + case PropertyWithEnumT::CONTEXTS_ELEMENT_LINK: + contexts.Add(extensions::MenuItem::LINK); + break; + case PropertyWithEnumT::CONTEXTS_ELEMENT_EDITABLE: + contexts.Add(extensions::MenuItem::EDITABLE); + break; + case PropertyWithEnumT::CONTEXTS_ELEMENT_IMAGE: + contexts.Add(extensions::MenuItem::IMAGE); + break; + case PropertyWithEnumT::CONTEXTS_ELEMENT_VIDEO: + contexts.Add(extensions::MenuItem::VIDEO); + break; + case PropertyWithEnumT::CONTEXTS_ELEMENT_AUDIO: + contexts.Add(extensions::MenuItem::AUDIO); + break; + case PropertyWithEnumT::CONTEXTS_ELEMENT_FRAME: + contexts.Add(extensions::MenuItem::FRAME); + break; + case PropertyWithEnumT::CONTEXTS_ELEMENT_LAUNCHER: + contexts.Add(extensions::MenuItem::LAUNCHER); + break; + case PropertyWithEnumT::CONTEXTS_ELEMENT_NONE: + NOTREACHED(); + } + } + return contexts; +} + +template +extensions::MenuItem::Type GetType(const PropertyWithEnumT& property) { + switch (property.type) { + case PropertyWithEnumT::TYPE_NONE: + case PropertyWithEnumT::TYPE_NORMAL: + return extensions::MenuItem::NORMAL; + case PropertyWithEnumT::TYPE_CHECKBOX: + return extensions::MenuItem::CHECKBOX; + case PropertyWithEnumT::TYPE_RADIO: + return extensions::MenuItem::RADIO; + case PropertyWithEnumT::TYPE_SEPARATOR: + return extensions::MenuItem::SEPARATOR; + } + return extensions::MenuItem::NORMAL; +} + +template +scoped_ptr GetParentId( + const PropertyWithEnumT& property, + bool is_off_the_record, + std::string extension_id) { + scoped_ptr parent_id( + new extensions::MenuItem::Id(is_off_the_record, extension_id)); + switch (property.parent_id_type) { + case PropertyWithEnumT::PARENT_ID_NONE: + return scoped_ptr().Pass(); + case PropertyWithEnumT::PARENT_ID_INTEGER: + parent_id->uid = *property.parent_id_integer; + break; + case PropertyWithEnumT::PARENT_ID_STRING: + parent_id->string_uid = *property.parent_id_string; + break; + } + return parent_id.Pass(); +} + +extensions::MenuItem* GetParent(extensions::MenuItem::Id parent_id, + const extensions::MenuManager* menu_manager, + std::string* error) { + extensions::MenuItem* parent = menu_manager->GetItemById(parent_id); + if (!parent) { + *error = ErrorUtils::FormatErrorMessage( + kCannotFindItemError, GetIDString(parent_id)); + return NULL; + } + if (parent->type() != extensions::MenuItem::NORMAL) { + *error = kParentsMustBeNormalError; + return NULL; + } + + return parent; +} + +} // namespace + +namespace extensions { + +namespace Create = api::context_menus::Create; +namespace Remove = api::context_menus::Remove; +namespace Update = api::context_menus::Update; + +bool CreateContextMenuFunction::RunImpl() { + MenuItem::Id id(profile()->IsOffTheRecord(), extension_id()); + scoped_ptr params(Create::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + if (params->create_properties.id.get()) { + id.string_uid = *params->create_properties.id; + } else { + if (GetExtension()->has_lazy_background_page()) { + error_ = kIdRequiredError; + return false; + } + + // The Generated Id is added by context_menus_custom_bindings.js. + DictionaryValue* properties = NULL; + EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &properties)); + EXTENSION_FUNCTION_VALIDATE(properties->GetInteger(kGeneratedIdKey, + &id.uid)); + } + + std::string title; + if (params->create_properties.title.get()) + title = *params->create_properties.title; + + MenuManager* menu_manager = profile()->GetExtensionService()->menu_manager(); + + if (menu_manager->GetItemById(id)) { + error_ = ErrorUtils::FormatErrorMessage(kDuplicateIDError, + GetIDString(id)); + return false; + } + + if (GetExtension()->has_lazy_background_page() && + params->create_properties.onclick.get()) { + error_ = kOnclickDisallowedError; + return false; + } + + MenuItem::ContextList contexts; + if (params->create_properties.contexts.get()) + contexts = GetContexts(params->create_properties); + else + contexts.Add(MenuItem::PAGE); + + if (contexts.Contains(MenuItem::LAUNCHER) && + !GetExtension()->is_platform_app()) { + error_ = kLauncherNotAllowedError; + return false; + } + + MenuItem::Type type = GetType(params->create_properties); + + if (title.empty() && type != MenuItem::SEPARATOR) { + error_ = kTitleNeededError; + return false; + } + + bool checked = false; + if (params->create_properties.checked.get()) + checked = *params->create_properties.checked; + + bool enabled = true; + if (params->create_properties.enabled.get()) + enabled = *params->create_properties.enabled; + + scoped_ptr item( + new MenuItem(id, title, checked, enabled, type, contexts)); + + if (!item->PopulateURLPatterns( + params->create_properties.document_url_patterns.get(), + params->create_properties.target_url_patterns.get(), + &error_)) { + return false; + } + + bool success = true; + scoped_ptr parent_id(GetParentId(params->create_properties, + profile()->IsOffTheRecord(), + extension_id())); + if (parent_id.get()) { + MenuItem* parent = GetParent(*parent_id, menu_manager, &error_); + if (!parent) + return false; + success = menu_manager->AddChildItem(parent->id(), item.release()); + } else { + success = menu_manager->AddContextItem(GetExtension(), item.release()); + } + + if (!success) + return false; + + menu_manager->WriteToStorage(GetExtension()); + return true; +} + +bool UpdateContextMenuFunction::RunImpl() { + bool radio_item_updated = false; + MenuItem::Id item_id(profile()->IsOffTheRecord(), extension_id()); + scoped_ptr params(Update::Params::Create(*args_)); + + EXTENSION_FUNCTION_VALIDATE(params.get()); + switch (params->id_type) { + case Update::Params::ID_STRING: + item_id.string_uid = *params->id_string; + break; + case Update::Params::ID_INTEGER: + item_id.uid = *params->id_integer; + break; + case Update::Params::ID_NONE: + NOTREACHED(); + } + + ExtensionService* service = profile()->GetExtensionService(); + MenuManager* manager = service->menu_manager(); + MenuItem* item = manager->GetItemById(item_id); + if (!item || item->extension_id() != extension_id()) { + error_ = ErrorUtils::FormatErrorMessage( + kCannotFindItemError, GetIDString(item_id)); + return false; + } + + // Type. + MenuItem::Type type = GetType(params->update_properties); + + if (type != item->type()) { + if (type == MenuItem::RADIO || item->type() == MenuItem::RADIO) + radio_item_updated = true; + item->set_type(type); + } + + // Title. + if (params->update_properties.title.get()) { + std::string title(*params->update_properties.title); + if (title.empty() && item->type() != MenuItem::SEPARATOR) { + error_ = kTitleNeededError; + return false; + } + item->set_title(title); + } + + // Checked state. + if (params->update_properties.checked.get()) { + bool checked = *params->update_properties.checked; + if (checked && + item->type() != MenuItem::CHECKBOX && + item->type() != MenuItem::RADIO) { + error_ = kCheckedError; + return false; + } + if (checked != item->checked()) { + if (!item->SetChecked(checked)) { + error_ = kCheckedError; + return false; + } + radio_item_updated = true; + } + } + + // Enabled. + if (params->update_properties.enabled.get()) + item->set_enabled(*params->update_properties.enabled); + + // Contexts. + MenuItem::ContextList contexts; + if (params->update_properties.contexts.get()) { + contexts = GetContexts(params->update_properties); + + if (contexts.Contains(MenuItem::LAUNCHER) && + !GetExtension()->is_platform_app()) { + error_ = kLauncherNotAllowedError; + return false; + } + + if (contexts != item->contexts()) + item->set_contexts(contexts); + } + + // Parent id. + MenuItem* parent = NULL; + scoped_ptr parent_id(GetParentId(params->update_properties, + profile()->IsOffTheRecord(), + extension_id())); + if (parent_id.get()) { + MenuItem* parent = GetParent(*parent_id, manager, &error_); + if (!parent || !manager->ChangeParent(item->id(), &parent->id())) + return false; + } + + // URL Patterns. + if (!item->PopulateURLPatterns( + params->update_properties.document_url_patterns.get(), + params->update_properties.target_url_patterns.get(), &error_)) { + return false; + } + + // There is no need to call ItemUpdated if ChangeParent is called because + // all sanitation is taken care of in ChangeParent. + if (!parent && radio_item_updated && !manager->ItemUpdated(item->id())) + return false; + + manager->WriteToStorage(GetExtension()); + return true; +} + +bool RemoveContextMenuFunction::RunImpl() { + scoped_ptr params(Remove::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + ExtensionService* service = profile()->GetExtensionService(); + MenuManager* manager = service->menu_manager(); + + MenuItem::Id id(profile()->IsOffTheRecord(), extension_id()); + switch (params->menu_item_id_type) { + case Remove::Params::MENU_ITEM_ID_STRING: + id.string_uid = *params->menu_item_id_string; + break; + case Remove::Params::MENU_ITEM_ID_INTEGER: + id.uid = *params->menu_item_id_integer; + break; + case Remove::Params::MENU_ITEM_ID_NONE: + NOTREACHED(); + } + + MenuItem* item = manager->GetItemById(id); + // Ensure one extension can't remove another's menu items. + if (!item || item->extension_id() != extension_id()) { + error_ = ErrorUtils::FormatErrorMessage( + kCannotFindItemError, GetIDString(id)); + return false; + } + + if (!manager->RemoveContextMenuItem(id)) + return false; + manager->WriteToStorage(GetExtension()); + return true; +} + +bool RemoveAllContextMenusFunction::RunImpl() { + ExtensionService* service = profile()->GetExtensionService(); + MenuManager* manager = service->menu_manager(); + manager->RemoveAllContextItems(GetExtension()->id()); + manager->WriteToStorage(GetExtension()); + return true; +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/context_menu/context_menu_api.h b/chrome/browser/extensions/api/context_menu/context_menu_api.h new file mode 100644 index 0000000..5f15c92 --- /dev/null +++ b/chrome/browser/extensions/api/context_menu/context_menu_api.h @@ -0,0 +1,58 @@ +// 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_API_CONTEXT_MENU_CONTEXT_MENU_API_H__ +#define CHROME_BROWSER_EXTENSIONS_API_CONTEXT_MENU_CONTEXT_MENU_API_H__ + +#include "chrome/browser/extensions/extension_function.h" + +namespace extensions { + +class CreateContextMenuFunction : public SyncExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION_NAME("contextMenus.create") + + protected: + virtual ~CreateContextMenuFunction() {} + + // ExtensionFunction: + virtual bool RunImpl() OVERRIDE; +}; + +class UpdateContextMenuFunction : public SyncExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION_NAME("contextMenus.update") + + protected: + virtual ~UpdateContextMenuFunction() {} + + // ExtensionFunction: + virtual bool RunImpl() OVERRIDE; +}; + +class RemoveContextMenuFunction : public SyncExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION_NAME("contextMenus.remove") + + protected: + virtual ~RemoveContextMenuFunction() {} + + // ExtensionFunction: + virtual bool RunImpl() OVERRIDE; +}; + +class RemoveAllContextMenusFunction : public SyncExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION_NAME("contextMenus.removeAll") + + protected: + virtual ~RemoveAllContextMenusFunction() {} + + // ExtensionFunction: + virtual bool RunImpl() OVERRIDE; +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_CONTEXT_MENU_CONTEXT_MENU_API_H__ diff --git a/chrome/browser/extensions/api/context_menu/context_menu_apitest.cc b/chrome/browser/extensions/api/context_menu/context_menu_apitest.cc new file mode 100644 index 0000000..85d2441 --- /dev/null +++ b/chrome/browser/extensions/api/context_menu/context_menu_apitest.cc @@ -0,0 +1,53 @@ +// 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/extension_action.h" +#include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/test/base/ui_test_utils.h" + +namespace extensions { + +// Times out on win asan, http://crbug.com/166026 +#if defined(OS_WIN) && defined(ADDRESS_SANITIZER) +#define MAYBE_ContextMenus DISABLED_ContextMenus +#else +#define MAYBE_ContextMenus ContextMenus +#endif +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_ContextMenus) { + ASSERT_TRUE(RunExtensionTest("context_menus/basics")) << message_; + ASSERT_TRUE(RunExtensionTest("context_menus/no_perms")) << message_; + ASSERT_TRUE(RunExtensionTest("context_menus/item_ids")) << message_; + ASSERT_TRUE(RunExtensionTest("context_menus/event_page")) << message_; +} + +// crbug.com/51436 -- creating context menus from multiple script contexts +// should work. +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContextMenusFromMultipleContexts) { + ASSERT_TRUE(test_server()->Start()); + ASSERT_TRUE(RunExtensionTest("context_menus/add_from_multiple_contexts")) + << message_; + const extensions::Extension* extension = GetSingleLoadedExtension(); + ASSERT_TRUE(extension) << message_; + + { + // Tell the extension to update the page action state. + ResultCatcher catcher; + ui_test_utils::NavigateToURL(browser(), + extension->GetResourceURL("popup.html")); + ASSERT_TRUE(catcher.GetNextResult()); + } + + { + // Tell the extension to update the page action state again. + ResultCatcher catcher; + ui_test_utils::NavigateToURL(browser(), + extension->GetResourceURL("popup2.html")); + ASSERT_TRUE(catcher.GetNextResult()); + } +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/context_menus/context_menu_apitest.cc b/chrome/browser/extensions/api/context_menus/context_menu_apitest.cc deleted file mode 100644 index 85d2441..0000000 --- a/chrome/browser/extensions/api/context_menus/context_menu_apitest.cc +++ /dev/null @@ -1,53 +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/extension_action.h" -#include "chrome/browser/extensions/extension_apitest.h" -#include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/test/base/ui_test_utils.h" - -namespace extensions { - -// Times out on win asan, http://crbug.com/166026 -#if defined(OS_WIN) && defined(ADDRESS_SANITIZER) -#define MAYBE_ContextMenus DISABLED_ContextMenus -#else -#define MAYBE_ContextMenus ContextMenus -#endif -IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_ContextMenus) { - ASSERT_TRUE(RunExtensionTest("context_menus/basics")) << message_; - ASSERT_TRUE(RunExtensionTest("context_menus/no_perms")) << message_; - ASSERT_TRUE(RunExtensionTest("context_menus/item_ids")) << message_; - ASSERT_TRUE(RunExtensionTest("context_menus/event_page")) << message_; -} - -// crbug.com/51436 -- creating context menus from multiple script contexts -// should work. -IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContextMenusFromMultipleContexts) { - ASSERT_TRUE(test_server()->Start()); - ASSERT_TRUE(RunExtensionTest("context_menus/add_from_multiple_contexts")) - << message_; - const extensions::Extension* extension = GetSingleLoadedExtension(); - ASSERT_TRUE(extension) << message_; - - { - // Tell the extension to update the page action state. - ResultCatcher catcher; - ui_test_utils::NavigateToURL(browser(), - extension->GetResourceURL("popup.html")); - ASSERT_TRUE(catcher.GetNextResult()); - } - - { - // Tell the extension to update the page action state again. - ResultCatcher catcher; - ui_test_utils::NavigateToURL(browser(), - extension->GetResourceURL("popup2.html")); - ASSERT_TRUE(catcher.GetNextResult()); - } -} - -} // namespace extensions diff --git a/chrome/browser/extensions/api/context_menus/context_menus_api.cc b/chrome/browser/extensions/api/context_menus/context_menus_api.cc deleted file mode 100644 index 45f4264..0000000 --- a/chrome/browser/extensions/api/context_menus/context_menus_api.cc +++ /dev/null @@ -1,398 +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/api/context_menus/context_menus_api.h" - -#include - -#include "base/string_number_conversions.h" -#include "base/string_util.h" -#include "base/values.h" -#include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/extensions/menu_manager.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/common/extensions/api/context_menus.h" -#include "extensions/common/error_utils.h" -#include "extensions/common/url_pattern_set.h" - -using extensions::ErrorUtils; - -namespace { - -const char kGeneratedIdKey[] = "generatedId"; - -const char kCannotFindItemError[] = "Cannot find menu item with id *"; -const char kOnclickDisallowedError[] = "Extensions using event pages cannot " - "pass an onclick parameter to chrome.contextMenus.create. Instead, use " - "the chrome.contextMenus.onClicked event."; -const char kCheckedError[] = - "Only items with type \"radio\" or \"checkbox\" can be checked"; -const char kDuplicateIDError[] = - "Cannot create item with duplicate id *"; -const char kIdRequiredError[] = "Extensions using event pages must pass an " - "id parameter to chrome.contextMenus.create"; -const char kParentsMustBeNormalError[] = - "Parent items must have type \"normal\""; -const char kTitleNeededError[] = - "All menu items except for separators must have a title"; -const char kLauncherNotAllowedError[] = - "Only packaged apps are allowed to use 'launcher' context"; - -std::string GetIDString(const extensions::MenuItem::Id& id) { - if (id.uid == 0) - return id.string_uid; - else - return base::IntToString(id.uid); -} - -template -extensions::MenuItem::ContextList GetContexts( - const PropertyWithEnumT& property) { - extensions::MenuItem::ContextList contexts; - for (size_t i = 0; i < property.contexts->size(); ++i) { - switch (property.contexts->at(i)) { - case PropertyWithEnumT::CONTEXTS_ELEMENT_ALL: - contexts.Add(extensions::MenuItem::ALL); - break; - case PropertyWithEnumT::CONTEXTS_ELEMENT_PAGE: - contexts.Add(extensions::MenuItem::PAGE); - break; - case PropertyWithEnumT::CONTEXTS_ELEMENT_SELECTION: - contexts.Add(extensions::MenuItem::SELECTION); - break; - case PropertyWithEnumT::CONTEXTS_ELEMENT_LINK: - contexts.Add(extensions::MenuItem::LINK); - break; - case PropertyWithEnumT::CONTEXTS_ELEMENT_EDITABLE: - contexts.Add(extensions::MenuItem::EDITABLE); - break; - case PropertyWithEnumT::CONTEXTS_ELEMENT_IMAGE: - contexts.Add(extensions::MenuItem::IMAGE); - break; - case PropertyWithEnumT::CONTEXTS_ELEMENT_VIDEO: - contexts.Add(extensions::MenuItem::VIDEO); - break; - case PropertyWithEnumT::CONTEXTS_ELEMENT_AUDIO: - contexts.Add(extensions::MenuItem::AUDIO); - break; - case PropertyWithEnumT::CONTEXTS_ELEMENT_FRAME: - contexts.Add(extensions::MenuItem::FRAME); - break; - case PropertyWithEnumT::CONTEXTS_ELEMENT_LAUNCHER: - contexts.Add(extensions::MenuItem::LAUNCHER); - break; - case PropertyWithEnumT::CONTEXTS_ELEMENT_NONE: - NOTREACHED(); - } - } - return contexts; -} - -template -extensions::MenuItem::Type GetType(const PropertyWithEnumT& property) { - switch (property.type) { - case PropertyWithEnumT::TYPE_NONE: - case PropertyWithEnumT::TYPE_NORMAL: - return extensions::MenuItem::NORMAL; - case PropertyWithEnumT::TYPE_CHECKBOX: - return extensions::MenuItem::CHECKBOX; - case PropertyWithEnumT::TYPE_RADIO: - return extensions::MenuItem::RADIO; - case PropertyWithEnumT::TYPE_SEPARATOR: - return extensions::MenuItem::SEPARATOR; - } - return extensions::MenuItem::NORMAL; -} - -template -scoped_ptr GetParentId( - const PropertyWithEnumT& property, - bool is_off_the_record, - std::string extension_id) { - scoped_ptr parent_id( - new extensions::MenuItem::Id(is_off_the_record, extension_id)); - switch (property.parent_id_type) { - case PropertyWithEnumT::PARENT_ID_NONE: - return scoped_ptr().Pass(); - case PropertyWithEnumT::PARENT_ID_INTEGER: - parent_id->uid = *property.parent_id_integer; - break; - case PropertyWithEnumT::PARENT_ID_STRING: - parent_id->string_uid = *property.parent_id_string; - break; - } - return parent_id.Pass(); -} - -extensions::MenuItem* GetParent(extensions::MenuItem::Id parent_id, - const extensions::MenuManager* menu_manager, - std::string* error) { - extensions::MenuItem* parent = menu_manager->GetItemById(parent_id); - if (!parent) { - *error = ErrorUtils::FormatErrorMessage( - kCannotFindItemError, GetIDString(parent_id)); - return NULL; - } - if (parent->type() != extensions::MenuItem::NORMAL) { - *error = kParentsMustBeNormalError; - return NULL; - } - - return parent; -} - -} // namespace - -namespace extensions { - -namespace Create = api::context_menus::Create; -namespace Remove = api::context_menus::Remove; -namespace Update = api::context_menus::Update; - -bool ContextMenusCreateFunction::RunImpl() { - MenuItem::Id id(profile()->IsOffTheRecord(), extension_id()); - scoped_ptr params(Create::Params::Create(*args_)); - EXTENSION_FUNCTION_VALIDATE(params.get()); - - if (params->create_properties.id.get()) { - id.string_uid = *params->create_properties.id; - } else { - if (GetExtension()->has_lazy_background_page()) { - error_ = kIdRequiredError; - return false; - } - - // The Generated Id is added by context_menus_custom_bindings.js. - DictionaryValue* properties = NULL; - EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &properties)); - EXTENSION_FUNCTION_VALIDATE(properties->GetInteger(kGeneratedIdKey, - &id.uid)); - } - - std::string title; - if (params->create_properties.title.get()) - title = *params->create_properties.title; - - MenuManager* menu_manager = profile()->GetExtensionService()->menu_manager(); - - if (menu_manager->GetItemById(id)) { - error_ = ErrorUtils::FormatErrorMessage(kDuplicateIDError, - GetIDString(id)); - return false; - } - - if (GetExtension()->has_lazy_background_page() && - params->create_properties.onclick.get()) { - error_ = kOnclickDisallowedError; - return false; - } - - MenuItem::ContextList contexts; - if (params->create_properties.contexts.get()) - contexts = GetContexts(params->create_properties); - else - contexts.Add(MenuItem::PAGE); - - if (contexts.Contains(MenuItem::LAUNCHER) && - !GetExtension()->is_platform_app()) { - error_ = kLauncherNotAllowedError; - return false; - } - - MenuItem::Type type = GetType(params->create_properties); - - if (title.empty() && type != MenuItem::SEPARATOR) { - error_ = kTitleNeededError; - return false; - } - - bool checked = false; - if (params->create_properties.checked.get()) - checked = *params->create_properties.checked; - - bool enabled = true; - if (params->create_properties.enabled.get()) - enabled = *params->create_properties.enabled; - - scoped_ptr item( - new MenuItem(id, title, checked, enabled, type, contexts)); - - if (!item->PopulateURLPatterns( - params->create_properties.document_url_patterns.get(), - params->create_properties.target_url_patterns.get(), - &error_)) { - return false; - } - - bool success = true; - scoped_ptr parent_id(GetParentId(params->create_properties, - profile()->IsOffTheRecord(), - extension_id())); - if (parent_id.get()) { - MenuItem* parent = GetParent(*parent_id, menu_manager, &error_); - if (!parent) - return false; - success = menu_manager->AddChildItem(parent->id(), item.release()); - } else { - success = menu_manager->AddContextItem(GetExtension(), item.release()); - } - - if (!success) - return false; - - menu_manager->WriteToStorage(GetExtension()); - return true; -} - -bool ContextMenusUpdateFunction::RunImpl() { - bool radio_item_updated = false; - MenuItem::Id item_id(profile()->IsOffTheRecord(), extension_id()); - scoped_ptr params(Update::Params::Create(*args_)); - - EXTENSION_FUNCTION_VALIDATE(params.get()); - switch (params->id_type) { - case Update::Params::ID_STRING: - item_id.string_uid = *params->id_string; - break; - case Update::Params::ID_INTEGER: - item_id.uid = *params->id_integer; - break; - case Update::Params::ID_NONE: - NOTREACHED(); - } - - ExtensionService* service = profile()->GetExtensionService(); - MenuManager* manager = service->menu_manager(); - MenuItem* item = manager->GetItemById(item_id); - if (!item || item->extension_id() != extension_id()) { - error_ = ErrorUtils::FormatErrorMessage( - kCannotFindItemError, GetIDString(item_id)); - return false; - } - - // Type. - MenuItem::Type type = GetType(params->update_properties); - - if (type != item->type()) { - if (type == MenuItem::RADIO || item->type() == MenuItem::RADIO) - radio_item_updated = true; - item->set_type(type); - } - - // Title. - if (params->update_properties.title.get()) { - std::string title(*params->update_properties.title); - if (title.empty() && item->type() != MenuItem::SEPARATOR) { - error_ = kTitleNeededError; - return false; - } - item->set_title(title); - } - - // Checked state. - if (params->update_properties.checked.get()) { - bool checked = *params->update_properties.checked; - if (checked && - item->type() != MenuItem::CHECKBOX && - item->type() != MenuItem::RADIO) { - error_ = kCheckedError; - return false; - } - if (checked != item->checked()) { - if (!item->SetChecked(checked)) { - error_ = kCheckedError; - return false; - } - radio_item_updated = true; - } - } - - // Enabled. - if (params->update_properties.enabled.get()) - item->set_enabled(*params->update_properties.enabled); - - // Contexts. - MenuItem::ContextList contexts; - if (params->update_properties.contexts.get()) { - contexts = GetContexts(params->update_properties); - - if (contexts.Contains(MenuItem::LAUNCHER) && - !GetExtension()->is_platform_app()) { - error_ = kLauncherNotAllowedError; - return false; - } - - if (contexts != item->contexts()) - item->set_contexts(contexts); - } - - // Parent id. - MenuItem* parent = NULL; - scoped_ptr parent_id(GetParentId(params->update_properties, - profile()->IsOffTheRecord(), - extension_id())); - if (parent_id.get()) { - MenuItem* parent = GetParent(*parent_id, manager, &error_); - if (!parent || !manager->ChangeParent(item->id(), &parent->id())) - return false; - } - - // URL Patterns. - if (!item->PopulateURLPatterns( - params->update_properties.document_url_patterns.get(), - params->update_properties.target_url_patterns.get(), &error_)) { - return false; - } - - // There is no need to call ItemUpdated if ChangeParent is called because - // all sanitation is taken care of in ChangeParent. - if (!parent && radio_item_updated && !manager->ItemUpdated(item->id())) - return false; - - manager->WriteToStorage(GetExtension()); - return true; -} - -bool ContextMenusRemoveFunction::RunImpl() { - scoped_ptr params(Remove::Params::Create(*args_)); - EXTENSION_FUNCTION_VALIDATE(params.get()); - - ExtensionService* service = profile()->GetExtensionService(); - MenuManager* manager = service->menu_manager(); - - MenuItem::Id id(profile()->IsOffTheRecord(), extension_id()); - switch (params->menu_item_id_type) { - case Remove::Params::MENU_ITEM_ID_STRING: - id.string_uid = *params->menu_item_id_string; - break; - case Remove::Params::MENU_ITEM_ID_INTEGER: - id.uid = *params->menu_item_id_integer; - break; - case Remove::Params::MENU_ITEM_ID_NONE: - NOTREACHED(); - } - - MenuItem* item = manager->GetItemById(id); - // Ensure one extension can't remove another's menu items. - if (!item || item->extension_id() != extension_id()) { - error_ = ErrorUtils::FormatErrorMessage( - kCannotFindItemError, GetIDString(id)); - return false; - } - - if (!manager->RemoveContextMenuItem(id)) - return false; - manager->WriteToStorage(GetExtension()); - return true; -} - -bool ContextMenusRemoveAllFunction::RunImpl() { - ExtensionService* service = profile()->GetExtensionService(); - MenuManager* manager = service->menu_manager(); - manager->RemoveAllContextItems(GetExtension()->id()); - manager->WriteToStorage(GetExtension()); - return true; -} - -} // namespace extensions diff --git a/chrome/browser/extensions/api/context_menus/context_menus_api.h b/chrome/browser/extensions/api/context_menus/context_menus_api.h deleted file mode 100644 index d6b1217..0000000 --- a/chrome/browser/extensions/api/context_menus/context_menus_api.h +++ /dev/null @@ -1,58 +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_API_CONTEXT_MENUS_CONTEXT_MENUS_API_H_ -#define CHROME_BROWSER_EXTENSIONS_API_CONTEXT_MENUS_CONTEXT_MENUS_API_H_ - -#include "chrome/browser/extensions/extension_function.h" - -namespace extensions { - -class ContextMenusCreateFunction : public SyncExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION_NAME("contextMenus.create") - - protected: - virtual ~ContextMenusCreateFunction() {} - - // ExtensionFunction: - virtual bool RunImpl() OVERRIDE; -}; - -class ContextMenusUpdateFunction : public SyncExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION_NAME("contextMenus.update") - - protected: - virtual ~ContextMenusUpdateFunction() {} - - // ExtensionFunction: - virtual bool RunImpl() OVERRIDE; -}; - -class ContextMenusRemoveFunction : public SyncExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION_NAME("contextMenus.remove") - - protected: - virtual ~ContextMenusRemoveFunction() {} - - // ExtensionFunction: - virtual bool RunImpl() OVERRIDE; -}; - -class ContextMenusRemoveAllFunction : public SyncExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION_NAME("contextMenus.removeAll") - - protected: - virtual ~ContextMenusRemoveAllFunction() {} - - // ExtensionFunction: - virtual bool RunImpl() OVERRIDE; -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_CONTEXT_MENUS_CONTEXT_MENUS_API_H_ diff --git a/chrome/browser/extensions/api/cookies/cookies_api.cc b/chrome/browser/extensions/api/cookies/cookies_api.cc index 26d401e..1a34a18 100644 --- a/chrome/browser/extensions/api/cookies/cookies_api.cc +++ b/chrome/browser/extensions/api/cookies/cookies_api.cc @@ -189,13 +189,13 @@ bool CookiesFunction::ParseStoreContext( return true; } -CookiesGetFunction::CookiesGetFunction() { +GetCookieFunction::GetCookieFunction() { } -CookiesGetFunction::~CookiesGetFunction() { +GetCookieFunction::~GetCookieFunction() { } -bool CookiesGetFunction::RunImpl() { +bool GetCookieFunction::RunImpl() { parsed_args_ = Get::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(parsed_args_.get()); @@ -216,23 +216,23 @@ bool CookiesGetFunction::RunImpl() { bool rv = BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - base::Bind(&CookiesGetFunction::GetCookieOnIOThread, this)); + base::Bind(&GetCookieFunction::GetCookieOnIOThread, this)); DCHECK(rv); // Will finish asynchronously. return true; } -void CookiesGetFunction::GetCookieOnIOThread() { +void GetCookieFunction::GetCookieOnIOThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); net::CookieStore* cookie_store = store_context_->GetURLRequestContext()->cookie_store(); cookies_helpers::GetCookieListFromStore( cookie_store, url_, - base::Bind(&CookiesGetFunction::GetCookieCallback, this)); + base::Bind(&GetCookieFunction::GetCookieCallback, this)); } -void CookiesGetFunction::GetCookieCallback(const net::CookieList& cookie_list) { +void GetCookieFunction::GetCookieCallback(const net::CookieList& cookie_list) { net::CookieList::const_iterator it; for (it = cookie_list.begin(); it != cookie_list.end(); ++it) { // Return the first matching cookie. Relies on the fact that the @@ -252,22 +252,22 @@ void CookiesGetFunction::GetCookieCallback(const net::CookieList& cookie_list) { bool rv = BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - base::Bind(&CookiesGetFunction::RespondOnUIThread, this)); + base::Bind(&GetCookieFunction::RespondOnUIThread, this)); DCHECK(rv); } -void CookiesGetFunction::RespondOnUIThread() { +void GetCookieFunction::RespondOnUIThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); SendResponse(true); } -CookiesGetAllFunction::CookiesGetAllFunction() { +GetAllCookiesFunction::GetAllCookiesFunction() { } -CookiesGetAllFunction::~CookiesGetAllFunction() { +GetAllCookiesFunction::~GetAllCookiesFunction() { } -bool CookiesGetAllFunction::RunImpl() { +bool GetAllCookiesFunction::RunImpl() { parsed_args_ = GetAll::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(parsed_args_.get()); @@ -287,23 +287,23 @@ bool CookiesGetAllFunction::RunImpl() { bool rv = BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - base::Bind(&CookiesGetAllFunction::GetAllCookiesOnIOThread, this)); + base::Bind(&GetAllCookiesFunction::GetAllCookiesOnIOThread, this)); DCHECK(rv); // Will finish asynchronously. return true; } -void CookiesGetAllFunction::GetAllCookiesOnIOThread() { +void GetAllCookiesFunction::GetAllCookiesOnIOThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); net::CookieStore* cookie_store = store_context_->GetURLRequestContext()->cookie_store(); cookies_helpers::GetCookieListFromStore( cookie_store, url_, - base::Bind(&CookiesGetAllFunction::GetAllCookiesCallback, this)); + base::Bind(&GetAllCookiesFunction::GetAllCookiesCallback, this)); } -void CookiesGetAllFunction::GetAllCookiesCallback( +void GetAllCookiesFunction::GetAllCookiesCallback( const net::CookieList& cookie_list) { const extensions::Extension* extension = GetExtension(); if (extension) { @@ -316,22 +316,22 @@ void CookiesGetAllFunction::GetAllCookiesCallback( } bool rv = BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - base::Bind(&CookiesGetAllFunction::RespondOnUIThread, this)); + base::Bind(&GetAllCookiesFunction::RespondOnUIThread, this)); DCHECK(rv); } -void CookiesGetAllFunction::RespondOnUIThread() { +void GetAllCookiesFunction::RespondOnUIThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); SendResponse(true); } -CookiesSetFunction::CookiesSetFunction() : success_(false) { +SetCookieFunction::SetCookieFunction() : success_(false) { } -CookiesSetFunction::~CookiesSetFunction() { +SetCookieFunction::~SetCookieFunction() { } -bool CookiesSetFunction::RunImpl() { +bool SetCookieFunction::RunImpl() { parsed_args_ = Set::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(parsed_args_.get()); @@ -350,14 +350,14 @@ bool CookiesSetFunction::RunImpl() { bool rv = BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - base::Bind(&CookiesSetFunction::SetCookieOnIOThread, this)); + base::Bind(&SetCookieFunction::SetCookieOnIOThread, this)); DCHECK(rv); // Will finish asynchronously. return true; } -void CookiesSetFunction::SetCookieOnIOThread() { +void SetCookieFunction::SetCookieOnIOThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); net::CookieMonster* cookie_monster = store_context_->GetURLRequestContext()->cookie_store()-> @@ -385,10 +385,10 @@ void CookiesSetFunction::SetCookieOnIOThread() { parsed_args_->details.http_only.get() ? *parsed_args_->details.http_only : false, - base::Bind(&CookiesSetFunction::PullCookie, this)); + base::Bind(&SetCookieFunction::PullCookie, this)); } -void CookiesSetFunction::PullCookie(bool set_cookie_result) { +void SetCookieFunction::PullCookie(bool set_cookie_result) { // Pull the newly set cookie. net::CookieMonster* cookie_monster = store_context_->GetURLRequestContext()->cookie_store()-> @@ -396,11 +396,10 @@ void CookiesSetFunction::PullCookie(bool set_cookie_result) { success_ = set_cookie_result; cookies_helpers::GetCookieListFromStore( cookie_monster, url_, - base::Bind(&CookiesSetFunction::PullCookieCallback, this)); + base::Bind(&SetCookieFunction::PullCookieCallback, this)); } -void CookiesSetFunction::PullCookieCallback( - const net::CookieList& cookie_list) { +void SetCookieFunction::PullCookieCallback(const net::CookieList& cookie_list) { net::CookieList::const_iterator it; for (it = cookie_list.begin(); it != cookie_list.end(); ++it) { // Return the first matching cookie. Relies on the fact that the @@ -418,11 +417,11 @@ void CookiesSetFunction::PullCookieCallback( bool rv = BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - base::Bind(&CookiesSetFunction::RespondOnUIThread, this)); + base::Bind(&SetCookieFunction::RespondOnUIThread, this)); DCHECK(rv); } -void CookiesSetFunction::RespondOnUIThread() { +void SetCookieFunction::RespondOnUIThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (!success_) { std::string name = parsed_args_->details.name.get() ? @@ -433,13 +432,13 @@ void CookiesSetFunction::RespondOnUIThread() { SendResponse(success_); } -CookiesRemoveFunction::CookiesRemoveFunction() { +RemoveCookieFunction::RemoveCookieFunction() { } -CookiesRemoveFunction::~CookiesRemoveFunction() { +RemoveCookieFunction::~RemoveCookieFunction() { } -bool CookiesRemoveFunction::RunImpl() { +bool RemoveCookieFunction::RunImpl() { parsed_args_ = Remove::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(parsed_args_.get()); @@ -459,14 +458,14 @@ bool CookiesRemoveFunction::RunImpl() { // Pass the work off to the IO thread. bool rv = BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - base::Bind(&CookiesRemoveFunction::RemoveCookieOnIOThread, this)); + base::Bind(&RemoveCookieFunction::RemoveCookieOnIOThread, this)); DCHECK(rv); // Will return asynchronously. return true; } -void CookiesRemoveFunction::RemoveCookieOnIOThread() { +void RemoveCookieFunction::RemoveCookieOnIOThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); // Remove the cookie @@ -474,10 +473,10 @@ void CookiesRemoveFunction::RemoveCookieOnIOThread() { store_context_->GetURLRequestContext()->cookie_store(); cookie_store->DeleteCookieAsync( url_, parsed_args_->details.name, - base::Bind(&CookiesRemoveFunction::RemoveCookieCallback, this)); + base::Bind(&RemoveCookieFunction::RemoveCookieCallback, this)); } -void CookiesRemoveFunction::RemoveCookieCallback() { +void RemoveCookieFunction::RemoveCookieCallback() { // Build the callback result Remove::Results::Details details; details.name = parsed_args_->details.name; @@ -488,16 +487,16 @@ void CookiesRemoveFunction::RemoveCookieCallback() { // Return to UI thread bool rv = BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - base::Bind(&CookiesRemoveFunction::RespondOnUIThread, this)); + base::Bind(&RemoveCookieFunction::RespondOnUIThread, this)); DCHECK(rv); } -void CookiesRemoveFunction::RespondOnUIThread() { +void RemoveCookieFunction::RespondOnUIThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); SendResponse(true); } -bool CookiesGetAllCookieStoresFunction::RunImpl() { +bool GetAllCookieStoresFunction::RunImpl() { Profile* original_profile = profile(); DCHECK(original_profile); scoped_ptr original_tab_ids(new ListValue()); @@ -540,7 +539,7 @@ bool CookiesGetAllCookieStoresFunction::RunImpl() { return true; } -void CookiesGetAllCookieStoresFunction::Run() { +void GetAllCookieStoresFunction::Run() { SendResponse(RunImpl()); } diff --git a/chrome/browser/extensions/api/cookies/cookies_api.h b/chrome/browser/extensions/api/cookies/cookies_api.h index 7c1b377..5eddcd2 100644 --- a/chrome/browser/extensions/api/cookies/cookies_api.h +++ b/chrome/browser/extensions/api/cookies/cookies_api.h @@ -87,14 +87,14 @@ class CookiesFunction : public AsyncExtensionFunction { }; // Implements the cookies.get() extension function. -class CookiesGetFunction : public CookiesFunction { +class GetCookieFunction : public CookiesFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("cookies.get") - CookiesGetFunction(); + GetCookieFunction(); protected: - virtual ~CookiesGetFunction(); + virtual ~GetCookieFunction(); // ExtensionFunction: virtual bool RunImpl() OVERRIDE; @@ -110,14 +110,14 @@ class CookiesGetFunction : public CookiesFunction { }; // Implements the cookies.getAll() extension function. -class CookiesGetAllFunction : public CookiesFunction { +class GetAllCookiesFunction : public CookiesFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("cookies.getAll") - CookiesGetAllFunction(); + GetAllCookiesFunction(); protected: - virtual ~CookiesGetAllFunction(); + virtual ~GetAllCookiesFunction(); // ExtensionFunction: virtual bool RunImpl() OVERRIDE; @@ -133,14 +133,14 @@ class CookiesGetAllFunction : public CookiesFunction { }; // Implements the cookies.set() extension function. -class CookiesSetFunction : public CookiesFunction { +class SetCookieFunction : public CookiesFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("cookies.set") - CookiesSetFunction(); + SetCookieFunction(); protected: - virtual ~CookiesSetFunction(); + virtual ~SetCookieFunction(); virtual bool RunImpl() OVERRIDE; private: @@ -156,14 +156,14 @@ class CookiesSetFunction : public CookiesFunction { }; // Implements the cookies.remove() extension function. -class CookiesRemoveFunction : public CookiesFunction { +class RemoveCookieFunction : public CookiesFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("cookies.remove") - CookiesRemoveFunction(); + RemoveCookieFunction(); protected: - virtual ~CookiesRemoveFunction(); + virtual ~RemoveCookieFunction(); // ExtensionFunction: virtual bool RunImpl() OVERRIDE; @@ -179,15 +179,15 @@ class CookiesRemoveFunction : public CookiesFunction { }; // Implements the cookies.getAllCookieStores() extension function. -class CookiesGetAllCookieStoresFunction : public CookiesFunction { +class GetAllCookieStoresFunction : public CookiesFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("cookies.getAllCookieStores") protected: - virtual ~CookiesGetAllCookieStoresFunction() {} + virtual ~GetAllCookieStoresFunction() {} // ExtensionFunction: - // CookiesGetAllCookieStoresFunction is sync. + // GetAllCookieStoresFunction is sync. virtual void Run() OVERRIDE; virtual bool RunImpl() OVERRIDE; }; diff --git a/chrome/browser/extensions/api/debugger/debugger_api.cc b/chrome/browser/extensions/api/debugger/debugger_api.cc index d672af7..79b28b7 100644 --- a/chrome/browser/extensions/api/debugger/debugger_api.cc +++ b/chrome/browser/extensions/api/debugger/debugger_api.cc @@ -104,7 +104,7 @@ class ExtensionDevToolsClientHost : public DevToolsClientHost, bool MatchesContentsAndExtensionId(WebContents* web_contents, const std::string& extension_id); void Close(); - void SendMessageToBackend(DebuggerSendCommandFunction* function, + void SendMessageToBackend(SendCommandDebuggerFunction* function, const std::string& method, SendCommand::Params::CommandParams* command_params); @@ -129,7 +129,7 @@ class ExtensionDevToolsClientHost : public DevToolsClientHost, int tab_id_; content::NotificationRegistrar registrar_; int last_request_id_; - typedef std::map > + typedef std::map > PendingRequests; PendingRequests pending_requests_; ExtensionDevToolsInfoBarDelegate* infobar_delegate_; @@ -249,7 +249,7 @@ void ExtensionDevToolsClientHost::Close() { } void ExtensionDevToolsClientHost::SendMessageToBackend( - DebuggerSendCommandFunction* function, + SendCommandDebuggerFunction* function, const std::string& method, SendCommand::Params::CommandParams* command_params) { DictionaryValue protocol_request; @@ -343,7 +343,7 @@ void ExtensionDevToolsClientHost::DispatchOnInspectorFrontend( extensions::ExtensionSystem::Get(profile)->event_router()-> DispatchEventToExtension(extension_id_, event.Pass()); } else { - DebuggerSendCommandFunction* function = pending_requests_[id]; + SendCommandDebuggerFunction* function = pending_requests_[id]; if (!function) return; @@ -457,11 +457,11 @@ bool DebuggerFunction::InitClientHost() { return true; } -DebuggerAttachFunction::DebuggerAttachFunction() {} +AttachDebuggerFunction::AttachDebuggerFunction() {} -DebuggerAttachFunction::~DebuggerAttachFunction() {} +AttachDebuggerFunction::~AttachDebuggerFunction() {} -bool DebuggerAttachFunction::RunImpl() { +bool AttachDebuggerFunction::RunImpl() { scoped_ptr params(Attach::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); @@ -497,11 +497,11 @@ bool DebuggerAttachFunction::RunImpl() { return true; } -DebuggerDetachFunction::DebuggerDetachFunction() {} +DetachDebuggerFunction::DetachDebuggerFunction() {} -DebuggerDetachFunction::~DebuggerDetachFunction() {} +DetachDebuggerFunction::~DetachDebuggerFunction() {} -bool DebuggerDetachFunction::RunImpl() { +bool DetachDebuggerFunction::RunImpl() { scoped_ptr params(Detach::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); @@ -514,11 +514,11 @@ bool DebuggerDetachFunction::RunImpl() { return true; } -DebuggerSendCommandFunction::DebuggerSendCommandFunction() {} +SendCommandDebuggerFunction::SendCommandDebuggerFunction() {} -DebuggerSendCommandFunction::~DebuggerSendCommandFunction() {} +SendCommandDebuggerFunction::~SendCommandDebuggerFunction() {} -bool DebuggerSendCommandFunction::RunImpl() { +bool SendCommandDebuggerFunction::RunImpl() { scoped_ptr params(SendCommand::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); @@ -531,7 +531,7 @@ bool DebuggerSendCommandFunction::RunImpl() { return true; } -void DebuggerSendCommandFunction::SendResponseBody( +void SendCommandDebuggerFunction::SendResponseBody( DictionaryValue* response) { Value* error_body; if (response->Get("error", &error_body)) { diff --git a/chrome/browser/extensions/api/debugger/debugger_api.h b/chrome/browser/extensions/api/debugger/debugger_api.h index 44243d9..36b0cb5 100644 --- a/chrome/browser/extensions/api/debugger/debugger_api.h +++ b/chrome/browser/extensions/api/debugger/debugger_api.h @@ -39,43 +39,43 @@ class DebuggerFunction : public AsyncExtensionFunction { }; // Implements the debugger.attach() extension function. -class DebuggerAttachFunction : public DebuggerFunction { +class AttachDebuggerFunction : public DebuggerFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("debugger.attach") - DebuggerAttachFunction(); + AttachDebuggerFunction(); protected: - virtual ~DebuggerAttachFunction(); + virtual ~AttachDebuggerFunction(); // ExtensionFunction: virtual bool RunImpl() OVERRIDE; }; // Implements the debugger.detach() extension function. -class DebuggerDetachFunction : public DebuggerFunction { +class DetachDebuggerFunction : public DebuggerFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("debugger.detach") - DebuggerDetachFunction(); + DetachDebuggerFunction(); protected: - virtual ~DebuggerDetachFunction(); + virtual ~DetachDebuggerFunction(); // ExtensionFunction: virtual bool RunImpl() OVERRIDE; }; // Implements the debugger.sendCommand() extension function. -class DebuggerSendCommandFunction : public DebuggerFunction { +class SendCommandDebuggerFunction : public DebuggerFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("debugger.sendCommand") - DebuggerSendCommandFunction(); + SendCommandDebuggerFunction(); void SendResponseBody(base::DictionaryValue* result); protected: - virtual ~DebuggerSendCommandFunction(); + virtual ~SendCommandDebuggerFunction(); // ExtensionFunction: virtual bool RunImpl() OVERRIDE; diff --git a/chrome/browser/extensions/api/declarative/declarative_api.cc b/chrome/browser/extensions/api/declarative/declarative_api.cc index 8c57352..57cd0e8 100644 --- a/chrome/browser/extensions/api/declarative/declarative_api.cc +++ b/chrome/browser/extensions/api/declarative/declarative_api.cc @@ -61,7 +61,7 @@ bool RulesFunction::RunImpl() { return true; } -bool EventsAddRulesFunction::RunImplOnCorrectThread() { +bool AddRulesFunction::RunImplOnCorrectThread() { scoped_ptr params(AddRules::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); @@ -73,7 +73,7 @@ bool EventsAddRulesFunction::RunImplOnCorrectThread() { return error_.empty(); } -bool EventsRemoveRulesFunction::RunImplOnCorrectThread() { +bool RemoveRulesFunction::RunImplOnCorrectThread() { scoped_ptr params(RemoveRules::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); @@ -87,7 +87,7 @@ bool EventsRemoveRulesFunction::RunImplOnCorrectThread() { return error_.empty(); } -bool EventsGetRulesFunction::RunImplOnCorrectThread() { +bool GetRulesFunction::RunImplOnCorrectThread() { scoped_ptr params(GetRules::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); diff --git a/chrome/browser/extensions/api/declarative/declarative_api.h b/chrome/browser/extensions/api/declarative/declarative_api.h index 5caf4ac..5f7acc0 100644 --- a/chrome/browser/extensions/api/declarative/declarative_api.h +++ b/chrome/browser/extensions/api/declarative/declarative_api.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_EXTENSIONS_API_DECLARATIVE_DECLARATIVE_API_H_ -#define CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_DECLARATIVE_API_H_ +#ifndef CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_DECLARATIVE_API_H__ +#define CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_DECLARATIVE_API_H__ #include "base/compiler_specific.h" #include "base/memory/ref_counted.h" @@ -31,34 +31,34 @@ class RulesFunction : public AsyncExtensionFunction { scoped_refptr rules_registry_; }; -class EventsAddRulesFunction : public RulesFunction { +class AddRulesFunction : public RulesFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("events.addRules"); protected: - virtual ~EventsAddRulesFunction() {} + virtual ~AddRulesFunction() {} // RulesFunction: virtual bool RunImplOnCorrectThread() OVERRIDE; }; -class EventsRemoveRulesFunction : public RulesFunction { +class RemoveRulesFunction : public RulesFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("events.removeRules"); protected: - virtual ~EventsRemoveRulesFunction() {} + virtual ~RemoveRulesFunction() {} // RulesFunction: virtual bool RunImplOnCorrectThread() OVERRIDE; }; -class EventsGetRulesFunction : public RulesFunction { +class GetRulesFunction : public RulesFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("events.getRules"); protected: - virtual ~EventsGetRulesFunction() {} + virtual ~GetRulesFunction() {} // RulesFunction: virtual bool RunImplOnCorrectThread() OVERRIDE; @@ -66,4 +66,4 @@ class EventsGetRulesFunction : public RulesFunction { } // namespace extensions -#endif // CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_DECLARATIVE_API_H_ +#endif // CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_DECLARATIVE_API_H__ diff --git a/chrome/browser/extensions/api/font_settings/font_settings_api.cc b/chrome/browser/extensions/api/font_settings/font_settings_api.cc index 7aeec6f..802c625 100644 --- a/chrome/browser/extensions/api/font_settings/font_settings_api.cc +++ b/chrome/browser/extensions/api/font_settings/font_settings_api.cc @@ -217,7 +217,7 @@ FontSettingsAPI::FontSettingsAPI(Profile* profile) FontSettingsAPI::~FontSettingsAPI() { } -bool FontSettingsClearFontFunction::RunImpl() { +bool ClearFontFunction::RunImpl() { if (profile_->IsOffTheRecord()) { error_ = kSetFromIncognitoError; return false; @@ -242,7 +242,7 @@ bool FontSettingsClearFontFunction::RunImpl() { return true; } -bool FontSettingsGetFontFunction::RunImpl() { +bool GetFontFunction::RunImpl() { scoped_ptr params( fonts::GetFont::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); @@ -275,7 +275,7 @@ bool FontSettingsGetFontFunction::RunImpl() { return true; } -bool FontSettingsSetFontFunction::RunImpl() { +bool SetFontFunction::RunImpl() { if (profile_->IsOffTheRecord()) { error_ = kSetFromIncognitoError; return false; @@ -302,19 +302,18 @@ bool FontSettingsSetFontFunction::RunImpl() { return true; } -bool FontSettingsGetFontListFunction::RunImpl() { +bool GetFontListFunction::RunImpl() { content::GetFontListAsync( - Bind(&FontSettingsGetFontListFunction::FontListHasLoaded, this)); + Bind(&GetFontListFunction::FontListHasLoaded, this)); return true; } -void FontSettingsGetFontListFunction::FontListHasLoaded( - scoped_ptr list) { +void GetFontListFunction::FontListHasLoaded(scoped_ptr list) { bool success = CopyFontsToResult(list.get()); SendResponse(success); } -bool FontSettingsGetFontListFunction::CopyFontsToResult(ListValue* fonts) { +bool GetFontListFunction::CopyFontsToResult(ListValue* fonts) { scoped_ptr result(new ListValue()); for (ListValue::iterator it = fonts->begin(); it != fonts->end(); ++it) { ListValue* font_list_value; @@ -402,63 +401,63 @@ bool SetFontPrefExtensionFunction::RunImpl() { return true; } -const char* FontSettingsClearDefaultFontSizeFunction::GetPrefName() { +const char* ClearDefaultFontSizeFunction::GetPrefName() { return prefs::kWebKitDefaultFontSize; } -const char* FontSettingsGetDefaultFontSizeFunction::GetPrefName() { +const char* GetDefaultFontSizeFunction::GetPrefName() { return prefs::kWebKitDefaultFontSize; } -const char* FontSettingsGetDefaultFontSizeFunction::GetKey() { +const char* GetDefaultFontSizeFunction::GetKey() { return kPixelSizeKey; } -const char* FontSettingsSetDefaultFontSizeFunction::GetPrefName() { +const char* SetDefaultFontSizeFunction::GetPrefName() { return prefs::kWebKitDefaultFontSize; } -const char* FontSettingsSetDefaultFontSizeFunction::GetKey() { +const char* SetDefaultFontSizeFunction::GetKey() { return kPixelSizeKey; } -const char* FontSettingsClearDefaultFixedFontSizeFunction::GetPrefName() { +const char* ClearDefaultFixedFontSizeFunction::GetPrefName() { return prefs::kWebKitDefaultFixedFontSize; } -const char* FontSettingsGetDefaultFixedFontSizeFunction::GetPrefName() { +const char* GetDefaultFixedFontSizeFunction::GetPrefName() { return prefs::kWebKitDefaultFixedFontSize; } -const char* FontSettingsGetDefaultFixedFontSizeFunction::GetKey() { +const char* GetDefaultFixedFontSizeFunction::GetKey() { return kPixelSizeKey; } -const char* FontSettingsSetDefaultFixedFontSizeFunction::GetPrefName() { +const char* SetDefaultFixedFontSizeFunction::GetPrefName() { return prefs::kWebKitDefaultFixedFontSize; } -const char* FontSettingsSetDefaultFixedFontSizeFunction::GetKey() { +const char* SetDefaultFixedFontSizeFunction::GetKey() { return kPixelSizeKey; } -const char* FontSettingsClearMinimumFontSizeFunction::GetPrefName() { +const char* ClearMinimumFontSizeFunction::GetPrefName() { return prefs::kWebKitMinimumFontSize; } -const char* FontSettingsGetMinimumFontSizeFunction::GetPrefName() { +const char* GetMinimumFontSizeFunction::GetPrefName() { return prefs::kWebKitMinimumFontSize; } -const char* FontSettingsGetMinimumFontSizeFunction::GetKey() { +const char* GetMinimumFontSizeFunction::GetKey() { return kPixelSizeKey; } -const char* FontSettingsSetMinimumFontSizeFunction::GetPrefName() { +const char* SetMinimumFontSizeFunction::GetPrefName() { return prefs::kWebKitMinimumFontSize; } -const char* FontSettingsSetMinimumFontSizeFunction::GetKey() { +const char* SetMinimumFontSizeFunction::GetKey() { return kPixelSizeKey; } diff --git a/chrome/browser/extensions/api/font_settings/font_settings_api.h b/chrome/browser/extensions/api/font_settings/font_settings_api.h index 3920ca8..e4d2cb0 100644 --- a/chrome/browser/extensions/api/font_settings/font_settings_api.h +++ b/chrome/browser/extensions/api/font_settings/font_settings_api.h @@ -5,8 +5,8 @@ // Defines the classes to realize the Font Settings Extension API as specified // in the extension API JSON. -#ifndef CHROME_BROWSER_EXTENSIONS_API_FONT_SETTINGS_FONT_SETTINGS_API_H_ -#define CHROME_BROWSER_EXTENSIONS_API_FONT_SETTINGS_FONT_SETTINGS_API_H_ +#ifndef CHROME_BROWSER_EXTENSIONS_API_FONT_SETTINGS_FONT_SETTINGS_API_H__ +#define CHROME_BROWSER_EXTENSIONS_API_FONT_SETTINGS_FONT_SETTINGS_API_H__ #include @@ -80,50 +80,50 @@ class FontSettingsAPI : public ProfileKeyedService { }; // fontSettings.clearFont API function. -class FontSettingsClearFontFunction : public SyncExtensionFunction { +class ClearFontFunction : public SyncExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("fontSettings.clearFont") protected: // RefCounted types have non-public destructors, as with all extension // functions in this file. - virtual ~FontSettingsClearFontFunction() {} + virtual ~ClearFontFunction() {} // ExtensionFunction: virtual bool RunImpl() OVERRIDE; }; // fontSettings.getFont API function. -class FontSettingsGetFontFunction : public SyncExtensionFunction { +class GetFontFunction : public SyncExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("fontSettings.getFont") protected: - virtual ~FontSettingsGetFontFunction() {} + virtual ~GetFontFunction() {} // ExtensionFunction: virtual bool RunImpl() OVERRIDE; }; // fontSettings.setFont API function. -class FontSettingsSetFontFunction : public SyncExtensionFunction { +class SetFontFunction : public SyncExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("fontSettings.setFont") protected: - virtual ~FontSettingsSetFontFunction() {} + virtual ~SetFontFunction() {} // ExtensionFunction: virtual bool RunImpl() OVERRIDE; }; // fontSettings.getFontList API function. -class FontSettingsGetFontListFunction : public AsyncExtensionFunction { +class GetFontListFunction : public AsyncExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("fontSettings.getFontList") protected: - virtual ~FontSettingsGetFontListFunction() {} + virtual ~GetFontListFunction() {} // ExtensionFunction: virtual bool RunImpl() OVERRIDE; @@ -183,114 +183,106 @@ class SetFontPrefExtensionFunction : public SyncExtensionFunction { // The following are get/set/clear API functions that act on a browser font // pref. -class FontSettingsClearDefaultFontSizeFunction - : public ClearFontPrefExtensionFunction { +class ClearDefaultFontSizeFunction : public ClearFontPrefExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("fontSettings.clearDefaultFontSize") protected: - virtual ~FontSettingsClearDefaultFontSizeFunction() {} + virtual ~ClearDefaultFontSizeFunction() {} // ClearFontPrefExtensionFunction: virtual const char* GetPrefName() OVERRIDE; }; -class FontSettingsGetDefaultFontSizeFunction - : public GetFontPrefExtensionFunction { +class GetDefaultFontSizeFunction : public GetFontPrefExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("fontSettings.getDefaultFontSize") protected: - virtual ~FontSettingsGetDefaultFontSizeFunction() {} + virtual ~GetDefaultFontSizeFunction() {} // GetFontPrefExtensionFunction: virtual const char* GetPrefName() OVERRIDE; virtual const char* GetKey() OVERRIDE; }; -class FontSettingsSetDefaultFontSizeFunction - : public SetFontPrefExtensionFunction { +class SetDefaultFontSizeFunction : public SetFontPrefExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("fontSettings.setDefaultFontSize") protected: - virtual ~FontSettingsSetDefaultFontSizeFunction() {} + virtual ~SetDefaultFontSizeFunction() {} // SetFontPrefExtensionFunction: virtual const char* GetPrefName() OVERRIDE; virtual const char* GetKey() OVERRIDE; }; -class FontSettingsClearDefaultFixedFontSizeFunction +class ClearDefaultFixedFontSizeFunction : public ClearFontPrefExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("fontSettings.clearDefaultFixedFontSize") protected: - virtual ~FontSettingsClearDefaultFixedFontSizeFunction() {} + virtual ~ClearDefaultFixedFontSizeFunction() {} // ClearFontPrefExtensionFunction: virtual const char* GetPrefName() OVERRIDE; }; -class FontSettingsGetDefaultFixedFontSizeFunction - : public GetFontPrefExtensionFunction { +class GetDefaultFixedFontSizeFunction : public GetFontPrefExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("fontSettings.getDefaultFixedFontSize") protected: - virtual ~FontSettingsGetDefaultFixedFontSizeFunction() {} + virtual ~GetDefaultFixedFontSizeFunction() {} // GetFontPrefExtensionFunction: virtual const char* GetPrefName() OVERRIDE; virtual const char* GetKey() OVERRIDE; }; -class FontSettingsSetDefaultFixedFontSizeFunction - : public SetFontPrefExtensionFunction { +class SetDefaultFixedFontSizeFunction : public SetFontPrefExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("fontSettings.setDefaultFixedFontSize") protected: - virtual ~FontSettingsSetDefaultFixedFontSizeFunction() {} + virtual ~SetDefaultFixedFontSizeFunction() {} // SetFontPrefExtensionFunction: virtual const char* GetPrefName() OVERRIDE; virtual const char* GetKey() OVERRIDE; }; -class FontSettingsClearMinimumFontSizeFunction - : public ClearFontPrefExtensionFunction { +class ClearMinimumFontSizeFunction : public ClearFontPrefExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("fontSettings.clearMinimumFontSize") protected: - virtual ~FontSettingsClearMinimumFontSizeFunction() {} + virtual ~ClearMinimumFontSizeFunction() {} // ClearFontPrefExtensionFunction: virtual const char* GetPrefName() OVERRIDE; }; -class FontSettingsGetMinimumFontSizeFunction - : public GetFontPrefExtensionFunction { +class GetMinimumFontSizeFunction : public GetFontPrefExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("fontSettings.getMinimumFontSize") protected: - virtual ~FontSettingsGetMinimumFontSizeFunction() {} + virtual ~GetMinimumFontSizeFunction() {} // GetFontPrefExtensionFunction: virtual const char* GetPrefName() OVERRIDE; virtual const char* GetKey() OVERRIDE; }; -class FontSettingsSetMinimumFontSizeFunction - : public SetFontPrefExtensionFunction { +class SetMinimumFontSizeFunction : public SetFontPrefExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("fontSettings.setMinimumFontSize") protected: - virtual ~FontSettingsSetMinimumFontSizeFunction() {} + virtual ~SetMinimumFontSizeFunction() {} // SetFontPrefExtensionFunction: virtual const char* GetPrefName() OVERRIDE; @@ -299,4 +291,4 @@ class FontSettingsSetMinimumFontSizeFunction } // namespace extensions -#endif // CHROME_BROWSER_EXTENSIONS_API_FONT_SETTINGS_FONT_SETTINGS_API_H_ +#endif // CHROME_BROWSER_EXTENSIONS_API_FONT_SETTINGS_FONT_SETTINGS_API_H__ diff --git a/chrome/browser/extensions/api/history/history_api.cc b/chrome/browser/extensions/api/history/history_api.cc index 06b5fcc..00af52c 100644 --- a/chrome/browser/extensions/api/history/history_api.cc +++ b/chrome/browser/extensions/api/history/history_api.cc @@ -268,7 +268,7 @@ void HistoryFunctionWithCallback::SendResponseToCallback() { Release(); // Balanced in RunImpl(). } -bool HistoryGetMostVisitedFunction::RunAsyncImpl() { +bool GetMostVisitedHistoryFunction::RunAsyncImpl() { scoped_ptr params = GetMostVisited::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(params.get()); @@ -288,12 +288,12 @@ bool HistoryGetMostVisitedFunction::RunAsyncImpl() { HistoryService* hs = HistoryServiceFactory::GetForProfile(profile(), Profile::EXPLICIT_ACCESS); hs->QueryFilteredURLs(max_results, filter, false, &cancelable_consumer_, - base::Bind(&HistoryGetMostVisitedFunction::QueryComplete, + base::Bind(&GetMostVisitedHistoryFunction::QueryComplete, base::Unretained(this))); return true; } -void HistoryGetMostVisitedFunction::QueryComplete( +void GetMostVisitedHistoryFunction::QueryComplete( CancelableRequestProvider::Handle handle, const history::FilteredURLList& data) { std::vector > results; @@ -308,7 +308,7 @@ void HistoryGetMostVisitedFunction::QueryComplete( SendAsyncResponse(); } -bool HistoryGetVisitsFunction::RunAsyncImpl() { +bool GetVisitsHistoryFunction::RunAsyncImpl() { scoped_ptr params(GetVisits::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); @@ -322,13 +322,13 @@ bool HistoryGetVisitsFunction::RunAsyncImpl() { hs->QueryURL(url, true, // Retrieve full history of a URL. &cancelable_consumer_, - base::Bind(&HistoryGetVisitsFunction::QueryComplete, + base::Bind(&GetVisitsHistoryFunction::QueryComplete, base::Unretained(this))); return true; } -void HistoryGetVisitsFunction::QueryComplete( +void GetVisitsHistoryFunction::QueryComplete( HistoryService::Handle request_service, bool success, const history::URLRow* url_row, @@ -347,7 +347,7 @@ void HistoryGetVisitsFunction::QueryComplete( SendAsyncResponse(); } -bool HistorySearchFunction::RunAsyncImpl() { +bool SearchHistoryFunction::RunAsyncImpl() { scoped_ptr params(Search::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); @@ -368,13 +368,13 @@ bool HistorySearchFunction::RunAsyncImpl() { HistoryServiceFactory::GetForProfile(profile(), Profile::EXPLICIT_ACCESS); hs->QueryHistory(search_text, options, &cancelable_consumer_, - base::Bind(&HistorySearchFunction::SearchComplete, + base::Bind(&SearchHistoryFunction::SearchComplete, base::Unretained(this))); return true; } -void HistorySearchFunction::SearchComplete( +void SearchHistoryFunction::SearchComplete( HistoryService::Handle request_handle, history::QueryResults* results) { HistoryItemList history_item_vec; @@ -391,7 +391,7 @@ void HistorySearchFunction::SearchComplete( SendAsyncResponse(); } -bool HistoryAddUrlFunction::RunImpl() { +bool AddUrlHistoryFunction::RunImpl() { scoped_ptr params(AddUrl::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); @@ -408,7 +408,7 @@ bool HistoryAddUrlFunction::RunImpl() { return true; } -bool HistoryDeleteUrlFunction::RunImpl() { +bool DeleteUrlHistoryFunction::RunImpl() { scoped_ptr params(DeleteUrl::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); @@ -425,7 +425,7 @@ bool HistoryDeleteUrlFunction::RunImpl() { return true; } -bool HistoryDeleteRangeFunction::RunAsyncImpl() { +bool DeleteRangeHistoryFunction::RunAsyncImpl() { scoped_ptr params(DeleteRange::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); @@ -440,18 +440,18 @@ bool HistoryDeleteRangeFunction::RunAsyncImpl() { restrict_urls, start_time, end_time, - base::Bind(&HistoryDeleteRangeFunction::DeleteComplete, + base::Bind(&DeleteRangeHistoryFunction::DeleteComplete, base::Unretained(this)), &task_tracker_); return true; } -void HistoryDeleteRangeFunction::DeleteComplete() { +void DeleteRangeHistoryFunction::DeleteComplete() { SendAsyncResponse(); } -bool HistoryDeleteAllFunction::RunAsyncImpl() { +bool DeleteAllHistoryFunction::RunAsyncImpl() { std::set restrict_urls; HistoryService* hs = HistoryServiceFactory::GetForProfile(profile(), @@ -460,14 +460,14 @@ bool HistoryDeleteAllFunction::RunAsyncImpl() { restrict_urls, base::Time::UnixEpoch(), // From the beginning of the epoch. base::Time::Now(), // To the current time. - base::Bind(&HistoryDeleteAllFunction::DeleteComplete, + base::Bind(&DeleteAllHistoryFunction::DeleteComplete, base::Unretained(this)), &task_tracker_); return true; } -void HistoryDeleteAllFunction::DeleteComplete() { +void DeleteAllHistoryFunction::DeleteComplete() { SendAsyncResponse(); } diff --git a/chrome/browser/extensions/api/history/history_api.h b/chrome/browser/extensions/api/history/history_api.h index 9700b18..83860eb 100644 --- a/chrome/browser/extensions/api/history/history_api.h +++ b/chrome/browser/extensions/api/history/history_api.h @@ -112,12 +112,12 @@ class HistoryFunctionWithCallback : public HistoryFunction { void SendResponseToCallback(); }; -class HistoryGetMostVisitedFunction : public HistoryFunctionWithCallback { +class GetMostVisitedHistoryFunction : public HistoryFunctionWithCallback { public: DECLARE_EXTENSION_FUNCTION_NAME("experimental.history.getMostVisited"); protected: - virtual ~HistoryGetMostVisitedFunction() {} + virtual ~GetMostVisitedHistoryFunction() {} // HistoryFunctionWithCallback: virtual bool RunAsyncImpl() OVERRIDE; @@ -127,12 +127,12 @@ class HistoryGetMostVisitedFunction : public HistoryFunctionWithCallback { const history::FilteredURLList& data); }; -class HistoryGetVisitsFunction : public HistoryFunctionWithCallback { +class GetVisitsHistoryFunction : public HistoryFunctionWithCallback { public: DECLARE_EXTENSION_FUNCTION_NAME("history.getVisits"); protected: - virtual ~HistoryGetVisitsFunction() {} + virtual ~GetVisitsHistoryFunction() {} // HistoryFunctionWithCallback: virtual bool RunAsyncImpl() OVERRIDE; @@ -144,12 +144,12 @@ class HistoryGetVisitsFunction : public HistoryFunctionWithCallback { history::VisitVector* visits); }; -class HistorySearchFunction : public HistoryFunctionWithCallback { +class SearchHistoryFunction : public HistoryFunctionWithCallback { public: DECLARE_EXTENSION_FUNCTION_NAME("history.search"); protected: - virtual ~HistorySearchFunction() {} + virtual ~SearchHistoryFunction() {} // HistoryFunctionWithCallback: virtual bool RunAsyncImpl() OVERRIDE; @@ -159,23 +159,23 @@ class HistorySearchFunction : public HistoryFunctionWithCallback { history::QueryResults* results); }; -class HistoryAddUrlFunction : public HistoryFunction { +class AddUrlHistoryFunction : public HistoryFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("history.addUrl"); protected: - virtual ~HistoryAddUrlFunction() {} + virtual ~AddUrlHistoryFunction() {} // HistoryFunctionWithCallback: virtual bool RunImpl() OVERRIDE; }; -class HistoryDeleteAllFunction : public HistoryFunctionWithCallback { +class DeleteAllHistoryFunction : public HistoryFunctionWithCallback { public: DECLARE_EXTENSION_FUNCTION_NAME("history.deleteAll"); protected: - virtual ~HistoryDeleteAllFunction() {} + virtual ~DeleteAllHistoryFunction() {} // HistoryFunctionWithCallback: virtual bool RunAsyncImpl() OVERRIDE; @@ -185,23 +185,23 @@ class HistoryDeleteAllFunction : public HistoryFunctionWithCallback { }; -class HistoryDeleteUrlFunction : public HistoryFunction { +class DeleteUrlHistoryFunction : public HistoryFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("history.deleteUrl"); protected: - virtual ~HistoryDeleteUrlFunction() {} + virtual ~DeleteUrlHistoryFunction() {} // HistoryFunctionWithCallback: virtual bool RunImpl() OVERRIDE; }; -class HistoryDeleteRangeFunction : public HistoryFunctionWithCallback { +class DeleteRangeHistoryFunction : public HistoryFunctionWithCallback { public: DECLARE_EXTENSION_FUNCTION_NAME("history.deleteRange"); protected: - virtual ~HistoryDeleteRangeFunction() {} + virtual ~DeleteRangeHistoryFunction() {} // HistoryFunctionWithCallback: virtual bool RunAsyncImpl() OVERRIDE; diff --git a/chrome/browser/extensions/api/i18n/i18n_api.cc b/chrome/browser/extensions/api/i18n/i18n_api.cc index ad2ef32..51897da 100644 --- a/chrome/browser/extensions/api/i18n/i18n_api.cc +++ b/chrome/browser/extensions/api/i18n/i18n_api.cc @@ -12,15 +12,15 @@ #include "base/string_split.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/common/extensions/api/i18n.h" #include "chrome/common/pref_names.h" +#include "chrome/common/extensions/api/i18n.h" namespace GetAcceptLanguages = extensions::api::i18n::GetAcceptLanguages; // Errors. static const char kEmptyAcceptLanguagesError[] = "accept-languages is empty."; -bool I18nGetAcceptLanguagesFunction::RunImpl() { +bool GetAcceptLanguagesFunction::RunImpl() { std::string accept_languages = profile()->GetPrefs()->GetString(prefs::kAcceptLanguages); // Currently, there are 2 ways to set browser's accept-languages: through UI diff --git a/chrome/browser/extensions/api/i18n/i18n_api.h b/chrome/browser/extensions/api/i18n/i18n_api.h index 1975f17..d69bd9b 100644 --- a/chrome/browser/extensions/api/i18n/i18n_api.h +++ b/chrome/browser/extensions/api/i18n/i18n_api.h @@ -2,15 +2,15 @@ // 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_I18N_I18N_API_H_ -#define CHROME_BROWSER_EXTENSIONS_API_I18N_I18N_API_H_ +#ifndef CHROME_BROWSER_EXTENSIONS_API_I18N_I18N_API_H__ +#define CHROME_BROWSER_EXTENSIONS_API_I18N_I18N_API_H__ #include "chrome/browser/extensions/extension_function.h" -class I18nGetAcceptLanguagesFunction : public SyncExtensionFunction { - virtual ~I18nGetAcceptLanguagesFunction() {} +class GetAcceptLanguagesFunction : public SyncExtensionFunction { + virtual ~GetAcceptLanguagesFunction() {} virtual bool RunImpl() OVERRIDE; DECLARE_EXTENSION_FUNCTION_NAME("i18n.getAcceptLanguages") }; -#endif // CHROME_BROWSER_EXTENSIONS_API_I18N_I18N_API_H_ +#endif // CHROME_BROWSER_EXTENSIONS_API_I18N_I18N_API_H__ diff --git a/chrome/browser/extensions/api/management/management_api.cc b/chrome/browser/extensions/api/management/management_api.cc index 04914f0..140517a 100644 --- a/chrome/browser/extensions/api/management/management_api.cc +++ b/chrome/browser/extensions/api/management/management_api.cc @@ -219,7 +219,7 @@ ExtensionService* AsyncManagementFunction::service() { return profile()->GetExtensionService(); } -bool ManagementGetAllFunction::RunImpl() { +bool GetAllExtensionsFunction::RunImpl() { ExtensionInfoList extensions; ExtensionSystem* system = ExtensionSystem::Get(profile()); @@ -231,7 +231,7 @@ bool ManagementGetAllFunction::RunImpl() { return true; } -bool ManagementGetFunction::RunImpl() { +bool GetExtensionByIdFunction::RunImpl() { scoped_ptr params( management::Get::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); @@ -250,7 +250,7 @@ bool ManagementGetFunction::RunImpl() { return true; } -bool ManagementGetPermissionWarningsByIdFunction::RunImpl() { +bool GetPermissionWarningsByIdFunction::RunImpl() { scoped_ptr params( management::GetPermissionWarningsById::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); @@ -269,13 +269,12 @@ bool ManagementGetPermissionWarningsByIdFunction::RunImpl() { namespace { -// This class helps ManagementGetPermissionWarningsByManifestFunction manage +// This class helps GetPermissionWarningsByManifestFunction manage // sending manifest JSON strings to the utility process for parsing. class SafeManifestJSONParser : public UtilityProcessHostClient { public: - SafeManifestJSONParser( - ManagementGetPermissionWarningsByManifestFunction* client, - const std::string& manifest) + SafeManifestJSONParser(GetPermissionWarningsByManifestFunction* client, + const std::string& manifest) : client_(client), manifest_(manifest) {} @@ -346,7 +345,7 @@ class SafeManifestJSONParser : public UtilityProcessHostClient { ~SafeManifestJSONParser() {} // The client who we'll report results back to. - ManagementGetPermissionWarningsByManifestFunction* client_; + GetPermissionWarningsByManifestFunction* client_; // Data to parse. std::string manifest_; @@ -359,7 +358,7 @@ class SafeManifestJSONParser : public UtilityProcessHostClient { } // namespace -bool ManagementGetPermissionWarningsByManifestFunction::RunImpl() { +bool GetPermissionWarningsByManifestFunction::RunImpl() { scoped_ptr params( management::GetPermissionWarningsByManifest::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); @@ -375,7 +374,7 @@ bool ManagementGetPermissionWarningsByManifestFunction::RunImpl() { return true; } -void ManagementGetPermissionWarningsByManifestFunction::OnParseSuccess( +void GetPermissionWarningsByManifestFunction::OnParseSuccess( DictionaryValue* parsed_manifest) { CHECK(parsed_manifest); @@ -396,7 +395,7 @@ void ManagementGetPermissionWarningsByManifestFunction::OnParseSuccess( Release(); } -void ManagementGetPermissionWarningsByManifestFunction::OnParseFailure( +void GetPermissionWarningsByManifestFunction::OnParseFailure( const std::string& error) { error_ = error; SendResponse(false); @@ -405,7 +404,7 @@ void ManagementGetPermissionWarningsByManifestFunction::OnParseFailure( Release(); } -bool ManagementLaunchAppFunction::RunImpl() { +bool LaunchAppFunction::RunImpl() { scoped_ptr params( management::LaunchApp::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); @@ -437,13 +436,13 @@ bool ManagementLaunchAppFunction::RunImpl() { return true; } -ManagementSetEnabledFunction::ManagementSetEnabledFunction() { +SetEnabledFunction::SetEnabledFunction() { } -ManagementSetEnabledFunction::~ManagementSetEnabledFunction() { +SetEnabledFunction::~SetEnabledFunction() { } -bool ManagementSetEnabledFunction::RunImpl() { +bool SetEnabledFunction::RunImpl() { scoped_ptr params( management::SetEnabled::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); @@ -488,30 +487,30 @@ bool ManagementSetEnabledFunction::RunImpl() { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - base::Bind(&ManagementSetEnabledFunction::SendResponse, this, true)); + base::Bind(&SetEnabledFunction::SendResponse, this, true)); return true; } -void ManagementSetEnabledFunction::InstallUIProceed() { +void SetEnabledFunction::InstallUIProceed() { service()->EnableExtension(extension_id_); SendResponse(true); Release(); } -void ManagementSetEnabledFunction::InstallUIAbort(bool user_initiated) { +void SetEnabledFunction::InstallUIAbort(bool user_initiated) { error_ = keys::kUserDidNotReEnableError; SendResponse(false); Release(); } -ManagementUninstallFunction::ManagementUninstallFunction() { +UninstallFunction::UninstallFunction() { } -ManagementUninstallFunction::~ManagementUninstallFunction() { +UninstallFunction::~UninstallFunction() { } -bool ManagementUninstallFunction::RunImpl() { +bool UninstallFunction::RunImpl() { scoped_ptr params( management::Uninstall::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); @@ -553,11 +552,11 @@ bool ManagementUninstallFunction::RunImpl() { } // static -void ManagementUninstallFunction::SetAutoConfirmForTest(bool should_proceed) { +void UninstallFunction::SetAutoConfirmForTest(bool should_proceed) { auto_confirm_for_test = should_proceed ? PROCEED : ABORT; } -void ManagementUninstallFunction::Finish(bool should_uninstall) { +void UninstallFunction::Finish(bool should_uninstall) { if (should_uninstall) { bool success = service()->UninstallExtension( extension_id_, @@ -574,12 +573,12 @@ void ManagementUninstallFunction::Finish(bool should_uninstall) { } -void ManagementUninstallFunction::ExtensionUninstallAccepted() { +void UninstallFunction::ExtensionUninstallAccepted() { Finish(true); Release(); } -void ManagementUninstallFunction::ExtensionUninstallCanceled() { +void UninstallFunction::ExtensionUninstallCanceled() { Finish(false); Release(); } diff --git a/chrome/browser/extensions/api/management/management_api.h b/chrome/browser/extensions/api/management/management_api.h index 07553c8..1bb39ee 100644 --- a/chrome/browser/extensions/api/management/management_api.h +++ b/chrome/browser/extensions/api/management/management_api.h @@ -34,41 +34,40 @@ class AsyncManagementFunction : public AsyncExtensionFunction { ExtensionService* service(); }; -class ManagementGetAllFunction : public ManagementFunction { +class GetAllExtensionsFunction : public ManagementFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("management.getAll"); protected: - virtual ~ManagementGetAllFunction() {} + virtual ~GetAllExtensionsFunction() {} // ExtensionFunction: virtual bool RunImpl() OVERRIDE; }; -class ManagementGetFunction : public ManagementFunction { +class GetExtensionByIdFunction : public ManagementFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("management.get"); protected: - virtual ~ManagementGetFunction() {} + virtual ~GetExtensionByIdFunction() {} // ExtensionFunction: virtual bool RunImpl() OVERRIDE; }; -class ManagementGetPermissionWarningsByIdFunction : public ManagementFunction { +class GetPermissionWarningsByIdFunction : public ManagementFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("management.getPermissionWarningsById"); protected: - virtual ~ManagementGetPermissionWarningsByIdFunction() {} + virtual ~GetPermissionWarningsByIdFunction() {} // ExtensionFunction: virtual bool RunImpl() OVERRIDE; }; -class ManagementGetPermissionWarningsByManifestFunction - : public AsyncExtensionFunction { +class GetPermissionWarningsByManifestFunction : public AsyncExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME( "management.getPermissionWarningsByManifest"); @@ -78,32 +77,32 @@ class ManagementGetPermissionWarningsByManifestFunction void OnParseFailure(const std::string& error); protected: - virtual ~ManagementGetPermissionWarningsByManifestFunction() {} + virtual ~GetPermissionWarningsByManifestFunction() {} // ExtensionFunction: virtual bool RunImpl() OVERRIDE; }; -class ManagementLaunchAppFunction : public ManagementFunction { +class LaunchAppFunction : public ManagementFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("management.launchApp"); protected: - virtual ~ManagementLaunchAppFunction() {} + virtual ~LaunchAppFunction() {} // ExtensionFunction: virtual bool RunImpl() OVERRIDE; }; -class ManagementSetEnabledFunction : public AsyncManagementFunction, +class SetEnabledFunction : public AsyncManagementFunction, public ExtensionInstallPrompt::Delegate { public: DECLARE_EXTENSION_FUNCTION_NAME("management.setEnabled"); - ManagementSetEnabledFunction(); + SetEnabledFunction(); protected: - virtual ~ManagementSetEnabledFunction(); + virtual ~SetEnabledFunction(); // ExtensionFunction: virtual bool RunImpl() OVERRIDE; @@ -119,12 +118,12 @@ class ManagementSetEnabledFunction : public AsyncManagementFunction, scoped_ptr install_prompt_; }; -class ManagementUninstallFunction : public AsyncManagementFunction, +class UninstallFunction : public AsyncManagementFunction, public ExtensionUninstallDialog::Delegate { public: DECLARE_EXTENSION_FUNCTION_NAME("management.uninstall"); - ManagementUninstallFunction(); + UninstallFunction(); static void SetAutoConfirmForTest(bool should_proceed); // ExtensionUninstallDialog::Delegate implementation. @@ -132,7 +131,7 @@ class ManagementUninstallFunction : public AsyncManagementFunction, virtual void ExtensionUninstallCanceled() OVERRIDE; private: - virtual ~ManagementUninstallFunction(); + virtual ~UninstallFunction(); virtual bool RunImpl() OVERRIDE; diff --git a/chrome/browser/extensions/api/management/management_api_browsertest.cc b/chrome/browser/extensions/api/management/management_api_browsertest.cc index 3250a3b..5ca8f4c6 100644 --- a/chrome/browser/extensions/api/management/management_api_browsertest.cc +++ b/chrome/browser/extensions/api/management/management_api_browsertest.cc @@ -106,9 +106,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementApiBrowserTest, const std::string id = extension->id(); // Uninstall, then cancel via the confirm dialog. - scoped_refptr uninstall_function( - new ManagementUninstallFunction()); - ManagementUninstallFunction::SetAutoConfirmForTest(false); + scoped_refptr uninstall_function(new UninstallFunction()); + UninstallFunction::SetAutoConfirmForTest(false); EXPECT_TRUE(MatchPattern( util::RunFunctionAndReturnError( @@ -122,8 +121,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementApiBrowserTest, EXPECT_TRUE(service->GetExtensionById(id, false) != NULL); // Uninstall, then accept via the confirm dialog. - uninstall_function = new ManagementUninstallFunction(); - ManagementUninstallFunction::SetAutoConfirmForTest(true); + uninstall_function = new UninstallFunction(); + UninstallFunction::SetAutoConfirmForTest(true); util::RunFunctionAndReturnSingleResult( uninstall_function, @@ -145,8 +144,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementApiBrowserTest, ASSERT_TRUE(listener.WaitUntilSatisfied()); // The management API should list this extension. - scoped_refptr function = - new ManagementGetAllFunction(); + scoped_refptr function = + new GetAllExtensionsFunction(); scoped_ptr result(util::RunFunctionAndReturnSingleResult( function.get(), "[]", browser())); base::ListValue* list; @@ -156,7 +155,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementApiBrowserTest, // And it should continue to do so even after it crashes. ASSERT_TRUE(CrashEnabledExtension(extension->id())); - function = new ManagementGetAllFunction(); + function = new GetAllExtensionsFunction(); result.reset(util::RunFunctionAndReturnSingleResult( function.get(), "[]", browser())); ASSERT_TRUE(result->GetAsList(&list)); @@ -201,8 +200,7 @@ class ExtensionManagementApiEscalationTest : void SetEnabled(bool enabled, bool user_gesture, const std::string& expected_error) { - scoped_refptr function( - new ManagementSetEnabledFunction); + scoped_refptr function(new SetEnabledFunction); const char* enabled_string = enabled ? "true" : "false"; if (user_gesture) function->set_user_gesture(true); @@ -229,8 +227,8 @@ const char ExtensionManagementApiEscalationTest::kId[] = IN_PROC_BROWSER_TEST_F(ExtensionManagementApiEscalationTest, DisabledReason) { - scoped_refptr function = - new ManagementGetFunction(); + scoped_refptr function = + new GetExtensionByIdFunction(); scoped_ptr result(util::RunFunctionAndReturnSingleResult( function.get(), base::StringPrintf("[\"%s\"]", kId), diff --git a/chrome/browser/extensions/api/permissions/permissions_api.cc b/chrome/browser/extensions/api/permissions/permissions_api.cc index ba71c9c..c395b9d 100644 --- a/chrome/browser/extensions/api/permissions/permissions_api.cc +++ b/chrome/browser/extensions/api/permissions/permissions_api.cc @@ -52,7 +52,7 @@ bool ignore_user_gesture_for_tests = false; } // namespace -bool PermissionsContainsFunction::RunImpl() { +bool ContainsPermissionsFunction::RunImpl() { scoped_ptr params(Contains::Params::Create(*args_)); scoped_refptr permissions = @@ -65,14 +65,14 @@ bool PermissionsContainsFunction::RunImpl() { return true; } -bool PermissionsGetAllFunction::RunImpl() { +bool GetAllPermissionsFunction::RunImpl() { scoped_ptr permissions = helpers::PackPermissionSet(GetExtension()->GetActivePermissions()); results_ = GetAll::Results::Create(*permissions); return true; } -bool PermissionsRemoveFunction::RunImpl() { +bool RemovePermissionsFunction::RunImpl() { scoped_ptr params(Remove::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); @@ -110,19 +110,19 @@ bool PermissionsRemoveFunction::RunImpl() { } // static -void PermissionsRequestFunction::SetAutoConfirmForTests(bool should_proceed) { +void RequestPermissionsFunction::SetAutoConfirmForTests(bool should_proceed) { auto_confirm_for_tests = should_proceed ? PROCEED : ABORT; } // static -void PermissionsRequestFunction::SetIgnoreUserGestureForTests( +void RequestPermissionsFunction::SetIgnoreUserGestureForTests( bool ignore) { ignore_user_gesture_for_tests = ignore; } -PermissionsRequestFunction::PermissionsRequestFunction() {} +RequestPermissionsFunction::RequestPermissionsFunction() {} -void PermissionsRequestFunction::InstallUIProceed() { +void RequestPermissionsFunction::InstallUIProceed() { PermissionsUpdater perms_updater(profile()); perms_updater.AddPermissions(GetExtension(), requested_permissions_.get()); @@ -132,16 +132,16 @@ void PermissionsRequestFunction::InstallUIProceed() { Release(); // Balanced in RunImpl(). } -void PermissionsRequestFunction::InstallUIAbort(bool user_initiated) { +void RequestPermissionsFunction::InstallUIAbort(bool user_initiated) { results_ = Request::Results::Create(false); SendResponse(true); Release(); // Balanced in RunImpl(). } -PermissionsRequestFunction::~PermissionsRequestFunction() {} +RequestPermissionsFunction::~RequestPermissionsFunction() {} -bool PermissionsRequestFunction::RunImpl() { +bool RequestPermissionsFunction::RunImpl() { if (!user_gesture() && !ignore_user_gesture_for_tests) { error_ = kUserGestureRequiredError; return false; diff --git a/chrome/browser/extensions/api/permissions/permissions_api.h b/chrome/browser/extensions/api/permissions/permissions_api.h index 21281e2..9801ea1 100644 --- a/chrome/browser/extensions/api/permissions/permissions_api.h +++ b/chrome/browser/extensions/api/permissions/permissions_api.h @@ -15,48 +15,48 @@ class ExtensionService; // chrome.permissions.contains -class PermissionsContainsFunction : public SyncExtensionFunction { +class ContainsPermissionsFunction : public SyncExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("permissions.contains") protected: - virtual ~PermissionsContainsFunction() {} + virtual ~ContainsPermissionsFunction() {} // ExtensionFunction: virtual bool RunImpl() OVERRIDE; }; // chrome.permissions.getAll -class PermissionsGetAllFunction : public SyncExtensionFunction { +class GetAllPermissionsFunction : public SyncExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("permissions.getAll") protected: - virtual ~PermissionsGetAllFunction() {} + virtual ~GetAllPermissionsFunction() {} // ExtensionFunction: virtual bool RunImpl() OVERRIDE; }; // chrome.permissions.remove -class PermissionsRemoveFunction : public SyncExtensionFunction { +class RemovePermissionsFunction : public SyncExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("permissions.remove") protected: - virtual ~PermissionsRemoveFunction() {} + virtual ~RemovePermissionsFunction() {} // ExtensionFunction: virtual bool RunImpl() OVERRIDE; }; // chrome.permissions.request -class PermissionsRequestFunction : public AsyncExtensionFunction, +class RequestPermissionsFunction : public AsyncExtensionFunction, public ExtensionInstallPrompt::Delegate { public: DECLARE_EXTENSION_FUNCTION_NAME("permissions.request") - PermissionsRequestFunction(); + RequestPermissionsFunction(); // FOR TESTS ONLY to bypass the confirmation UI. static void SetAutoConfirmForTests(bool should_proceed); @@ -67,7 +67,7 @@ class PermissionsRequestFunction : public AsyncExtensionFunction, virtual void InstallUIAbort(bool user_initiated) OVERRIDE; protected: - virtual ~PermissionsRequestFunction(); + virtual ~RequestPermissionsFunction(); // ExtensionFunction: virtual bool RunImpl() OVERRIDE; diff --git a/chrome/browser/extensions/api/permissions/permissions_apitest.cc b/chrome/browser/extensions/api/permissions/permissions_apitest.cc index a3944b5..4006d9b 100644 --- a/chrome/browser/extensions/api/permissions/permissions_apitest.cc +++ b/chrome/browser/extensions/api/permissions/permissions_apitest.cc @@ -85,7 +85,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, OptionalPermissionsGranted) { prefs->AddGrantedPermissions("kjmkgkdkpedkejedfhmfcenooemhbpbo", granted_permissions); - PermissionsRequestFunction::SetIgnoreUserGestureForTests(true); + RequestPermissionsFunction::SetIgnoreUserGestureForTests(true); host_resolver()->AddRule("*.com", "127.0.0.1"); ASSERT_TRUE(StartTestServer()); EXPECT_TRUE(RunExtensionTest("permissions/optional")) << message_; @@ -95,8 +95,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, OptionalPermissionsGranted) { IN_PROC_BROWSER_TEST_F(ExtensionApiTest, OptionalPermissionsAutoConfirm) { // Rather than setting the granted permissions, set the UI autoconfirm flag // and run the same tests. - PermissionsRequestFunction::SetAutoConfirmForTests(true); - PermissionsRequestFunction::SetIgnoreUserGestureForTests(true); + RequestPermissionsFunction::SetAutoConfirmForTests(true); + RequestPermissionsFunction::SetIgnoreUserGestureForTests(true); host_resolver()->AddRule("*.com", "127.0.0.1"); ASSERT_TRUE(StartTestServer()); EXPECT_TRUE(RunExtensionTest("permissions/optional")) << message_; @@ -107,15 +107,15 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, OptionalPermissionsAutoConfirm) { IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ComplexOptionalPermissions) { // Rather than setting the granted permissions, set the UI autoconfirm flag // and run the same tests. - PermissionsRequestFunction::SetAutoConfirmForTests(true); - PermissionsRequestFunction::SetIgnoreUserGestureForTests(true); + RequestPermissionsFunction::SetAutoConfirmForTests(true); + RequestPermissionsFunction::SetIgnoreUserGestureForTests(true); EXPECT_TRUE(RunExtensionTest("permissions/complex_optional")) << message_; } // Test that denying the optional permissions confirmation dialog works. IN_PROC_BROWSER_TEST_F(ExtensionApiTest, OptionalPermissionsDeny) { - PermissionsRequestFunction::SetAutoConfirmForTests(false); - PermissionsRequestFunction::SetIgnoreUserGestureForTests(true); + RequestPermissionsFunction::SetAutoConfirmForTests(false); + RequestPermissionsFunction::SetIgnoreUserGestureForTests(true); host_resolver()->AddRule("*.com", "127.0.0.1"); ASSERT_TRUE(StartTestServer()); EXPECT_TRUE(RunExtensionTest("permissions/optional_deny")) << message_; @@ -124,7 +124,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, OptionalPermissionsDeny) { // Tests that the permissions.request function must be called from within a // user gesture. IN_PROC_BROWSER_TEST_F(ExtensionApiTest, OptionalPermissionsGesture) { - PermissionsRequestFunction::SetIgnoreUserGestureForTests(false); + RequestPermissionsFunction::SetIgnoreUserGestureForTests(false); host_resolver()->AddRule("*.com", "127.0.0.1"); ASSERT_TRUE(StartTestServer()); EXPECT_TRUE(RunExtensionTest("permissions/optional_gesture")) << message_; diff --git a/chrome/browser/extensions/api/processes/processes_api.cc b/chrome/browser/extensions/api/processes/processes_api.cc index eb20dac..3042051 100644 --- a/chrome/browser/extensions/api/processes/processes_api.cc +++ b/chrome/browser/extensions/api/processes/processes_api.cc @@ -12,6 +12,7 @@ #include "base/string_number_conversions.h" #include "base/utf_string_conversions.h" #include "base/values.h" + #include "chrome/browser/extensions/api/processes/processes_api_constants.h" #include "chrome/browser/extensions/api/tabs/tabs_constants.h" #include "chrome/browser/extensions/event_router.h" diff --git a/chrome/browser/extensions/api/record/record_api.cc b/chrome/browser/extensions/api/record/record_api.cc index a16b755..ff60cb3 100644 --- a/chrome/browser/extensions/api/record/record_api.cc +++ b/chrome/browser/extensions/api/record/record_api.cc @@ -152,16 +152,16 @@ const ProcessStrategy &RunPageCyclerFunction::GetProcessStrategy() { return *process_strategy_; } -// RecordCaptureURLsFunction ------------------------------------------------ +// CaptureURLsFunction ------------------------------------------------ -RecordCaptureURLsFunction::RecordCaptureURLsFunction() +CaptureURLsFunction::CaptureURLsFunction() : RunPageCyclerFunction(new ProductionProcessStrategy()) {} -RecordCaptureURLsFunction::RecordCaptureURLsFunction(ProcessStrategy* strategy) +CaptureURLsFunction::CaptureURLsFunction(ProcessStrategy* strategy) : RunPageCyclerFunction(strategy) {} // Fetch data for possible optional switch for an extension to load. -bool RecordCaptureURLsFunction::ParseJSParameters() { +bool CaptureURLsFunction::ParseJSParameters() { scoped_ptr params( record::CaptureURLs::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); @@ -174,34 +174,34 @@ bool RecordCaptureURLsFunction::ParseJSParameters() { return true; } -// RecordCaptureURLsFunction adds "record-mode" to sub-browser call, and returns +// CaptureURLsFunction adds "record-mode" to sub-browser call, and returns // just the (possibly empty) error list. -void RecordCaptureURLsFunction::AddSwitches(CommandLine* line) { +void CaptureURLsFunction::AddSwitches(CommandLine* line) { if (!line->HasSwitch(switches::kRecordMode)) line->AppendSwitch(switches::kRecordMode); } -void RecordCaptureURLsFunction::Finish() { +void CaptureURLsFunction::Finish() { results_ = record::CaptureURLs::Results::Create(errors_); SendResponse(true); } -// RecordReplayURLsFunction ------------------------------------------------ +// ReplayURLsFunction ------------------------------------------------ -RecordReplayURLsFunction::RecordReplayURLsFunction() +ReplayURLsFunction::ReplayURLsFunction() : RunPageCyclerFunction(new ProductionProcessStrategy()), run_time_ms_(0.0) { } -RecordReplayURLsFunction::RecordReplayURLsFunction(ProcessStrategy* strategy) +ReplayURLsFunction::ReplayURLsFunction(ProcessStrategy* strategy) : RunPageCyclerFunction(strategy), run_time_ms_(0.0) { } -RecordReplayURLsFunction::~RecordReplayURLsFunction() {} +ReplayURLsFunction::~ReplayURLsFunction() {} // Fetch data for possible optional switches for a repeat count and an // extension to load. -bool RecordReplayURLsFunction::ParseJSParameters() { +bool ReplayURLsFunction::ParseJSParameters() { scoped_ptr params( record::ReplayURLs::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); @@ -228,7 +228,7 @@ bool RecordReplayURLsFunction::ParseJSParameters() { // plus temp file into which to place stats. (Can't do this in // ParseJSParameters because file creation can't go on the UI thread.) // Plus, initialize time to create run time statistic. -void RecordReplayURLsFunction::AddSwitches(CommandLine* line) { +void ReplayURLsFunction::AddSwitches(CommandLine* line) { file_util::CreateTemporaryFile(&stats_file_path_); if (!extension_path_.empty()) @@ -240,13 +240,13 @@ void RecordReplayURLsFunction::AddSwitches(CommandLine* line) { } // Read stats file, and get run time. -void RecordReplayURLsFunction::ReadReplyFiles() { +void ReplayURLsFunction::ReadReplyFiles() { file_util::ReadFileToString(stats_file_path_, &stats_); run_time_ms_ = (base::Time::NowFromSystemTime() - timer_).InMillisecondsF(); } -void RecordReplayURLsFunction::Finish() { +void ReplayURLsFunction::Finish() { record::ReplayURLsResult result; result.run_time = run_time_ms_; diff --git a/chrome/browser/extensions/api/record/record_api.h b/chrome/browser/extensions/api/record/record_api.h index 69974ac..4f5559a 100644 --- a/chrome/browser/extensions/api/record/record_api.h +++ b/chrome/browser/extensions/api/record/record_api.h @@ -5,10 +5,10 @@ #ifndef CHROME_BROWSER_EXTENSIONS_API_RECORD_RECORD_API_H_ #define CHROME_BROWSER_EXTENSIONS_API_RECORD_RECORD_API_H_ +#include "chrome/browser/extensions/extension_function.h" #include "base/command_line.h" #include "base/file_path.h" #include "base/time.h" -#include "chrome/browser/extensions/extension_function.h" namespace { @@ -105,15 +105,15 @@ class RunPageCyclerFunction : public AsyncExtensionFunction { scoped_ptr process_strategy_; }; -class RecordCaptureURLsFunction : public RunPageCyclerFunction { +class CaptureURLsFunction : public RunPageCyclerFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("experimental.record.captureURLs"); - RecordCaptureURLsFunction(); - explicit RecordCaptureURLsFunction(ProcessStrategy* strategy); + CaptureURLsFunction(); + explicit CaptureURLsFunction(ProcessStrategy* strategy); private: - virtual ~RecordCaptureURLsFunction() {} + virtual ~CaptureURLsFunction() {} // Read the ReplayDetails parameter if it exists. virtual bool ParseJSParameters() OVERRIDE; @@ -125,15 +125,15 @@ class RecordCaptureURLsFunction : public RunPageCyclerFunction { virtual void Finish() OVERRIDE; }; -class RecordReplayURLsFunction : public RunPageCyclerFunction { +class ReplayURLsFunction : public RunPageCyclerFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("experimental.record.replayURLs"); - RecordReplayURLsFunction(); - explicit RecordReplayURLsFunction(ProcessStrategy* strategy); + ReplayURLsFunction(); + explicit ReplayURLsFunction(ProcessStrategy* strategy); private: - virtual ~RecordReplayURLsFunction(); + virtual ~ReplayURLsFunction(); // Read the ReplayDetails parameter if it exists. virtual bool ParseJSParameters() OVERRIDE; diff --git a/chrome/browser/extensions/api/record/record_api_test.cc b/chrome/browser/extensions/api/record/record_api_test.cc index 50de96a..c81dbe1 100644 --- a/chrome/browser/extensions/api/record/record_api_test.cc +++ b/chrome/browser/extensions/api/record/record_api_test.cc @@ -217,14 +217,13 @@ class RecordApiTest : public InProcessBrowserTest { // Run a capture, using standard URL test list and the specified // user data dir. Return via |out_list| the list of error URLs, // if any, resulting from the capture. And return directly the - // RecordCaptureURLsFunction that was used, so that its state may be + // CaptureURLsFunction that was used, so that its state may be // queried. - scoped_refptr RunCapture( - const FilePath& user_data_dir, + scoped_refptr RunCapture(const FilePath& user_data_dir, scoped_ptr* out_list) { - scoped_refptr capture_function( - new RecordCaptureURLsFunction(new TestProcessStrategy(&temp_files_))); + scoped_refptr capture_function( + new CaptureURLsFunction(new TestProcessStrategy(&temp_files_))); std::string escaped_user_data_dir; ReplaceChars(user_data_dir.AsUTF8Unsafe(), "\\", "\\\\", @@ -278,7 +277,7 @@ IN_PROC_BROWSER_TEST_F(RecordApiTest, DISABLED_CheckCapture) { scoped_ptr result; EXPECT_TRUE(user_data_dir.CreateUniqueTempDir()); - scoped_refptr capture_URLs_function = + scoped_refptr capture_URLs_function = RunCapture(user_data_dir.path(), &result); // Check that user-data-dir switch has been properly overridden. @@ -315,8 +314,7 @@ IN_PROC_BROWSER_TEST_F(RecordApiTest, MAYBE_CheckPlayback) { ReplaceChars(user_data_dir.path().AsUTF8Unsafe(), "\\", "\\\\", &escaped_user_data_dir); - scoped_refptr playback_function( - new RecordReplayURLsFunction( + scoped_refptr playback_function(new ReplayURLsFunction( new TestProcessStrategy(&temp_files_))); scoped_ptr result(utils::ToDictionary( utils::RunFunctionAndReturnSingleResult(playback_function, diff --git a/chrome/browser/extensions/api/storage/leveldb_settings_storage_factory.cc b/chrome/browser/extensions/api/storage/leveldb_settings_storage_factory.cc deleted file mode 100644 index 6fbffbf..0000000 --- a/chrome/browser/extensions/api/storage/leveldb_settings_storage_factory.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/extensions/api/storage/leveldb_settings_storage_factory.h" - -#include "base/logging.h" -#include "chrome/browser/value_store/leveldb_value_store.h" - -namespace extensions { - -ValueStore* LeveldbSettingsStorageFactory::Create( - const FilePath& base_path, - const std::string& extension_id) { - return new LeveldbValueStore(base_path.AppendASCII(extension_id)); -} - -} // namespace extensions diff --git a/chrome/browser/extensions/api/storage/leveldb_settings_storage_factory.h b/chrome/browser/extensions/api/storage/leveldb_settings_storage_factory.h deleted file mode 100644 index 395fcfc..0000000 --- a/chrome/browser/extensions/api/storage/leveldb_settings_storage_factory.h +++ /dev/null @@ -1,25 +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_API_STORAGE_LEVELDB_SETTINGS_STORAGE_FACTORY_H_ -#define CHROME_BROWSER_EXTENSIONS_API_STORAGE_LEVELDB_SETTINGS_STORAGE_FACTORY_H_ - -#include "chrome/browser/extensions/api/storage/settings_storage_factory.h" - -namespace extensions { - -// Factory for creating LeveldbValueStore instances. -class LeveldbSettingsStorageFactory : public SettingsStorageFactory { - public: - virtual ValueStore* Create(const FilePath& base_path, - const std::string& extension_id) OVERRIDE; - - private: - // SettingsStorageFactory is refcounted. - virtual ~LeveldbSettingsStorageFactory() {} -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_STORAGE_LEVELDB_SETTINGS_STORAGE_FACTORY_H_ diff --git a/chrome/browser/extensions/api/storage/managed_value_store_cache.cc b/chrome/browser/extensions/api/storage/managed_value_store_cache.cc deleted file mode 100644 index e71d9bf..0000000 --- a/chrome/browser/extensions/api/storage/managed_value_store_cache.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/extensions/api/storage/managed_value_store_cache.h" - -#include - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/callback.h" -#include "base/file_util.h" -#include "base/logging.h" -#include "base/message_loop_proxy.h" -#include "chrome/browser/extensions/api/storage/policy_value_store.h" -#include "chrome/browser/extensions/api/storage/settings_storage_factory.h" -#include "chrome/browser/extensions/event_names.h" -#include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/value_store/value_store_change.h" -#include "chrome/common/extensions/extension.h" -#include "content/public/browser/browser_thread.h" - -using content::BrowserThread; - -namespace extensions { - -ManagedValueStoreCache::ManagedValueStoreCache( - policy::PolicyService* policy_service, - EventRouter* event_router, - const scoped_refptr& factory, - const scoped_refptr& observers, - const FilePath& profile_path) - : ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), - weak_this_on_ui_(weak_factory_.GetWeakPtr()), - policy_service_(policy_service), - event_router_(event_router), - storage_factory_(factory), - observers_(observers), - base_path_(profile_path.AppendASCII( - ExtensionService::kManagedSettingsDirectoryName)) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - // |event_router| can be NULL on unit_tests. - if (event_router_) - event_router_->RegisterObserver(this, event_names::kOnSettingsChanged); - policy_service_->AddObserver(policy::POLICY_DOMAIN_EXTENSIONS, this); -} - -ManagedValueStoreCache::~ManagedValueStoreCache() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - DCHECK(!event_router_); - // Delete the PolicyValueStores on FILE. - store_map_.clear(); -} - -void ManagedValueStoreCache::ShutdownOnUI() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - policy_service_->RemoveObserver(policy::POLICY_DOMAIN_EXTENSIONS, this); - policy_service_ = NULL; - if (event_router_) - event_router_->UnregisterObserver(this); - event_router_ = NULL; - weak_factory_.InvalidateWeakPtrs(); -} - -void ManagedValueStoreCache::RunWithValueStoreForExtension( - const StorageCallback& callback, - scoped_refptr extension) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - PolicyValueStore* store = GetStoreFor(extension->id()); - if (store) { - callback.Run(store); - } else { - // First time that an extension calls storage.managed.get(). Create the - // store and load it with the current policy, and don't send event - // notifications. - CreateStoreFor( - extension->id(), - false, - base::Bind(&ManagedValueStoreCache::RunWithValueStoreForExtension, - base::Unretained(this), - callback, - extension)); - } -} - -void ManagedValueStoreCache::DeleteStorageSoon( - const std::string& extension_id) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - PolicyValueStore* store = GetStoreFor(extension_id); - if (!store) { - // It's possible that the store exists, but hasn't been loaded yet - // (because the extension is unloaded, for example). Open the database to - // clear it if it exists. - // TODO(joaodasilva): move this check to a ValueStore method. - if (file_util::DirectoryExists(base_path_.AppendASCII(extension_id))) { - CreateStoreFor( - extension_id, - false, - base::Bind(&ManagedValueStoreCache::DeleteStorageSoon, - base::Unretained(this), - extension_id)); - } - } else { - store->DeleteStorage(); - store_map_.erase(extension_id); - } -} - -void ManagedValueStoreCache::OnPolicyUpdated(policy::PolicyDomain domain, - const std::string& component_id, - const policy::PolicyMap& previous, - const policy::PolicyMap& current) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, - base::Bind(&ManagedValueStoreCache::UpdatePolicyOnFILE, - base::Unretained(this), - std::string(component_id), - base::Passed(current.DeepCopy()))); -} - -void ManagedValueStoreCache::UpdatePolicyOnFILE( - const std::string& extension_id, - scoped_ptr current_policy) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - PolicyValueStore* store = GetStoreFor(extension_id); - if (!store) { - // The extension hasn't executed any storage.managed.* calls, and isn't - // listening for onChanged() either. Ignore this notification in that case. - return; - } - // Update the policy on the backing store, and fire notifications if it - // changed. - store->SetCurrentPolicy(*current_policy, true); -} - -void ManagedValueStoreCache::OnListenerAdded( - const EventListenerInfo& details) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK_EQ(std::string(event_names::kOnSettingsChanged), details.event_name); - // This is invoked on several occasions: - // - // 1. when an extension first registers to observe storage.onChanged; in this - // case the backend doesn't have any previous data persisted, and it won't - // trigger a notification. - // - // 2. when the browser starts up and all existing extensions re-register for - // the onChanged event. In this case, if the current policy differs from - // the persisted version then a notification will be sent. - // - // 3. a policy update just occurred and sent a notification, and an extension - // with EventPages that is observing onChanged just woke up and registed - // again. In this case the policy update already persisted the current - // policy version, and |store| already exists. - BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, - base::Bind(&ManagedValueStoreCache::CreateForExtensionOnFILE, - base::Unretained(this), - details.extension_id)); -} - -void ManagedValueStoreCache::CreateForExtensionOnFILE( - const std::string& extension_id) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - PolicyValueStore* store = GetStoreFor(extension_id); - if (!store) - CreateStoreFor(extension_id, true, base::Closure()); -} - -PolicyValueStore* ManagedValueStoreCache::GetStoreFor( - const std::string& extension_id) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - PolicyValueStoreMap::iterator it = store_map_.find(extension_id); - if (it == store_map_.end()) - return NULL; - return it->second.get(); -} - -void ManagedValueStoreCache::CreateStoreFor( - const std::string& extension_id, - bool notify_if_changed, - const base::Closure& continuation) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - DCHECK(!GetStoreFor(extension_id)); - // Creating or loading an existing database requires an immediate update - // with the current policy for the corresponding extension, which must be - // retrieved on UI. - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&ManagedValueStoreCache::GetInitialPolicy, - weak_this_on_ui_, - extension_id, - notify_if_changed, - continuation)); -} - -void ManagedValueStoreCache::GetInitialPolicy( - const std::string& extension_id, - bool notify_if_changed, - const base::Closure& continuation) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - const policy::PolicyMap& policy = policy_service_->GetPolicies( - policy::POLICY_DOMAIN_EXTENSIONS, extension_id); - // Now post back to FILE to create the database. - BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, - base::Bind(&ManagedValueStoreCache::CreateStoreWithInitialPolicy, - base::Unretained(this), - extension_id, - notify_if_changed, - base::Passed(policy.DeepCopy()), - continuation)); -} - -void ManagedValueStoreCache::CreateStoreWithInitialPolicy( - const std::string& extension_id, - bool notify_if_changed, - scoped_ptr initial_policy, - const base::Closure& continuation) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - // If a 2nd call to CreateStoreFor() is issued before the 1st gets to execute - // its UI task, then the 2nd will enter this function but the store has - // already been created. Check for that. - PolicyValueStore* store = GetStoreFor(extension_id); - - if (!store) { - // Create it now. - - // If the database doesn't exist yet then this is the initial install, - // and no notifications should be issued in that case. - // TODO(joaodasilva): move this check to a ValueStore method. - if (!file_util::DirectoryExists(base_path_.AppendASCII(extension_id))) - notify_if_changed = false; - - store = new PolicyValueStore( - extension_id, - observers_, - make_scoped_ptr(storage_factory_->Create(base_path_, extension_id))); - store_map_[extension_id] = make_linked_ptr(store); - } - - // Send the latest policy to the store. - store->SetCurrentPolicy(*initial_policy, notify_if_changed); - - // And finally resume from where this process started. - if (!continuation.is_null()) - continuation.Run(); -} - -} // namespace extensions diff --git a/chrome/browser/extensions/api/storage/managed_value_store_cache.h b/chrome/browser/extensions/api/storage/managed_value_store_cache.h deleted file mode 100644 index 2862732..0000000 --- a/chrome/browser/extensions/api/storage/managed_value_store_cache.h +++ /dev/null @@ -1,154 +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_API_STORAGE_MANAGED_VALUE_STORE_CACHE_H_ -#define CHROME_BROWSER_EXTENSIONS_API_STORAGE_MANAGED_VALUE_STORE_CACHE_H_ - -#include -#include - -#include "base/basictypes.h" -#include "base/callback_forward.h" -#include "base/compiler_specific.h" -#include "base/file_path.h" -#include "base/memory/linked_ptr.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" -#include "chrome/browser/extensions/api/storage/settings_observer.h" -#include "chrome/browser/extensions/api/storage/value_store_cache.h" -#include "chrome/browser/extensions/event_router.h" -#include "chrome/browser/policy/policy_service.h" - -namespace policy { -class PolicyMap; -} - -namespace extensions { - -class PolicyValueStore; -class SettingsStorageFactory; - -// A ValueStoreCache that manages a PolicyValueStore for each extension that -// uses the storage.managed namespace. This class observes policy changes and -// which extensions listen for storage.onChanged(), and sends the appropriate -// updates to the corresponding PolicyValueStore on the FILE thread. -class ManagedValueStoreCache : public ValueStoreCache, - public policy::PolicyService::Observer, - public EventRouter::Observer { - public: - // |policy_service| is used to retrieve policy for extensions, and to observe - // policy updates. - // ||event_router| is used to observe which extensions listen for onChanged. - // |factory| is used to create databases for the PolicyValueStores. - // |observers| is the list of SettingsObservers to notify when a ValueStore - // changes. - // |profile_path| is the path for the profile. The databases are created in - // a directory under this path. - ManagedValueStoreCache(policy::PolicyService* policy_service, - EventRouter* event_router, - const scoped_refptr& factory, - const scoped_refptr& observers, - const FilePath& profile_path); - virtual ~ManagedValueStoreCache(); - - private: - // Maps an extension ID to its PolicyValueStoreMap. - typedef std::map > - PolicyValueStoreMap; - - // ValueStoreCache implementation: - virtual void ShutdownOnUI() OVERRIDE; - virtual void RunWithValueStoreForExtension( - const StorageCallback& callback, - scoped_refptr extension) OVERRIDE; - virtual void DeleteStorageSoon(const std::string& extension_id) OVERRIDE; - - // PolicyService::Observer implementation: - virtual void OnPolicyUpdated(policy::PolicyDomain domain, - const std::string& component_id, - const policy::PolicyMap& previous, - const policy::PolicyMap& current) OVERRIDE; - - // Posted by OnPolicyUpdated() to update a PolicyValueStore on the FILE - // thread. - void UpdatePolicyOnFILE(const std::string& extension_id, - scoped_ptr current_policy); - - // EventRouter::Observer implementation: - virtual void OnListenerAdded(const EventListenerInfo& details) OVERRIDE; - - // Posted by OnListenerAdded() to load or create a PolicyValueStore for the - // given |extension_id|. - void CreateForExtensionOnFILE(const std::string& extension_id); - - // Returns an existing PolicyValueStore for |extension_id|, or NULL. - PolicyValueStore* GetStoreFor(const std::string& extension_id); - - // Creates a new PolicyValueStore for |extension_id|. This may open an - // existing database, or create a new one. This also sends the current policy - // for |extension_id| to the database. When |notify_if_changed| is true, - // a notification is sent with the changes between the current policy and the - // previously stored policy, if there are any. - // - // Since this is used on FILE but must retrieve the current policy, this - // method first posts GetInitialPolicy() to UI and then resumes in - // CreateStoreWithInitialPolicy(). If |continuation| is not null then it - // will be invoked after the store is created. - // - // CreateStoreFor() can be safely invoked from any method on the FILE thread. - // It posts to UI used |weak_this_on_ui_|, so that the task is dropped if - // ShutdownOnUI() has been invoked. Otherwise, GetInitialPolicy() executes - // on UI and can safely post CreateStoreWithInitialPolicy to FILE. - // CreateStoreWithInitialPolicy then guarantees that a store for - // |extension_id| exists or is created, and then executes the |continuation|; - // so when the |continuation| executes, a store for |extension_id| is - // guaranteed to exist. - void CreateStoreFor(const std::string& extension_id, - bool notify_if_changed, - const base::Closure& continuation); - - // Helper for CreateStoreFor, invoked on UI. - void GetInitialPolicy(const std::string& extension_id, - bool notify_if_changed, - const base::Closure& continuation); - - // Helper for CreateStoreFor, invoked on FILE. - void CreateStoreWithInitialPolicy(const std::string& extension_id, - bool notify_if_changed, - scoped_ptr policy, - const base::Closure& continuation); - - // Used to create a WeakPtr valid on the UI thread, so that FILE tasks can - // post back to UI. - base::WeakPtrFactory weak_factory_; - - // A WeakPtr to |this| that is valid on UI. This is used by tasks on the FILE - // thread to post back to UI. - base::WeakPtr weak_this_on_ui_; - - // The PolicyService that is observed for policy updates. Lives on UI. - policy::PolicyService* policy_service_; - - // The EventRouter is created before the SettingsFrontend (which owns the - // instance of this class), and the SettingsFrontend is also destroyed before - // the EventRouter is. |event_router_| is thus valid for the lifetime of this - // object, until ShutdownOnUI() is invoked. Lives on UI. - EventRouter* event_router_; - - // These live on the FILE thread. - scoped_refptr storage_factory_; - scoped_refptr observers_; - FilePath base_path_; - - // All the PolicyValueStores live on the FILE thread, and |store_map_| can be - // accessed only on the FILE thread as well. - PolicyValueStoreMap store_map_; - - DISALLOW_COPY_AND_ASSIGN(ManagedValueStoreCache); -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_STORAGE_MANAGED_VALUE_STORE_CACHE_H_ diff --git a/chrome/browser/extensions/api/storage/policy_value_store.cc b/chrome/browser/extensions/api/storage/policy_value_store.cc deleted file mode 100644 index 8e84994..0000000 --- a/chrome/browser/extensions/api/storage/policy_value_store.cc +++ /dev/null @@ -1,163 +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/api/storage/policy_value_store.h" - -#include "base/logging.h" -#include "base/values.h" -#include "chrome/browser/extensions/api/storage/settings_namespace.h" -#include "chrome/browser/policy/policy_map.h" -#include "chrome/browser/policy/policy_types.h" -#include "chrome/browser/value_store/value_store_change.h" -#include "content/public/browser/browser_thread.h" - -using content::BrowserThread; - -namespace extensions { - -namespace { - -const char kReadOnlyStoreErrorMessage[] = "This is a read-only store."; - -ValueStore::WriteResult WriteResultError() { - return ValueStore::MakeWriteResult(kReadOnlyStoreErrorMessage); -} - -} // namespace - -PolicyValueStore::PolicyValueStore( - const std::string& extension_id, - const scoped_refptr& observers, - scoped_ptr delegate) - : extension_id_(extension_id), - observers_(observers), - delegate_(delegate.Pass()) {} - -PolicyValueStore::~PolicyValueStore() {} - -void PolicyValueStore::SetCurrentPolicy(const policy::PolicyMap& policy, - bool notify_if_changed) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - // Convert |policy| to a dictionary value. Only include mandatory policies - // for now. - base::DictionaryValue current_policy; - for (policy::PolicyMap::const_iterator it = policy.begin(); - it != policy.end(); ++it) { - if (it->second.level == policy::POLICY_LEVEL_MANDATORY) { - current_policy.SetWithoutPathExpansion( - it->first, it->second.value->DeepCopy()); - } - } - - // Get the previous policies stored in the database. - // TODO(joaodasilva): it'd be better to have a less expensive way of - // determining which keys are currently stored, or of determining which keys - // must be removed. - base::DictionaryValue previous_policy; - ValueStore::ReadResult read_result = delegate_->Get(); - if (read_result->HasError()) { - LOG(WARNING) << "Failed to read managed settings for extension " - << extension_id_ << ": " << read_result->error(); - // Leave |previous_policy| empty, so that events are generated for every - // policy in |current_policy|. - } else { - read_result->settings()->Swap(&previous_policy); - } - - // Now get two lists of changes: changes after setting the current policies, - // and changes after removing old policies that aren't in |current_policy| - // anymore. - std::vector removed_keys; - for (base::DictionaryValue::Iterator it(previous_policy); - it.HasNext(); it.Advance()) { - if (!current_policy.HasKey(it.key())) - removed_keys.push_back(it.key()); - } - - ValueStoreChangeList changes; - - WriteResult result = delegate_->Remove(removed_keys); - if (!result->HasError()) { - changes.insert( - changes.end(), result->changes().begin(), result->changes().end()); - } - - // IGNORE_QUOTA because these settings aren't writable by the extension, and - // are configured by the domain administrator. - ValueStore::WriteOptions options = ValueStore::IGNORE_QUOTA; - result = delegate_->Set(options, current_policy); - if (!result->HasError()) { - changes.insert( - changes.end(), result->changes().begin(), result->changes().end()); - } - - if (!changes.empty() && notify_if_changed) { - observers_->Notify( - &SettingsObserver::OnSettingsChanged, - extension_id_, - settings_namespace::MANAGED, - ValueStoreChange::ToJson(changes)); - } -} - -void PolicyValueStore::DeleteStorage() { - // This is called from our owner, indicating that storage for this extension - // should be removed. - delegate_->Clear(); -} - -size_t PolicyValueStore::GetBytesInUse(const std::string& key) { - // LeveldbValueStore doesn't implement this; and the underlying database - // isn't acccessible to the extension in any case; from the extension's - // perspective this is a read-only store. - return 0; -} - -size_t PolicyValueStore::GetBytesInUse(const std::vector& keys) { - // See note above. - return 0; -} - -size_t PolicyValueStore::GetBytesInUse() { - // See note above. - return 0; -} - -ValueStore::ReadResult PolicyValueStore::Get(const std::string& key) { - return delegate_->Get(key); -} - -ValueStore::ReadResult PolicyValueStore::Get( - const std::vector& keys) { - return delegate_->Get(keys); -} - -ValueStore::ReadResult PolicyValueStore::Get() { - return delegate_->Get(); -} - -ValueStore::WriteResult PolicyValueStore::Set( - WriteOptions options, const std::string& key, const base::Value& value) { - return WriteResultError(); -} - -ValueStore::WriteResult PolicyValueStore::Set( - WriteOptions options, const base::DictionaryValue& settings) { - return WriteResultError(); -} - -ValueStore::WriteResult PolicyValueStore::Remove(const std::string& key) { - return WriteResultError(); -} - -ValueStore::WriteResult PolicyValueStore::Remove( - const std::vector& keys) { - return WriteResultError(); -} - -ValueStore::WriteResult PolicyValueStore::Clear() { - return WriteResultError(); -} - -} // namespace extensions diff --git a/chrome/browser/extensions/api/storage/policy_value_store.h b/chrome/browser/extensions/api/storage/policy_value_store.h deleted file mode 100644 index ea4f4da..0000000 --- a/chrome/browser/extensions/api/storage/policy_value_store.h +++ /dev/null @@ -1,75 +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_API_STORAGE_POLICY_VALUE_STORE_H_ -#define CHROME_BROWSER_EXTENSIONS_API_STORAGE_POLICY_VALUE_STORE_H_ - -#include -#include - -#include "base/compiler_specific.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "chrome/browser/extensions/api/storage/settings_observer.h" -#include "chrome/browser/value_store/value_store.h" - -namespace policy { -class PolicyMap; -} - -namespace extensions { - -// A ValueStore that is backed by another, persistent ValueStore, and stores -// the policies for a specific extension there. This ValueStore is used to -// run the function of the storage.managed namespace; it's read-only for the -// extension. The ManagedValueStoreCache sends updated policy to this store -// and manages its lifetime. -class PolicyValueStore : public ValueStore { - public: - PolicyValueStore(const std::string& extension_id, - const scoped_refptr& observers, - scoped_ptr delegate); - virtual ~PolicyValueStore(); - - // Stores |policy| in the persistent database represented by the |delegate_|. - // If |notify_if_changed| and |policy| differs from the previously persisted - // version, then a notification is sent to the |observers_| with a list of the - // changes detected. - void SetCurrentPolicy(const policy::PolicyMap& policy, - bool notify_if_changed); - - // Clears all the stored data and deletes the database. - void DeleteStorage(); - - // ValueStore implementation: - virtual size_t GetBytesInUse(const std::string& key) OVERRIDE; - virtual size_t GetBytesInUse(const std::vector& keys) OVERRIDE; - virtual size_t GetBytesInUse() OVERRIDE; - virtual ReadResult Get(const std::string& key) OVERRIDE; - virtual ReadResult Get(const std::vector& keys) OVERRIDE; - virtual ReadResult Get() OVERRIDE; - virtual WriteResult Set( - WriteOptions options, - const std::string& key, - const base::Value& value) OVERRIDE; - virtual WriteResult Set( - WriteOptions options, const base::DictionaryValue& values) OVERRIDE; - virtual WriteResult Remove(const std::string& key) OVERRIDE; - virtual WriteResult Remove(const std::vector& keys) OVERRIDE; - virtual WriteResult Clear() OVERRIDE; - - // For unit tests. - ValueStore* delegate() { return delegate_.get(); } - - private: - std::string extension_id_; - scoped_refptr observers_; - scoped_ptr delegate_; - - DISALLOW_COPY_AND_ASSIGN(PolicyValueStore); -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_STORAGE_POLICY_VALUE_STORE_H_ diff --git a/chrome/browser/extensions/api/storage/policy_value_store_unittest.cc b/chrome/browser/extensions/api/storage/policy_value_store_unittest.cc deleted file mode 100644 index 06f1ad9..0000000 --- a/chrome/browser/extensions/api/storage/policy_value_store_unittest.cc +++ /dev/null @@ -1,217 +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/api/storage/policy_value_store.h" - -#include "base/file_path.h" -#include "base/files/scoped_temp_dir.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop.h" -#include "chrome/browser/extensions/api/storage/settings_observer.h" -#include "chrome/browser/policy/policy_map.h" -#include "chrome/browser/value_store/leveldb_value_store.h" -#include "chrome/browser/value_store/value_store_unittest.h" -#include "content/public/test/test_browser_thread.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using testing::_; -using testing::Mock; - -namespace extensions { - -namespace { - -const char kTestExtensionId[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; - -class MockSettingsObserver : public SettingsObserver { - public: - MOCK_METHOD3(OnSettingsChanged, void( - const std::string& extension_id, - settings_namespace::Namespace settings_namespace, - const std::string& changes_json)); -}; - -// Extends PolicyValueStore by overriding the mutating methods, so that the -// Get() base implementation can be tested with the ValueStoreTest parameterized -// tests. -class MutablePolicyValueStore : public PolicyValueStore { - public: - explicit MutablePolicyValueStore(const FilePath& path) - : PolicyValueStore(kTestExtensionId, - make_scoped_refptr(new SettingsObserverList()), - scoped_ptr(new LeveldbValueStore(path))) {} - virtual ~MutablePolicyValueStore() {} - - virtual WriteResult Set( - WriteOptions options, - const std::string& key, - const base::Value& value) OVERRIDE { - return delegate()->Set(options, key, value); - } - - virtual WriteResult Set( - WriteOptions options, const base::DictionaryValue& values) OVERRIDE { - return delegate()->Set(options, values); - } - - virtual WriteResult Remove(const std::string& key) OVERRIDE { - return delegate()->Remove(key); - } - - virtual WriteResult Remove(const std::vector& keys) OVERRIDE { - return delegate()->Remove(keys); - } - - virtual WriteResult Clear() OVERRIDE { - return delegate()->Clear(); - } - - private: - DISALLOW_COPY_AND_ASSIGN(MutablePolicyValueStore); -}; - -ValueStore* Param(const FilePath& file_path) { - return new MutablePolicyValueStore(file_path); -} - -} // namespace - -INSTANTIATE_TEST_CASE_P( - PolicyValueStoreTest, - ValueStoreTest, - testing::Values(&Param)); - -class PolicyValueStoreTest : public testing::Test { - public: - PolicyValueStoreTest() - : file_thread_(content::BrowserThread::FILE, &loop_) {} - virtual ~PolicyValueStoreTest() {} - - virtual void SetUp() OVERRIDE { - ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir()); - observers_ = new SettingsObserverList(); - observers_->AddObserver(&observer_); - store_.reset(new PolicyValueStore( - kTestExtensionId, - observers_, - scoped_ptr( - new LeveldbValueStore(scoped_temp_dir_.path())))); - } - - virtual void TearDown() OVERRIDE { - observers_->RemoveObserver(&observer_); - store_.reset(); - } - - protected: - base::ScopedTempDir scoped_temp_dir_; - MessageLoop loop_; - content::TestBrowserThread file_thread_; - scoped_ptr store_; - MockSettingsObserver observer_; - scoped_refptr observers_; -}; - -TEST_F(PolicyValueStoreTest, DontProvideRecommendedPolicies) { - policy::PolicyMap policies; - base::FundamentalValue expected(123); - policies.Set("must", policy::POLICY_LEVEL_MANDATORY, - policy::POLICY_SCOPE_USER, expected.DeepCopy()); - policies.Set("may", policy::POLICY_LEVEL_RECOMMENDED, - policy::POLICY_SCOPE_USER, base::Value::CreateIntegerValue(456)); - store_->SetCurrentPolicy(policies, false); - ValueStore::ReadResult result = store_->Get(); - ASSERT_FALSE(result->HasError()); - EXPECT_EQ(1u, result->settings()->size()); - base::Value* value = NULL; - EXPECT_FALSE(result->settings()->Get("may", &value)); - EXPECT_TRUE(result->settings()->Get("must", &value)); - EXPECT_TRUE(base::Value::Equals(&expected, value)); -} - -TEST_F(PolicyValueStoreTest, ReadOnly) { - ValueStore::WriteOptions options = ValueStore::DEFAULTS; - - base::StringValue string_value("value"); - EXPECT_TRUE(store_->Set(options, "key", string_value)->HasError()); - - base::DictionaryValue dict; - dict.SetString("key", "value"); - EXPECT_TRUE(store_->Set(options, dict)->HasError()); - - EXPECT_TRUE(store_->Remove("key")->HasError()); - std::vector keys; - keys.push_back("key"); - EXPECT_TRUE(store_->Remove(keys)->HasError()); - EXPECT_TRUE(store_->Clear()->HasError()); -} - -TEST_F(PolicyValueStoreTest, NotifyOnChanges) { - policy::PolicyMap policies; - policies.Set("aaa", policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER, - base::Value::CreateStringValue("111")); - EXPECT_CALL(observer_, OnSettingsChanged(_, _, _)).Times(0); - // No notification when setting the initial policy. - store_->SetCurrentPolicy(policies, false); - loop_.RunUntilIdle(); - Mock::VerifyAndClearExpectations(&observer_); - - // And no notifications on changes when not asked for. - policies.Set("aaa", policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER, - base::Value::CreateStringValue("222")); - policies.Set("bbb", policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER, - base::Value::CreateStringValue("223")); - EXPECT_CALL(observer_, OnSettingsChanged(_, _, _)).Times(0); - store_->SetCurrentPolicy(policies, false); - loop_.RunUntilIdle(); - Mock::VerifyAndClearExpectations(&observer_); - - // Notify when new policies are added. - ValueStoreChangeList changes; - base::StringValue value("333"); - changes.push_back(ValueStoreChange("ccc", NULL, value.DeepCopy())); - policies.Set("ccc", policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER, - value.DeepCopy()); - EXPECT_CALL(observer_, OnSettingsChanged(kTestExtensionId, - settings_namespace::MANAGED, - ValueStoreChange::ToJson(changes))); - store_->SetCurrentPolicy(policies, true); - loop_.RunUntilIdle(); - Mock::VerifyAndClearExpectations(&observer_); - - // Notify when policies change. - changes.clear(); - base::StringValue new_value("444"); - changes.push_back( - ValueStoreChange("ccc", value.DeepCopy(), new_value.DeepCopy())); - policies.Set("ccc", policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER, - new_value.DeepCopy()); - EXPECT_CALL(observer_, OnSettingsChanged(kTestExtensionId, - settings_namespace::MANAGED, - ValueStoreChange::ToJson(changes))); - store_->SetCurrentPolicy(policies, true); - loop_.RunUntilIdle(); - Mock::VerifyAndClearExpectations(&observer_); - - // Notify when policies are removed. - changes.clear(); - changes.push_back(ValueStoreChange("ccc", new_value.DeepCopy(), NULL)); - policies.Erase("ccc"); - EXPECT_CALL(observer_, OnSettingsChanged(kTestExtensionId, - settings_namespace::MANAGED, - ValueStoreChange::ToJson(changes))); - store_->SetCurrentPolicy(policies, true); - loop_.RunUntilIdle(); - Mock::VerifyAndClearExpectations(&observer_); - - // Don't notify when there aren't changes. - EXPECT_CALL(observer_, OnSettingsChanged(_, _, _)).Times(0); - store_->SetCurrentPolicy(policies, true); - loop_.RunUntilIdle(); - Mock::VerifyAndClearExpectations(&observer_); -} - -} // namespace extensions diff --git a/chrome/browser/extensions/api/storage/setting_sync_data.cc b/chrome/browser/extensions/api/storage/setting_sync_data.cc deleted file mode 100644 index cb4fe79..0000000 --- a/chrome/browser/extensions/api/storage/setting_sync_data.cc +++ /dev/null @@ -1,102 +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/api/storage/setting_sync_data.h" - -#include "base/json/json_reader.h" -#include "base/json/json_writer.h" -#include "sync/api/sync_data.h" -#include "sync/protocol/app_setting_specifics.pb.h" -#include "sync/protocol/extension_setting_specifics.pb.h" -#include "sync/protocol/sync.pb.h" - -namespace extensions { - -SettingSyncData::SettingSyncData( - const syncer::SyncChange& sync_change) { - Init(sync_change.change_type(), sync_change.sync_data()); -} - -SettingSyncData::SettingSyncData( - const syncer::SyncData& sync_data) { - Init(syncer::SyncChange::ACTION_INVALID, sync_data); -} - -void SettingSyncData::Init( - syncer::SyncChange::SyncChangeType change_type, - const syncer::SyncData& sync_data) { - DCHECK(!internal_.get()); - sync_pb::EntitySpecifics specifics = sync_data.GetSpecifics(); - // The data must only be either extension or app specfics. - DCHECK_NE(specifics.has_extension_setting(), - specifics.has_app_setting()); - if (specifics.has_extension_setting()) { - InitFromExtensionSettingSpecifics( - change_type, - specifics.extension_setting()); - } else if (specifics.has_app_setting()) { - InitFromExtensionSettingSpecifics( - change_type, - specifics.app_setting().extension_setting()); - } -} - -void SettingSyncData::InitFromExtensionSettingSpecifics( - syncer::SyncChange::SyncChangeType change_type, - const sync_pb::ExtensionSettingSpecifics& specifics) { - DCHECK(!internal_.get()); - scoped_ptr value( - base::JSONReader::Read(specifics.value())); - if (!value.get()) { - LOG(WARNING) << "Specifics for " << specifics.extension_id() << "/" << - specifics.key() << " had bad JSON for value: " << specifics.value(); - value.reset(new DictionaryValue()); - } - internal_ = new Internal( - change_type, - specifics.extension_id(), - specifics.key(), - value.Pass()); -} - -SettingSyncData::SettingSyncData( - syncer::SyncChange::SyncChangeType change_type, - const std::string& extension_id, - const std::string& key, - scoped_ptr value) - : internal_(new Internal(change_type, extension_id, key, value.Pass())) {} - -SettingSyncData::~SettingSyncData() {} - -syncer::SyncChange::SyncChangeType SettingSyncData::change_type() const { - return internal_->change_type_; -} - -const std::string& SettingSyncData::extension_id() const { - return internal_->extension_id_; -} - -const std::string& SettingSyncData::key() const { - return internal_->key_; -} - -const Value& SettingSyncData::value() const { - return *internal_->value_; -} - -SettingSyncData::Internal::Internal( - syncer::SyncChange::SyncChangeType change_type, - const std::string& extension_id, - const std::string& key, - scoped_ptr value) - : change_type_(change_type), - extension_id_(extension_id), - key_(key), - value_(value.Pass()) { - DCHECK(value_.get()); -} - -SettingSyncData::Internal::~Internal() {} - -} // namespace extensions diff --git a/chrome/browser/extensions/api/storage/setting_sync_data.h b/chrome/browser/extensions/api/storage/setting_sync_data.h deleted file mode 100644 index 4cecb23..0000000 --- a/chrome/browser/extensions/api/storage/setting_sync_data.h +++ /dev/null @@ -1,91 +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_API_STORAGE_SETTING_SYNC_DATA_H_ -#define CHROME_BROWSER_EXTENSIONS_API_STORAGE_SETTING_SYNC_DATA_H_ - -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/values.h" -#include "sync/api/sync_change.h" - -namespace syncer { -class SyncData; -} - -namespace sync_pb { -class ExtensionSettingSpecifics; -} - -namespace extensions { - -// Container for data interpreted from sync data/changes for an extension or -// app setting. Safe and efficient to copy. -class SettingSyncData { - public: - // Creates from a sync change. - explicit SettingSyncData(const syncer::SyncChange& sync_change); - - // Creates from sync data. |change_type| will be ACTION_INVALID. - explicit SettingSyncData(const syncer::SyncData& sync_data); - - // Creates explicitly. - SettingSyncData( - syncer::SyncChange::SyncChangeType change_type, - const std::string& extension_id, - const std::string& key, - scoped_ptr value); - - ~SettingSyncData(); - - // Returns the type of the sync change; may be ACTION_INVALID. - syncer::SyncChange::SyncChangeType change_type() const; - - // Returns the extension id the setting is for. - const std::string& extension_id() const; - - // Returns the settings key. - const std::string& key() const; - - // Returns the value of the setting. - const Value& value() const; - - private: - // Ref-counted container for the data. - // TODO(kalman): Use browser_sync::Immutable. - class Internal : public base::RefCountedThreadSafe { - public: - Internal( - syncer::SyncChange::SyncChangeType change_type, - const std::string& extension_id, - const std::string& key, - scoped_ptr value); - - syncer::SyncChange::SyncChangeType change_type_; - std::string extension_id_; - std::string key_; - scoped_ptr value_; - - private: - friend class base::RefCountedThreadSafe; - ~Internal(); - }; - - // Initializes internal_ from sync data for an extension or app setting. - void Init(syncer::SyncChange::SyncChangeType change_type, - const syncer::SyncData& sync_data); - - // Initializes internal_ from extension specifics. - void InitFromExtensionSettingSpecifics( - syncer::SyncChange::SyncChangeType change_type, - const sync_pb::ExtensionSettingSpecifics& specifics); - - scoped_refptr internal_; -}; - -typedef std::vector SettingSyncDataList; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_STORAGE_SETTING_SYNC_DATA_H_ diff --git a/chrome/browser/extensions/api/storage/settings_apitest.cc b/chrome/browser/extensions/api/storage/settings_apitest.cc deleted file mode 100644 index a5646eb..0000000 --- a/chrome/browser/extensions/api/storage/settings_apitest.cc +++ /dev/null @@ -1,518 +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 "base/bind.h" -#include "base/json/json_writer.h" -#include "chrome/browser/extensions/api/storage/settings_frontend.h" -#include "chrome/browser/extensions/api/storage/settings_namespace.h" -#include "chrome/browser/extensions/api/storage/settings_sync_util.h" -#include "chrome/browser/extensions/extension_apitest.h" -#include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/extensions/extension_test_message_listener.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/common/extensions/value_builder.h" -#include "chrome/test/base/ui_test_utils.h" -#include "sync/api/sync_change.h" -#include "sync/api/sync_change_processor.h" -#include "sync/api/sync_error_factory.h" -#include "sync/api/sync_error_factory_mock.h" -#include "testing/gmock/include/gmock/gmock.h" - -#if defined(ENABLE_CONFIGURATION_POLICY) -#include "chrome/browser/policy/browser_policy_connector.h" -#include "chrome/browser/policy/mock_configuration_policy_provider.h" -#include "chrome/browser/policy/policy_bundle.h" -#include "chrome/browser/policy/policy_map.h" -#endif - -namespace extensions { - -using settings_namespace::FromString; -using settings_namespace::LOCAL; -using settings_namespace::MANAGED; -using settings_namespace::Namespace; -using settings_namespace::SYNC; -using settings_namespace::ToString; -using testing::Return; - -namespace { - -// TODO(kalman): test both EXTENSION_SETTINGS and APP_SETTINGS. -const syncer::ModelType kModelType = syncer::EXTENSION_SETTINGS; - -// The managed_storage extension has a key defined in its manifest, so that -// its extension ID is well-known and the policy system can push policies for -// the extension. -const char kManagedStorageExtensionId[] = "kjmkgkdkpedkejedfhmfcenooemhbpbo"; - -class NoopSyncChangeProcessor : public syncer::SyncChangeProcessor { - public: - virtual syncer::SyncError ProcessSyncChanges( - const tracked_objects::Location& from_here, - const syncer::SyncChangeList& change_list) OVERRIDE { - return syncer::SyncError(); - } - - virtual ~NoopSyncChangeProcessor() {}; -}; - -class SyncChangeProcessorDelegate : public syncer::SyncChangeProcessor { - public: - explicit SyncChangeProcessorDelegate(syncer::SyncChangeProcessor* recipient) - : recipient_(recipient) { - DCHECK(recipient_); - } - virtual ~SyncChangeProcessorDelegate() {} - - // syncer::SyncChangeProcessor implementation. - virtual syncer::SyncError ProcessSyncChanges( - const tracked_objects::Location& from_here, - const syncer::SyncChangeList& change_list) OVERRIDE { - return recipient_->ProcessSyncChanges(from_here, change_list); - } - - private: - // The recipient of all sync changes. - syncer::SyncChangeProcessor* recipient_; - - DISALLOW_COPY_AND_ASSIGN(SyncChangeProcessorDelegate); -}; - -} // namespace - -class ExtensionSettingsApiTest : public ExtensionApiTest { - protected: - virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { - ExtensionApiTest::SetUpInProcessBrowserTestFixture(); - -#if defined(ENABLE_CONFIGURATION_POLICY) - EXPECT_CALL(policy_provider_, IsInitializationComplete()) - .WillRepeatedly(Return(true)); - policy::BrowserPolicyConnector::SetPolicyProviderForTesting( - &policy_provider_); -#endif - } - - void ReplyWhenSatisfied( - Namespace settings_namespace, - const std::string& normal_action, - const std::string& incognito_action) { - MaybeLoadAndReplyWhenSatisfied( - settings_namespace, normal_action, incognito_action, NULL, false); - } - - const Extension* LoadAndReplyWhenSatisfied( - Namespace settings_namespace, - const std::string& normal_action, - const std::string& incognito_action, - const std::string& extension_dir) { - return MaybeLoadAndReplyWhenSatisfied( - settings_namespace, - normal_action, - incognito_action, - &extension_dir, - false); - } - - void FinalReplyWhenSatisfied( - Namespace settings_namespace, - const std::string& normal_action, - const std::string& incognito_action) { - MaybeLoadAndReplyWhenSatisfied( - settings_namespace, normal_action, incognito_action, NULL, true); - } - - void InitSync(syncer::SyncChangeProcessor* sync_processor) { - MessageLoop::current()->RunUntilIdle(); - InitSyncWithSyncableService( - sync_processor, - browser()->profile()->GetExtensionService()->settings_frontend()-> - GetBackendForSync(kModelType)); - } - - void SendChanges(const syncer::SyncChangeList& change_list) { - MessageLoop::current()->RunUntilIdle(); - SendChangesToSyncableService( - change_list, - browser()->profile()->GetExtensionService()->settings_frontend()-> - GetBackendForSync(kModelType)); - } - -#if defined(ENABLE_CONFIGURATION_POLICY) - void SetPolicies(const base::DictionaryValue& policies) { - scoped_ptr bundle(new policy::PolicyBundle()); - policy::PolicyMap& policy_map = bundle->Get( - policy::POLICY_DOMAIN_EXTENSIONS, kManagedStorageExtensionId); - policy_map.LoadFrom( - &policies, policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER); - policy_provider_.UpdatePolicy(bundle.Pass()); - } -#endif - - private: - const Extension* MaybeLoadAndReplyWhenSatisfied( - Namespace settings_namespace, - const std::string& normal_action, - const std::string& incognito_action, - // May be NULL to imply not loading the extension. - const std::string* extension_dir, - bool is_final_action) { - ExtensionTestMessageListener listener("waiting", true); - ExtensionTestMessageListener listener_incognito("waiting_incognito", true); - - // Only load the extension after the listeners have been set up, to avoid - // initialisation race conditions. - const Extension* extension = NULL; - if (extension_dir) { - extension = LoadExtensionIncognito( - test_data_dir_.AppendASCII("settings").AppendASCII(*extension_dir)); - EXPECT_TRUE(extension); - } - - EXPECT_TRUE(listener.WaitUntilSatisfied()); - EXPECT_TRUE(listener_incognito.WaitUntilSatisfied()); - - listener.Reply( - CreateMessage(settings_namespace, normal_action, is_final_action)); - listener_incognito.Reply( - CreateMessage(settings_namespace, incognito_action, is_final_action)); - return extension; - } - - std::string CreateMessage( - Namespace settings_namespace, - const std::string& action, - bool is_final_action) { - scoped_ptr message(new DictionaryValue()); - message->SetString("namespace", ToString(settings_namespace)); - message->SetString("action", action); - message->SetBoolean("isFinalAction", is_final_action); - std::string message_json; - base::JSONWriter::Write(message.get(), &message_json); - return message_json; - } - - void InitSyncWithSyncableService( - syncer::SyncChangeProcessor* sync_processor, - syncer::SyncableService* settings_service) { - EXPECT_FALSE(settings_service->MergeDataAndStartSyncing( - kModelType, - syncer::SyncDataList(), - scoped_ptr( - new SyncChangeProcessorDelegate(sync_processor)), - scoped_ptr( - new syncer::SyncErrorFactoryMock())).error().IsSet()); - } - - void SendChangesToSyncableService( - const syncer::SyncChangeList& change_list, - syncer::SyncableService* settings_service) { - EXPECT_FALSE( - settings_service->ProcessSyncChanges(FROM_HERE, change_list).IsSet()); - } - - protected: -#if defined(ENABLE_CONFIGURATION_POLICY) - policy::MockConfigurationPolicyProvider policy_provider_; -#endif -}; - -IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, SimpleTest) { - ASSERT_TRUE(RunExtensionTest("settings/simple_test")) << message_; -} - -// Structure of this test taken from IncognitoSplitMode. -// Note that only split-mode incognito is tested, because spanning mode -// incognito looks the same as normal mode when the only API activity comes -// from background pages. -IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, SplitModeIncognito) { - // We need 2 ResultCatchers because we'll be running the same test in both - // regular and incognito mode. - ResultCatcher catcher, catcher_incognito; - catcher.RestrictToProfile(browser()->profile()); - catcher_incognito.RestrictToProfile( - browser()->profile()->GetOffTheRecordProfile()); - - LoadAndReplyWhenSatisfied(SYNC, - "assertEmpty", "assertEmpty", "split_incognito"); - ReplyWhenSatisfied(SYNC, "noop", "setFoo"); - ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo"); - ReplyWhenSatisfied(SYNC, "clear", "noop"); - ReplyWhenSatisfied(SYNC, "assertEmpty", "assertEmpty"); - ReplyWhenSatisfied(SYNC, "setFoo", "noop"); - ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo"); - ReplyWhenSatisfied(SYNC, "noop", "removeFoo"); - FinalReplyWhenSatisfied(SYNC, "assertEmpty", "assertEmpty"); - - EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); - EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message(); -} - -IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, - OnChangedNotificationsBetweenBackgroundPages) { - // We need 2 ResultCatchers because we'll be running the same test in both - // regular and incognito mode. - ResultCatcher catcher, catcher_incognito; - catcher.RestrictToProfile(browser()->profile()); - catcher_incognito.RestrictToProfile( - browser()->profile()->GetOffTheRecordProfile()); - - LoadAndReplyWhenSatisfied(SYNC, - "assertNoNotifications", "assertNoNotifications", "split_incognito"); - ReplyWhenSatisfied(SYNC, "noop", "setFoo"); - ReplyWhenSatisfied(SYNC, - "assertAddFooNotification", "assertAddFooNotification"); - ReplyWhenSatisfied(SYNC, "clearNotifications", "clearNotifications"); - ReplyWhenSatisfied(SYNC, "removeFoo", "noop"); - FinalReplyWhenSatisfied(SYNC, - "assertDeleteFooNotification", "assertDeleteFooNotification"); - - EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); - EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message(); -} - -IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, - SyncAndLocalAreasAreSeparate) { - // We need 2 ResultCatchers because we'll be running the same test in both - // regular and incognito mode. - ResultCatcher catcher, catcher_incognito; - catcher.RestrictToProfile(browser()->profile()); - catcher_incognito.RestrictToProfile( - browser()->profile()->GetOffTheRecordProfile()); - - LoadAndReplyWhenSatisfied(SYNC, - "assertNoNotifications", "assertNoNotifications", "split_incognito"); - - ReplyWhenSatisfied(SYNC, "noop", "setFoo"); - ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo"); - ReplyWhenSatisfied(SYNC, - "assertAddFooNotification", "assertAddFooNotification"); - ReplyWhenSatisfied(LOCAL, "assertEmpty", "assertEmpty"); - ReplyWhenSatisfied(LOCAL, "assertNoNotifications", "assertNoNotifications"); - - ReplyWhenSatisfied(SYNC, "clearNotifications", "clearNotifications"); - - ReplyWhenSatisfied(LOCAL, "setFoo", "noop"); - ReplyWhenSatisfied(LOCAL, "assertFoo", "assertFoo"); - ReplyWhenSatisfied(LOCAL, - "assertAddFooNotification", "assertAddFooNotification"); - ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo"); - ReplyWhenSatisfied(SYNC, "assertNoNotifications", "assertNoNotifications"); - - ReplyWhenSatisfied(LOCAL, "clearNotifications", "clearNotifications"); - - ReplyWhenSatisfied(LOCAL, "noop", "removeFoo"); - ReplyWhenSatisfied(LOCAL, "assertEmpty", "assertEmpty"); - ReplyWhenSatisfied(LOCAL, - "assertDeleteFooNotification", "assertDeleteFooNotification"); - ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo"); - ReplyWhenSatisfied(SYNC, "assertNoNotifications", "assertNoNotifications"); - - ReplyWhenSatisfied(LOCAL, "clearNotifications", "clearNotifications"); - - ReplyWhenSatisfied(SYNC, "removeFoo", "noop"); - ReplyWhenSatisfied(SYNC, "assertEmpty", "assertEmpty"); - ReplyWhenSatisfied(SYNC, - "assertDeleteFooNotification", "assertDeleteFooNotification"); - ReplyWhenSatisfied(LOCAL, "assertNoNotifications", "assertNoNotifications"); - FinalReplyWhenSatisfied(LOCAL, "assertEmpty", "assertEmpty"); - - EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); - EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message(); -} - -// Disabled, see crbug.com/101110 -IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, - DISABLED_OnChangedNotificationsFromSync) { - // We need 2 ResultCatchers because we'll be running the same test in both - // regular and incognito mode. - ResultCatcher catcher, catcher_incognito; - catcher.RestrictToProfile(browser()->profile()); - catcher_incognito.RestrictToProfile( - browser()->profile()->GetOffTheRecordProfile()); - - const Extension* extension = - LoadAndReplyWhenSatisfied(SYNC, - "assertNoNotifications", "assertNoNotifications", "split_incognito"); - const std::string& extension_id = extension->id(); - - NoopSyncChangeProcessor sync_processor; - InitSync(&sync_processor); - - // Set "foo" to "bar" via sync. - syncer::SyncChangeList sync_changes; - StringValue bar("bar"); - sync_changes.push_back(settings_sync_util::CreateAdd( - extension_id, "foo", bar, kModelType)); - SendChanges(sync_changes); - - ReplyWhenSatisfied(SYNC, - "assertAddFooNotification", "assertAddFooNotification"); - ReplyWhenSatisfied(SYNC, "clearNotifications", "clearNotifications"); - - // Remove "foo" via sync. - sync_changes.clear(); - sync_changes.push_back(settings_sync_util::CreateDelete( - extension_id, "foo", kModelType)); - SendChanges(sync_changes); - - FinalReplyWhenSatisfied(SYNC, - "assertDeleteFooNotification", "assertDeleteFooNotification"); - - EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); - EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message(); -} - -// Disabled, see crbug.com/101110 -// -// TODO: boring test, already done in the unit tests. What we really should be -// be testing is that the areas don't overlap. -IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, - DISABLED_OnChangedNotificationsFromSyncNotSentToLocal) { - // We need 2 ResultCatchers because we'll be running the same test in both - // regular and incognito mode. - ResultCatcher catcher, catcher_incognito; - catcher.RestrictToProfile(browser()->profile()); - catcher_incognito.RestrictToProfile( - browser()->profile()->GetOffTheRecordProfile()); - - const Extension* extension = - LoadAndReplyWhenSatisfied(LOCAL, - "assertNoNotifications", "assertNoNotifications", "split_incognito"); - const std::string& extension_id = extension->id(); - - NoopSyncChangeProcessor sync_processor; - InitSync(&sync_processor); - - // Set "foo" to "bar" via sync. - syncer::SyncChangeList sync_changes; - StringValue bar("bar"); - sync_changes.push_back(settings_sync_util::CreateAdd( - extension_id, "foo", bar, kModelType)); - SendChanges(sync_changes); - - ReplyWhenSatisfied(LOCAL, "assertNoNotifications", "assertNoNotifications"); - - // Remove "foo" via sync. - sync_changes.clear(); - sync_changes.push_back(settings_sync_util::CreateDelete( - extension_id, "foo", kModelType)); - SendChanges(sync_changes); - - FinalReplyWhenSatisfied(LOCAL, - "assertNoNotifications", "assertNoNotifications"); - - EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); - EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message(); -} - -IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, IsStorageEnabled) { - SettingsFrontend* frontend = - browser()->profile()->GetExtensionService()->settings_frontend(); - EXPECT_TRUE(frontend->IsStorageEnabled(LOCAL)); - EXPECT_TRUE(frontend->IsStorageEnabled(SYNC)); - -#if defined(ENABLE_CONFIGURATION_POLICY) - EXPECT_TRUE(frontend->IsStorageEnabled(MANAGED)); -#else - EXPECT_FALSE(frontend->IsStorageEnabled(MANAGED)); -#endif -} - -#if defined(ENABLE_CONFIGURATION_POLICY) - -IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, ManagedStorage) { - // Set policies for the test extension. - scoped_ptr policy = extensions::DictionaryBuilder() - .Set("string-policy", "value") - .Set("int-policy", -123) - .Set("double-policy", 456e7) - .SetBoolean("boolean-policy", true) - .Set("list-policy", extensions::ListBuilder() - .Append("one") - .Append("two") - .Append("three")) - .Set("dict-policy", extensions::DictionaryBuilder() - .Set("list", extensions::ListBuilder() - .Append(extensions::DictionaryBuilder() - .Set("one", 1) - .Set("two", 2)) - .Append(extensions::DictionaryBuilder() - .Set("three", 3)))) - .Build(); - SetPolicies(*policy); - // Now run the extension. - ASSERT_TRUE(RunExtensionTest("settings/managed_storage")) << message_; -} - -IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, PRE_ManagedStorageEvents) { - ResultCatcher catcher; - - // This test starts without any test extensions installed. - EXPECT_FALSE(GetSingleLoadedExtension()); - message_.clear(); - - // Set policies for the test extension. - scoped_ptr policy = extensions::DictionaryBuilder() - .Set("constant-policy", "aaa") - .Set("changes-policy", "bbb") - .Set("deleted-policy", "ccc") - .Build(); - SetPolicies(*policy); - - ExtensionTestMessageListener ready_listener("ready", false); - // Load the extension to install the event listener. - const Extension* extension = LoadExtension( - test_data_dir_.AppendASCII("settings/managed_storage_events")); - ASSERT_TRUE(extension); - // Wait until the extension sends the "ready" message. - ASSERT_TRUE(ready_listener.WaitUntilSatisfied()); - - // Now change the policies and wait until the extension is done. - policy = extensions::DictionaryBuilder() - .Set("constant-policy", "aaa") - .Set("changes-policy", "ddd") - .Set("new-policy", "eee") - .Build(); - SetPolicies(*policy); - EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); -} - -// TODO(joaodasilva): This test times out on Vista. http://crbug.com/166261 -#if !defined(OS_WIN) -IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, ManagedStorageEvents) { - // This test runs after PRE_ManagedStorageEvents without having deleted the - // profile, so the extension is still around. While the browser restarted the - // policy went back to the empty default, and so the extension should receive - // the corresponding change events. - - ResultCatcher catcher; - - // Verify that the test extension is still installed. - const Extension* extension = GetSingleLoadedExtension(); - ASSERT_TRUE(extension); - EXPECT_EQ(kManagedStorageExtensionId, extension->id()); - - // Running the test again skips the onInstalled callback, and just triggers - // the onChanged notification. - EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); -} -#endif // !defined(OS_WIN) - -#endif // defined(ENABLE_CONFIGURATION_POLICY) - -IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, ManagedStorageDisabled) { - // Disable the 'managed' namespace. This is redundant when - // ENABLE_CONFIGURATION_POLICY is not defined. - SettingsFrontend* frontend = - browser()->profile()->GetExtensionService()->settings_frontend(); - frontend->DisableStorageForTesting(MANAGED); - EXPECT_FALSE(frontend->IsStorageEnabled(MANAGED)); - // Now run the extension. - ASSERT_TRUE(RunExtensionTest("settings/managed_storage_disabled")) - << message_; -} - -} // namespace extensions diff --git a/chrome/browser/extensions/api/storage/settings_backend.cc b/chrome/browser/extensions/api/storage/settings_backend.cc deleted file mode 100644 index 1d8a5d5..0000000 --- a/chrome/browser/extensions/api/storage/settings_backend.cc +++ /dev/null @@ -1,285 +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/api/storage/settings_backend.h" - -#include "base/file_util.h" -#include "base/logging.h" -#include "chrome/browser/extensions/api/storage/settings_sync_processor.h" -#include "chrome/browser/extensions/api/storage/settings_sync_util.h" -#include "chrome/browser/extensions/api/storage/syncable_settings_storage.h" -#include "content/public/browser/browser_thread.h" -#include "sync/api/sync_error_factory.h" - -using content::BrowserThread; - -namespace extensions { - -SettingsBackend::SettingsBackend( - const scoped_refptr& storage_factory, - const FilePath& base_path, - const SettingsStorageQuotaEnforcer::Limits& quota, - const scoped_refptr& observers) - : storage_factory_(storage_factory), - base_path_(base_path), - quota_(quota), - observers_(observers), - sync_type_(syncer::UNSPECIFIED) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); -} - -SettingsBackend::~SettingsBackend() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); -} - -ValueStore* SettingsBackend::GetStorage( - const std::string& extension_id) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - DictionaryValue empty; - return GetOrCreateStorageWithSyncData(extension_id, empty); -} - -SyncableSettingsStorage* SettingsBackend::GetOrCreateStorageWithSyncData( - const std::string& extension_id, const DictionaryValue& sync_data) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - - StorageObjMap::iterator maybe_storage = storage_objs_.find(extension_id); - if (maybe_storage != storage_objs_.end()) { - return maybe_storage->second.get(); - } - - ValueStore* storage = storage_factory_->Create(base_path_, extension_id); - CHECK(storage); - - // It's fine to create the quota enforcer underneath the sync layer, since - // sync will only go ahead if each underlying storage operation succeeds. - storage = new SettingsStorageQuotaEnforcer(quota_, storage); - - linked_ptr syncable_storage( - new SyncableSettingsStorage( - observers_, - extension_id, - storage)); - storage_objs_[extension_id] = syncable_storage; - - if (sync_processor_.get()) { - syncer::SyncError error = - syncable_storage->StartSyncing( - sync_data, - CreateSettingsSyncProcessor(extension_id).Pass()); - if (error.IsSet()) - syncable_storage.get()->StopSyncing(); - } - - return syncable_storage.get(); -} - -void SettingsBackend::DeleteStorage(const std::string& extension_id) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - - // Clear settings when the extension is uninstalled. Leveldb implementations - // will also delete the database from disk when the object is destroyed as a - // result of being removed from |storage_objs_|. - // - // TODO(kalman): always GetStorage here (rather than only clearing if it - // exists) since the storage area may have been unloaded, but we still want - // to clear the data from disk. - // However, this triggers http://crbug.com/111072. - StorageObjMap::iterator maybe_storage = storage_objs_.find(extension_id); - if (maybe_storage == storage_objs_.end()) - return; - maybe_storage->second->Clear(); - storage_objs_.erase(extension_id); -} - -std::set SettingsBackend::GetKnownExtensionIDs() const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - std::set result; - - // Storage areas can be in-memory as well as on disk. |storage_objs_| will - // contain all that are in-memory. - for (StorageObjMap::iterator it = storage_objs_.begin(); - it != storage_objs_.end(); ++it) { - result.insert(it->first); - } - - // Leveldb databases are directories inside base_path_. - file_util::FileEnumerator::FindInfo find_info; - file_util::FileEnumerator extension_dirs( - base_path_, false, file_util::FileEnumerator::DIRECTORIES); - while (!extension_dirs.Next().empty()) { - extension_dirs.GetFindInfo(&find_info); - FilePath extension_dir(file_util::FileEnumerator::GetFilename(find_info)); - DCHECK(!extension_dir.IsAbsolute()); - // Extension IDs are created as std::strings so they *should* be ASCII. - std::string maybe_as_ascii(extension_dir.MaybeAsASCII()); - if (!maybe_as_ascii.empty()) { - result.insert(maybe_as_ascii); - } - } - - return result; -} - -static void AddAllSyncData( - const std::string& extension_id, - const DictionaryValue& src, - syncer::ModelType type, - syncer::SyncDataList* dst) { - for (DictionaryValue::Iterator it(src); it.HasNext(); it.Advance()) { - dst->push_back(settings_sync_util::CreateData( - extension_id, it.key(), it.value(), type)); - } -} - -syncer::SyncDataList SettingsBackend::GetAllSyncData( - syncer::ModelType type) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - // Ignore the type, it's just for sanity checking; assume that whatever base - // path we're constructed with is correct for the sync type. - DCHECK(type == syncer::EXTENSION_SETTINGS || - type == syncer::APP_SETTINGS); - - // For all extensions, get all their settings. This has the effect - // of bringing in the entire state of extension settings in memory; sad. - syncer::SyncDataList all_sync_data; - std::set known_extension_ids(GetKnownExtensionIDs()); - - for (std::set::const_iterator it = known_extension_ids.begin(); - it != known_extension_ids.end(); ++it) { - ValueStore::ReadResult maybe_settings = GetStorage(*it)->Get(); - if (maybe_settings->HasError()) { - LOG(WARNING) << "Failed to get settings for " << *it << ": " << - maybe_settings->error(); - continue; - } - AddAllSyncData(*it, *maybe_settings->settings().get(), - type, &all_sync_data); - } - - return all_sync_data; -} - -syncer::SyncMergeResult SettingsBackend::MergeDataAndStartSyncing( - syncer::ModelType type, - const syncer::SyncDataList& initial_sync_data, - scoped_ptr sync_processor, - scoped_ptr sync_error_factory) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - DCHECK(type == syncer::EXTENSION_SETTINGS || - type == syncer::APP_SETTINGS); - DCHECK_EQ(sync_type_, syncer::UNSPECIFIED); - DCHECK(!sync_processor_.get()); - DCHECK(sync_processor.get()); - DCHECK(sync_error_factory.get()); - - sync_type_ = type; - sync_processor_ = sync_processor.Pass(); - sync_error_factory_ = sync_error_factory.Pass(); - - // Group the initial sync data by extension id. - std::map > grouped_sync_data; - for (syncer::SyncDataList::const_iterator it = initial_sync_data.begin(); - it != initial_sync_data.end(); ++it) { - SettingSyncData data(*it); - linked_ptr sync_data = - grouped_sync_data[data.extension_id()]; - if (!sync_data.get()) { - sync_data = linked_ptr(new DictionaryValue()); - grouped_sync_data[data.extension_id()] = sync_data; - } - DCHECK(!sync_data->HasKey(data.key())) << - "Duplicate settings for " << data.extension_id() << "/" << data.key(); - sync_data->SetWithoutPathExpansion(data.key(), data.value().DeepCopy()); - } - - // Start syncing all existing storage areas. Any storage areas created in - // the future will start being synced as part of the creation process. - for (StorageObjMap::iterator it = storage_objs_.begin(); - it != storage_objs_.end(); ++it) { - std::map >::iterator - maybe_sync_data = grouped_sync_data.find(it->first); - syncer::SyncError error; - if (maybe_sync_data != grouped_sync_data.end()) { - error = it->second->StartSyncing( - *maybe_sync_data->second, - CreateSettingsSyncProcessor(it->first).Pass()); - grouped_sync_data.erase(it->first); - } else { - DictionaryValue empty; - error = it->second->StartSyncing( - empty, - CreateSettingsSyncProcessor(it->first).Pass()); - } - if (error.IsSet()) - it->second->StopSyncing(); - } - - // Eagerly create and init the rest of the storage areas that have sync data. - // Under normal circumstances (i.e. not first-time sync) this will be all of - // them. - for (std::map >::iterator it = - grouped_sync_data.begin(); it != grouped_sync_data.end(); ++it) { - GetOrCreateStorageWithSyncData(it->first, *it->second); - } - - return syncer::SyncMergeResult(type); -} - -syncer::SyncError SettingsBackend::ProcessSyncChanges( - const tracked_objects::Location& from_here, - const syncer::SyncChangeList& sync_changes) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - DCHECK(sync_processor_.get()); - - // Group changes by extension, to pass all changes in a single method call. - std::map grouped_sync_data; - for (syncer::SyncChangeList::const_iterator it = sync_changes.begin(); - it != sync_changes.end(); ++it) { - SettingSyncData data(*it); - grouped_sync_data[data.extension_id()].push_back(data); - } - - // Create any storage areas that don't exist yet but have sync data. - DictionaryValue empty; - for (std::map::iterator - it = grouped_sync_data.begin(); it != grouped_sync_data.end(); ++it) { - SyncableSettingsStorage* storage = - GetOrCreateStorageWithSyncData(it->first, empty); - syncer::SyncError error = storage->ProcessSyncChanges(it->second); - if (error.IsSet()) - storage->StopSyncing(); - } - - return syncer::SyncError(); -} - -void SettingsBackend::StopSyncing(syncer::ModelType type) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - DCHECK(type == syncer::EXTENSION_SETTINGS || - type == syncer::APP_SETTINGS); - DCHECK(sync_type_ == type || sync_type_ == syncer::UNSPECIFIED); - - for (StorageObjMap::iterator it = storage_objs_.begin(); - it != storage_objs_.end(); ++it) { - // Some storage areas may have already stopped syncing if they had areas - // and syncing was disabled, but StopSyncing is safe to call multiple times. - it->second->StopSyncing(); - } - - sync_type_ = syncer::UNSPECIFIED; - sync_processor_.reset(); - sync_error_factory_.reset(); -} - -scoped_ptr SettingsBackend::CreateSettingsSyncProcessor( - const std::string& extension_id) const { - CHECK(sync_processor_.get()); - return scoped_ptr( - new SettingsSyncProcessor(extension_id, - sync_type_, - sync_processor_.get())); -} - -} // namespace extensions diff --git a/chrome/browser/extensions/api/storage/settings_backend.h b/chrome/browser/extensions/api/storage/settings_backend.h deleted file mode 100644 index 60073b2..0000000 --- a/chrome/browser/extensions/api/storage/settings_backend.h +++ /dev/null @@ -1,116 +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_API_STORAGE_SETTINGS_BACKEND_H_ -#define CHROME_BROWSER_EXTENSIONS_API_STORAGE_SETTINGS_BACKEND_H_ - -#include -#include -#include - -#include "base/compiler_specific.h" -#include "base/file_path.h" -#include "base/memory/linked_ptr.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "chrome/browser/extensions/api/storage/settings_observer.h" -#include "chrome/browser/extensions/api/storage/settings_storage_factory.h" -#include "chrome/browser/extensions/api/storage/settings_storage_quota_enforcer.h" -#include "sync/api/syncable_service.h" - -namespace syncer { -class SyncErrorFactory; -} - -namespace extensions { - -class SettingsSyncProcessor; -class SyncableSettingsStorage; - -// Manages ValueStore objects for extensions, including routing -// changes from sync to them. -// Lives entirely on the FILE thread. -class SettingsBackend : public syncer::SyncableService { - public: - // |storage_factory| is use to create leveldb storage areas. - // |base_path| is the base of the extension settings directory, so the - // databases will be at base_path/extension_id. - // |observers| is the list of observers to settings changes. - SettingsBackend( - const scoped_refptr& storage_factory, - const FilePath& base_path, - const SettingsStorageQuotaEnforcer::Limits& quota, - const scoped_refptr& observers); - - virtual ~SettingsBackend(); - - // Gets a weak reference to the storage area for |extension_id|. - // Must be run on the FILE thread. - ValueStore* GetStorage(const std::string& extension_id) const; - - // Deletes all setting data for an extension. Call on the FILE thread. - void DeleteStorage(const std::string& extension_id); - - // syncer::SyncableService implementation. - virtual syncer::SyncDataList GetAllSyncData( - syncer::ModelType type) const OVERRIDE; - virtual syncer::SyncMergeResult MergeDataAndStartSyncing( - syncer::ModelType type, - const syncer::SyncDataList& initial_sync_data, - scoped_ptr sync_processor, - scoped_ptr sync_error_factory) OVERRIDE; - virtual syncer::SyncError ProcessSyncChanges( - const tracked_objects::Location& from_here, - const syncer::SyncChangeList& change_list) OVERRIDE; - virtual void StopSyncing(syncer::ModelType type) OVERRIDE; - - private: - // Gets a weak reference to the storage area for a given extension, - // initializing sync with some initial data if sync enabled. - SyncableSettingsStorage* GetOrCreateStorageWithSyncData( - const std::string& extension_id, - const DictionaryValue& sync_data) const; - - // Gets all extension IDs known to extension settings. This may not be all - // installed extensions. - std::set GetKnownExtensionIDs() const; - - // Creates a new SettingsSyncProcessor for an extension. - scoped_ptr CreateSettingsSyncProcessor( - const std::string& extension_id) const; - - // The Factory to use for creating leveldb storage areas. - const scoped_refptr storage_factory_; - - // The base file path to create any leveldb databases at. - const FilePath base_path_; - - // Quota limits (see SettingsStorageQuotaEnforcer). - const SettingsStorageQuotaEnforcer::Limits quota_; - - // The list of observers to settings changes. - const scoped_refptr observers_; - - // A cache of ValueStore objects that have already been created. - // Ensure that there is only ever one created per extension. - typedef std::map > - StorageObjMap; - mutable StorageObjMap storage_objs_; - - // Current sync model type. Will be UNSPECIFIED if sync hasn't been enabled - // yet, and either SETTINGS or APP_SETTINGS if it has been. - syncer::ModelType sync_type_; - - // Current sync processor, if any. - scoped_ptr sync_processor_; - - // Current sync error handler if any. - scoped_ptr sync_error_factory_; - - DISALLOW_COPY_AND_ASSIGN(SettingsBackend); -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_STORAGE_SETTINGS_BACKEND_H_ diff --git a/chrome/browser/extensions/api/storage/settings_frontend.cc b/chrome/browser/extensions/api/storage/settings_frontend.cc deleted file mode 100644 index 240bc66..0000000 --- a/chrome/browser/extensions/api/storage/settings_frontend.cc +++ /dev/null @@ -1,218 +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/api/storage/settings_frontend.h" - -#include - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/file_path.h" -#include "base/json/json_reader.h" -#include "chrome/browser/extensions/api/storage/leveldb_settings_storage_factory.h" -#include "chrome/browser/extensions/api/storage/settings_backend.h" -#include "chrome/browser/extensions/api/storage/sync_or_local_value_store_cache.h" -#include "chrome/browser/extensions/event_names.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/profiles/profile.h" -#include "chrome/common/extensions/api/storage.h" -#include "content/public/browser/browser_thread.h" - -#if defined(ENABLE_CONFIGURATION_POLICY) -#include "chrome/browser/extensions/api/storage/managed_value_store_cache.h" -#endif - -using content::BrowserThread; - -namespace extensions { - -namespace { - -// Settings change Observer which forwards changes on to the extension -// processes for |profile| and its incognito partner if it exists. -class DefaultObserver : public SettingsObserver { - public: - explicit DefaultObserver(Profile* profile) : profile_(profile) {} - - // SettingsObserver implementation. - virtual void OnSettingsChanged( - const std::string& extension_id, - settings_namespace::Namespace settings_namespace, - const std::string& change_json) OVERRIDE { - // TODO(gdk): This is a temporary hack while the refactoring for - // string-based event payloads is removed. http://crbug.com/136045 - scoped_ptr args(new ListValue()); - args->Append(base::JSONReader::Read(change_json)); - args->Append(Value::CreateStringValue(settings_namespace::ToString( - settings_namespace))); - scoped_ptr event(new Event( - event_names::kOnSettingsChanged, args.Pass())); - ExtensionSystem::Get(profile_)->event_router()-> - DispatchEventToExtension(extension_id, event.Pass()); - } - - private: - Profile* const profile_; -}; - -SettingsStorageQuotaEnforcer::Limits GetLocalLimits() { - SettingsStorageQuotaEnforcer::Limits limits = { - static_cast(api::storage::local::QUOTA_BYTES), - std::numeric_limits::max(), - std::numeric_limits::max() - }; - return limits; -} - -SettingsStorageQuotaEnforcer::Limits GetSyncLimits() { - SettingsStorageQuotaEnforcer::Limits limits = { - static_cast(api::storage::sync::QUOTA_BYTES), - static_cast(api::storage::sync::QUOTA_BYTES_PER_ITEM), - static_cast(api::storage::sync::MAX_ITEMS) - }; - return limits; -} - -} // namespace - -// static -SettingsFrontend* SettingsFrontend::Create(Profile* profile) { - return new SettingsFrontend(new LeveldbSettingsStorageFactory(), profile); -} - -// static -SettingsFrontend* SettingsFrontend::Create( - const scoped_refptr& storage_factory, - Profile* profile) { - return new SettingsFrontend(storage_factory, profile); -} - -SettingsFrontend::SettingsFrontend( - const scoped_refptr& factory, Profile* profile) - : local_quota_limit_(GetLocalLimits()), - sync_quota_limit_(GetSyncLimits()), - profile_(profile), - observers_(new SettingsObserverList()), - profile_observer_(new DefaultObserver(profile)) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK(!profile->IsOffTheRecord()); - - observers_->AddObserver(profile_observer_.get()); - - const FilePath& profile_path = profile->GetPath(); - caches_[settings_namespace::LOCAL] = - new SyncOrLocalValueStoreCache( - settings_namespace::LOCAL, - factory, - local_quota_limit_, - observers_, - profile_path); - caches_[settings_namespace::SYNC] = - new SyncOrLocalValueStoreCache( - settings_namespace::SYNC, - factory, - sync_quota_limit_, - observers_, - profile_path); - -#if defined(ENABLE_CONFIGURATION_POLICY) - caches_[settings_namespace::MANAGED] = - new ManagedValueStoreCache( - profile->GetPolicyService(), - ExtensionSystem::Get(profile)->event_router(), - factory, - observers_, - profile_path); -#endif -} - -SettingsFrontend::~SettingsFrontend() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - observers_->RemoveObserver(profile_observer_.get()); - for (CacheMap::iterator it = caches_.begin(); it != caches_.end(); ++it) { - ValueStoreCache* cache = it->second; - cache->ShutdownOnUI(); - BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, cache); - } -} - -syncer::SyncableService* SettingsFrontend::GetBackendForSync( - syncer::ModelType type) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - CacheMap::const_iterator it = caches_.find(settings_namespace::SYNC); - DCHECK(it != caches_.end()); - const SyncOrLocalValueStoreCache* sync_cache = - static_cast(it->second); - switch (type) { - case syncer::APP_SETTINGS: - return sync_cache->GetAppBackend(); - case syncer::EXTENSION_SETTINGS: - return sync_cache->GetExtensionBackend(); - default: - NOTREACHED(); - return NULL; - } -} - -bool SettingsFrontend::IsStorageEnabled( - settings_namespace::Namespace settings_namespace) const { - return caches_.find(settings_namespace) != caches_.end(); -} - -void SettingsFrontend::RunWithStorage( - const std::string& extension_id, - settings_namespace::Namespace settings_namespace, - const ValueStoreCache::StorageCallback& callback) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - ValueStoreCache* cache = caches_[settings_namespace]; - CHECK(cache); - - // The |extension| has already been referenced earlier in the stack, so it - // can't be gone here. - // TODO(kalman): change RunWithStorage() to take a - // scoped_refptr instead. - scoped_refptr extension = - extensions::ExtensionSystem::Get(profile_)->extension_service()-> - GetExtensionById(extension_id, true); - CHECK(extension); - - BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, - base::Bind(&ValueStoreCache::RunWithValueStoreForExtension, - base::Unretained(cache), callback, extension)); -} - -void SettingsFrontend::DeleteStorageSoon( - const std::string& extension_id) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - for (CacheMap::iterator it = caches_.begin(); it != caches_.end(); ++it) { - ValueStoreCache* cache = it->second; - BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, - base::Bind(&ValueStoreCache::DeleteStorageSoon, - base::Unretained(cache), - extension_id)); - } -} - -scoped_refptr SettingsFrontend::GetObservers() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - return observers_; -} - -void SettingsFrontend::DisableStorageForTesting( - settings_namespace::Namespace settings_namespace) { - CacheMap::iterator it = caches_.find(settings_namespace); - if (it != caches_.end()) { - ValueStoreCache* cache = it->second; - cache->ShutdownOnUI(); - BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, cache); - caches_.erase(it); - } -} - -} // namespace extensions diff --git a/chrome/browser/extensions/api/storage/settings_frontend.h b/chrome/browser/extensions/api/storage/settings_frontend.h deleted file mode 100644 index 6c655bf..0000000 --- a/chrome/browser/extensions/api/storage/settings_frontend.h +++ /dev/null @@ -1,93 +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_API_STORAGE_SETTINGS_FRONTEND_H_ -#define CHROME_BROWSER_EXTENSIONS_API_STORAGE_SETTINGS_FRONTEND_H_ - -#include -#include - -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "chrome/browser/extensions/api/storage/settings_namespace.h" -#include "chrome/browser/extensions/api/storage/settings_observer.h" -#include "chrome/browser/extensions/api/storage/settings_storage_factory.h" -#include "chrome/browser/extensions/api/storage/settings_storage_quota_enforcer.h" -#include "chrome/browser/extensions/api/storage/value_store_cache.h" -#include "sync/api/syncable_service.h" - -class Profile; - -namespace extensions { - -// The component of extension settings which runs on the UI thread, as opposed -// to SettingsBackend which lives on the FILE thread. -// All public methods, must be called on the UI thread, with the exception of -// GetBackendForSync(), which must be called on the FILE thread. -class SettingsFrontend { - public: - // Creates with the default factory. - static SettingsFrontend* Create(Profile* profile); - - // Creates with a specific factory |storage_factory| (presumably for tests). - static SettingsFrontend* Create( - const scoped_refptr& storage_factory, - Profile* profile); - - virtual ~SettingsFrontend(); - - // Must only be called from the FILE thread. |type| should be either - // APP_SETTINGS or EXTENSION_SETTINGS. - syncer::SyncableService* GetBackendForSync(syncer::ModelType type) const; - - // Returns true if |settings_namespace| is a valid namespace. - bool IsStorageEnabled(settings_namespace::Namespace settings_namespace) const; - - // Runs |callback| with the storage area of the given |settings_namespace| - // for the |extension_id|. - void RunWithStorage( - const std::string& extension_id, - settings_namespace::Namespace settings_namespace, - const ValueStoreCache::StorageCallback& callback); - - // Deletes the settings for the given |extension_id|. - void DeleteStorageSoon(const std::string& extension_id); - - // Gets the thread-safe observer list. - scoped_refptr GetObservers(); - - void DisableStorageForTesting( - settings_namespace::Namespace settings_namespace); - - private: - typedef std::map CacheMap; - - SettingsFrontend( - const scoped_refptr& storage_factory, - Profile* profile); - - // The quota limit configurations for the local and sync areas, taken out of - // the schema in chrome/common/extensions/api/storage.json. - const SettingsStorageQuotaEnforcer::Limits local_quota_limit_; - const SettingsStorageQuotaEnforcer::Limits sync_quota_limit_; - - // The (non-incognito) Profile this Frontend belongs to. - Profile* const profile_; - - // List of observers to settings changes. - scoped_refptr observers_; - - // Observer for |profile_|. - scoped_ptr profile_observer_; - - // Maps a known namespace to its corresponding ValueStoreCache. The caches - // are owned by this object. - CacheMap caches_; - - DISALLOW_COPY_AND_ASSIGN(SettingsFrontend); -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_STORAGE_SETTINGS_FRONTEND_H_ diff --git a/chrome/browser/extensions/api/storage/settings_frontend_unittest.cc b/chrome/browser/extensions/api/storage/settings_frontend_unittest.cc deleted file mode 100644 index 07a473d..0000000 --- a/chrome/browser/extensions/api/storage/settings_frontend_unittest.cc +++ /dev/null @@ -1,288 +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 "base/bind.h" -#include "base/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop.h" -#include "base/stringprintf.h" -#include "chrome/browser/extensions/api/storage/leveldb_settings_storage_factory.h" -#include "chrome/browser/extensions/api/storage/settings_frontend.h" -#include "chrome/browser/extensions/api/storage/settings_namespace.h" -#include "chrome/browser/extensions/api/storage/settings_test_util.h" -#include "chrome/browser/value_store/value_store.h" -#include "chrome/common/chrome_notification_types.h" -#include "content/public/test/test_browser_thread.h" -#include "testing/gtest/include/gtest/gtest.h" - -using content::BrowserThread; - -namespace extensions { - -namespace settings = settings_namespace; -namespace util = settings_test_util; - -namespace { - -// To save typing ValueStore::DEFAULTS everywhere. -const ValueStore::WriteOptions DEFAULTS = ValueStore::DEFAULTS; - -// Creates a kilobyte of data. -scoped_ptr CreateKilobyte() { - std::string kilobyte_string; - for (int i = 0; i < 1024; ++i) { - kilobyte_string += "a"; - } - return scoped_ptr(Value::CreateStringValue(kilobyte_string)); -} - -// Creates a megabyte of data. -scoped_ptr CreateMegabyte() { - ListValue* megabyte = new ListValue(); - for (int i = 0; i < 1000; ++i) { - megabyte->Append(CreateKilobyte().release()); - } - return scoped_ptr(megabyte); -} - -} - -class ExtensionSettingsFrontendTest : public testing::Test { - public: - ExtensionSettingsFrontendTest() - : storage_factory_(new util::ScopedSettingsStorageFactory()), - ui_thread_(BrowserThread::UI, MessageLoop::current()), - file_thread_(BrowserThread::FILE, MessageLoop::current()) {} - - virtual void SetUp() OVERRIDE { - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - profile_.reset(new util::MockProfile(temp_dir_.path())); - ResetFrontend(); - } - - virtual void TearDown() OVERRIDE { - frontend_.reset(); - profile_.reset(); - // Execute any pending deletion tasks. - message_loop_.RunUntilIdle(); - } - - protected: - void ResetFrontend() { - storage_factory_->Reset(new LeveldbSettingsStorageFactory()); - frontend_.reset( - SettingsFrontend::Create(storage_factory_.get(), profile_.get())); - } - - base::ScopedTempDir temp_dir_; - scoped_ptr profile_; - scoped_ptr frontend_; - scoped_refptr storage_factory_; - - private: - MessageLoop message_loop_; - content::TestBrowserThread ui_thread_; - content::TestBrowserThread file_thread_; -}; - -// Get a semblance of coverage for both extension and app settings by -// alternating in each test. -// TODO(kalman): explicitly test the two interact correctly. - -TEST_F(ExtensionSettingsFrontendTest, SettingsPreservedAcrossReconstruction) { - const std::string id = "ext"; - ExtensionServiceInterface* esi = - extensions::ExtensionSystem::Get(profile_.get())->extension_service(); - static_cast(esi)-> - AddExtensionWithId(id, Extension::TYPE_EXTENSION); - - ValueStore* storage = util::GetStorage(id, frontend_.get()); - - // The correctness of Get/Set/Remove/Clear is tested elsewhere so no need to - // be too rigorous. - { - StringValue bar("bar"); - ValueStore::WriteResult result = storage->Set(DEFAULTS, "foo", bar); - ASSERT_FALSE(result->HasError()); - } - - { - ValueStore::ReadResult result = storage->Get(); - ASSERT_FALSE(result->HasError()); - EXPECT_FALSE(result->settings()->empty()); - } - - ResetFrontend(); - storage = util::GetStorage(id, frontend_.get()); - - { - ValueStore::ReadResult result = storage->Get(); - ASSERT_FALSE(result->HasError()); - EXPECT_FALSE(result->settings()->empty()); - } -} - -TEST_F(ExtensionSettingsFrontendTest, SettingsClearedOnUninstall) { - const std::string id = "ext"; - ExtensionServiceInterface* esi = - extensions::ExtensionSystem::Get(profile_.get())->extension_service(); - static_cast(esi)-> - AddExtensionWithId(id, Extension::TYPE_LEGACY_PACKAGED_APP); - - ValueStore* storage = util::GetStorage(id, frontend_.get()); - - { - StringValue bar("bar"); - ValueStore::WriteResult result = storage->Set(DEFAULTS, "foo", bar); - ASSERT_FALSE(result->HasError()); - } - - // This would be triggered by extension uninstall via a DataDeleter. - frontend_->DeleteStorageSoon(id); - MessageLoop::current()->RunUntilIdle(); - - // The storage area may no longer be valid post-uninstall, so re-request. - storage = util::GetStorage(id, frontend_.get()); - { - ValueStore::ReadResult result = storage->Get(); - ASSERT_FALSE(result->HasError()); - EXPECT_TRUE(result->settings()->empty()); - } -} - -TEST_F(ExtensionSettingsFrontendTest, LeveldbDatabaseDeletedFromDiskOnClear) { - const std::string id = "ext"; - ExtensionServiceInterface* esi = - extensions::ExtensionSystem::Get(profile_.get())->extension_service(); - static_cast(esi)-> - AddExtensionWithId(id, Extension::TYPE_EXTENSION); - - ValueStore* storage = util::GetStorage(id, frontend_.get()); - - { - StringValue bar("bar"); - ValueStore::WriteResult result = storage->Set(DEFAULTS, "foo", bar); - ASSERT_FALSE(result->HasError()); - EXPECT_TRUE(file_util::PathExists(temp_dir_.path())); - } - - // Should need to both clear the database and delete the frontend for the - // leveldb database to be deleted from disk. - { - ValueStore::WriteResult result = storage->Clear(); - ASSERT_FALSE(result->HasError()); - EXPECT_TRUE(file_util::PathExists(temp_dir_.path())); - } - - frontend_.reset(); - MessageLoop::current()->RunUntilIdle(); - // TODO(kalman): Figure out why this fails, despite appearing to work. - // Leaving this commented out rather than disabling the whole test so that the - // deletion code paths are at least exercised. - //EXPECT_FALSE(file_util::PathExists(temp_dir_.path())); -} - -#if defined(OS_WIN) -// Failing on vista dbg. http://crbug.com/111100, http://crbug.com/108724 -#define QuotaLimitsEnforcedCorrectlyForSyncAndLocal \ - DISABLED_QuotaLimitsEnforcedCorrectlyForSyncAndLocal -#endif -TEST_F(ExtensionSettingsFrontendTest, - QuotaLimitsEnforcedCorrectlyForSyncAndLocal) { - const std::string id = "ext"; - ExtensionServiceInterface* esi = - extensions::ExtensionSystem::Get(profile_.get())->extension_service(); - static_cast(esi)-> - AddExtensionWithId(id, Extension::TYPE_EXTENSION); - - ValueStore* sync_storage = - util::GetStorage(id, settings::SYNC, frontend_.get()); - ValueStore* local_storage = - util::GetStorage(id, settings::LOCAL, frontend_.get()); - - // Sync storage should run out after ~100K. - scoped_ptr kilobyte = CreateKilobyte(); - for (int i = 0; i < 100; ++i) { - sync_storage->Set( - ValueStore::DEFAULTS, base::StringPrintf("%d", i), *kilobyte); - } - - EXPECT_TRUE(sync_storage->Set( - ValueStore::DEFAULTS, "WillError", *kilobyte)->HasError()); - - // Local storage shouldn't run out after ~100K. - for (int i = 0; i < 100; ++i) { - local_storage->Set( - ValueStore::DEFAULTS, base::StringPrintf("%d", i), *kilobyte); - } - - EXPECT_FALSE(local_storage->Set( - ValueStore::DEFAULTS, "WontError", *kilobyte)->HasError()); - - // Local storage should run out after ~5MB. - scoped_ptr megabyte = CreateMegabyte(); - for (int i = 0; i < 5; ++i) { - local_storage->Set( - ValueStore::DEFAULTS, base::StringPrintf("%d", i), *megabyte); - } - - EXPECT_TRUE(local_storage->Set( - ValueStore::DEFAULTS, "WillError", *megabyte)->HasError()); -} - -// In other tests, we assume that the result of GetStorage is a pointer to the -// a Storage owned by a Frontend object, but for the unlimitedStorage case, this -// might not be true. So, write the tests in a "callback" style. -// We should really rewrite all tests to be asynchronous in this way. - -static void UnlimitedSyncStorageTestCallback(ValueStore* sync_storage) { - // Sync storage should still run out after ~100K; the unlimitedStorage - // permission can't apply to sync. - scoped_ptr kilobyte = CreateKilobyte(); - for (int i = 0; i < 100; ++i) { - sync_storage->Set( - ValueStore::DEFAULTS, base::StringPrintf("%d", i), *kilobyte); - } - - EXPECT_TRUE(sync_storage->Set( - ValueStore::DEFAULTS, "WillError", *kilobyte)->HasError()); -} - -static void UnlimitedLocalStorageTestCallback(ValueStore* local_storage) { - // Local storage should never run out. - scoped_ptr megabyte = CreateMegabyte(); - for (int i = 0; i < 7; ++i) { - local_storage->Set( - ValueStore::DEFAULTS, base::StringPrintf("%d", i), *megabyte); - } - - EXPECT_FALSE(local_storage->Set( - ValueStore::DEFAULTS, "WontError", *megabyte)->HasError()); -} - -#if defined(OS_WIN) -// Failing on vista dbg. http://crbug.com/111100, http://crbug.com/108724 -#define UnlimitedStorageForLocalButNotSync DISABLED_UnlimitedStorageForLocalButNotSync -#endif -TEST_F(ExtensionSettingsFrontendTest, - UnlimitedStorageForLocalButNotSync) { - const std::string id = "ext"; - std::set permissions; - permissions.insert("unlimitedStorage"); - ExtensionServiceInterface* esi = - extensions::ExtensionSystem::Get(profile_.get())->extension_service(); - static_cast(esi)-> - AddExtensionWithIdAndPermissions(id, Extension::TYPE_EXTENSION, - permissions); - - frontend_->RunWithStorage( - id, settings::SYNC, base::Bind(&UnlimitedSyncStorageTestCallback)); - frontend_->RunWithStorage( - id, settings::LOCAL, base::Bind(&UnlimitedLocalStorageTestCallback)); - - MessageLoop::current()->RunUntilIdle(); -} - -} // namespace extensions diff --git a/chrome/browser/extensions/api/storage/settings_namespace.cc b/chrome/browser/extensions/api/storage/settings_namespace.cc deleted file mode 100644 index 0b7ee3c..0000000 --- a/chrome/browser/extensions/api/storage/settings_namespace.cc +++ /dev/null @@ -1,42 +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/api/storage/settings_namespace.h" - -#include "base/logging.h" - -namespace extensions { - -namespace settings_namespace { - -namespace { -const char kLocalNamespace[] = "local"; -const char kSyncNamespace[] = "sync"; -const char kManagedNamespace[] = "managed"; -} // namespace - -std::string ToString(Namespace settings_namespace) { - switch (settings_namespace) { - case LOCAL: return kLocalNamespace; - case SYNC: return kSyncNamespace; - case MANAGED: return kManagedNamespace; - case INVALID: break; - } - NOTREACHED(); - return std::string(); -} - -Namespace FromString(const std::string& namespace_string) { - if (namespace_string == kLocalNamespace) - return LOCAL; - if (namespace_string == kSyncNamespace) - return SYNC; - if (namespace_string == kManagedNamespace) - return MANAGED; - return INVALID; -} - -} // namespace settings_namespace - -} // namespace extensions diff --git a/chrome/browser/extensions/api/storage/settings_namespace.h b/chrome/browser/extensions/api/storage/settings_namespace.h deleted file mode 100644 index 1047b45..0000000 --- a/chrome/browser/extensions/api/storage/settings_namespace.h +++ /dev/null @@ -1,34 +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_API_STORAGE_SETTINGS_NAMESPACE_H_ -#define CHROME_BROWSER_EXTENSIONS_API_STORAGE_SETTINGS_NAMESPACE_H_ - -#include - -namespace extensions { - -namespace settings_namespace { - -// The namespaces of the storage areas. -enum Namespace { - LOCAL, // "local" i.e. chrome.storage.local - SYNC, // "sync" i.e. chrome.storage.sync - MANAGED, // "managed" i.e. chrome.storage.managed - INVALID -}; - -// Converts a namespace to its string representation. -// Namespace must not be INVALID. -std::string ToString(Namespace settings_namespace); - -// Converts a string representation of a namespace to its namespace, or INVALID -// if the string doesn't map to one. -Namespace FromString(const std::string& ns_string); - -} // namespace settings_namespace - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_STORAGE_SETTINGS_NAMESPACE_H_ diff --git a/chrome/browser/extensions/api/storage/settings_observer.h b/chrome/browser/extensions/api/storage/settings_observer.h deleted file mode 100644 index 030713e..0000000 --- a/chrome/browser/extensions/api/storage/settings_observer.h +++ /dev/null @@ -1,30 +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_EXTENSIONS_API_STORAGE_SETTINGS_OBSERVER_H_ -#define CHROME_BROWSER_EXTENSIONS_API_STORAGE_SETTINGS_OBSERVER_H_ - -#include "base/observer_list_threadsafe.h" -#include "chrome/browser/extensions/api/storage/settings_namespace.h" - -namespace extensions { - -// Interface for classes that listen to changes to extension settings. -class SettingsObserver { - public: - // Called when a list of settings have changed for an extension. - virtual void OnSettingsChanged( - const std::string& extension_id, - settings_namespace::Namespace settings_namespace, - const std::string& changes_json) = 0; - - virtual ~SettingsObserver() {} -}; - -typedef ObserverListThreadSafe - SettingsObserverList; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_STORAGE_SETTINGS_OBSERVER_H_ diff --git a/chrome/browser/extensions/api/storage/settings_quota_unittest.cc b/chrome/browser/extensions/api/storage/settings_quota_unittest.cc deleted file mode 100644 index 8ed71e8..0000000 --- a/chrome/browser/extensions/api/storage/settings_quota_unittest.cc +++ /dev/null @@ -1,594 +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 "base/json/json_writer.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/values.h" -#include "chrome/browser/extensions/api/storage/settings_backend.h" -#include "chrome/browser/extensions/api/storage/settings_storage_quota_enforcer.h" -#include "chrome/browser/value_store/testing_value_store.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace extensions { - -// To save typing ValueStore::DEFAULTS/IGNORE_QUOTA everywhere. -const ValueStore::WriteOptions DEFAULTS = ValueStore::DEFAULTS; -const ValueStore::WriteOptions IGNORE_QUOTA = - ValueStore::IGNORE_QUOTA; - -class ExtensionSettingsQuotaTest : public testing::Test { - public: - ExtensionSettingsQuotaTest() - : byte_value_1_(Value::CreateIntegerValue(1)), - byte_value_16_(Value::CreateStringValue("sixteen bytes.")), - byte_value_256_(new ListValue()), - delegate_(new TestingValueStore()) { - for (int i = 1; i < 89; ++i) { - byte_value_256_->Append(Value::CreateIntegerValue(i)); - } - ValidateByteValues(); - } - - void ValidateByteValues() { - std::string validate_sizes; - base::JSONWriter::Write(byte_value_1_.get(), &validate_sizes); - ASSERT_EQ(1u, validate_sizes.size()); - base::JSONWriter::Write(byte_value_16_.get(), &validate_sizes); - ASSERT_EQ(16u, validate_sizes.size()); - base::JSONWriter::Write(byte_value_256_.get(), &validate_sizes); - ASSERT_EQ(256u, validate_sizes.size()); - } - - virtual void TearDown() OVERRIDE { - ASSERT_TRUE(storage_.get() != NULL); - } - - protected: - // Creates |storage_|. Must only be called once. - void CreateStorage( - size_t quota_bytes, size_t quota_bytes_per_item, size_t max_items) { - ASSERT_TRUE(storage_.get() == NULL); - SettingsStorageQuotaEnforcer::Limits limits = - { quota_bytes, quota_bytes_per_item, max_items }; - storage_.reset(new SettingsStorageQuotaEnforcer(limits, delegate_)); - } - - // Returns whether the settings in |storage_| and |delegate_| are the same as - // |settings|. - bool SettingsEqual(const DictionaryValue& settings) { - return settings.Equals(storage_->Get()->settings().get()) && - settings.Equals(delegate_->Get()->settings().get()); - } - - // Values with different serialized sizes. - scoped_ptr byte_value_1_; - scoped_ptr byte_value_16_; - scoped_ptr byte_value_256_; - - // Quota enforcing storage area being tested. - scoped_ptr storage_; - - // In-memory storage area being delegated to. Always owned by |storage_|. - TestingValueStore* delegate_; -}; - -TEST_F(ExtensionSettingsQuotaTest, ZeroQuotaBytes) { - DictionaryValue empty; - CreateStorage(0, UINT_MAX, UINT_MAX); - - EXPECT_TRUE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); - EXPECT_FALSE(storage_->Remove("a")->HasError()); - EXPECT_FALSE(storage_->Remove("b")->HasError()); - EXPECT_TRUE(SettingsEqual(empty)); -} - -TEST_F(ExtensionSettingsQuotaTest, KeySizeTakenIntoAccount) { - DictionaryValue empty; - CreateStorage(8u, UINT_MAX, UINT_MAX); - EXPECT_TRUE( - storage_->Set(DEFAULTS, "Really long key", *byte_value_1_)->HasError()); - EXPECT_TRUE(SettingsEqual(empty)); -} - -TEST_F(ExtensionSettingsQuotaTest, SmallByteQuota) { - DictionaryValue settings; - CreateStorage(8u, UINT_MAX, UINT_MAX); - - EXPECT_FALSE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); - settings.Set("a", byte_value_1_->DeepCopy()); - EXPECT_TRUE(SettingsEqual(settings)); - - EXPECT_TRUE(storage_->Set(DEFAULTS, "b", *byte_value_16_)->HasError()); - EXPECT_TRUE(storage_->Set(DEFAULTS, "c", *byte_value_256_)->HasError()); - EXPECT_TRUE(SettingsEqual(settings)); -} - -TEST_F(ExtensionSettingsQuotaTest, MediumByteQuota) { - DictionaryValue settings; - CreateStorage(40, UINT_MAX, UINT_MAX); - - DictionaryValue to_set; - to_set.Set("a", byte_value_1_->DeepCopy()); - to_set.Set("b", byte_value_16_->DeepCopy()); - EXPECT_FALSE(storage_->Set(DEFAULTS, to_set)->HasError()); - settings.Set("a", byte_value_1_->DeepCopy()); - settings.Set("b", byte_value_16_->DeepCopy()); - EXPECT_TRUE(SettingsEqual(settings)); - - // Should be able to set value to other under-quota value. - to_set.Set("a", byte_value_16_->DeepCopy()); - EXPECT_FALSE(storage_->Set(DEFAULTS, to_set)->HasError()); - settings.Set("a", byte_value_16_->DeepCopy()); - EXPECT_TRUE(SettingsEqual(settings)); - - EXPECT_TRUE(storage_->Set(DEFAULTS, "c", *byte_value_256_)->HasError()); - EXPECT_TRUE(SettingsEqual(settings)); -} - -TEST_F(ExtensionSettingsQuotaTest, ZeroMaxKeys) { - DictionaryValue empty; - CreateStorage(UINT_MAX, UINT_MAX, 0); - - EXPECT_TRUE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); - EXPECT_FALSE(storage_->Remove("a")->HasError()); - EXPECT_FALSE(storage_->Remove("b")->HasError()); - EXPECT_TRUE(SettingsEqual(empty)); -} - -TEST_F(ExtensionSettingsQuotaTest, SmallMaxKeys) { - DictionaryValue settings; - CreateStorage(UINT_MAX, UINT_MAX, 1); - - EXPECT_FALSE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); - settings.Set("a", byte_value_1_->DeepCopy()); - EXPECT_TRUE(SettingsEqual(settings)); - - // Should be able to set existing key to other value without going over quota. - EXPECT_FALSE(storage_->Set(DEFAULTS, "a", *byte_value_16_)->HasError()); - settings.Set("a", byte_value_16_->DeepCopy()); - EXPECT_TRUE(SettingsEqual(settings)); - - EXPECT_TRUE(storage_->Set(DEFAULTS, "b", *byte_value_16_)->HasError()); - EXPECT_TRUE(storage_->Set(DEFAULTS, "c", *byte_value_256_)->HasError()); - EXPECT_TRUE(SettingsEqual(settings)); -} - -TEST_F(ExtensionSettingsQuotaTest, MediumMaxKeys) { - DictionaryValue settings; - CreateStorage(UINT_MAX, UINT_MAX, 2); - - DictionaryValue to_set; - to_set.Set("a", byte_value_1_->DeepCopy()); - to_set.Set("b", byte_value_16_->DeepCopy()); - EXPECT_FALSE(storage_->Set(DEFAULTS, to_set)->HasError()); - settings.Set("a", byte_value_1_->DeepCopy()); - settings.Set("b", byte_value_16_->DeepCopy()); - EXPECT_TRUE(SettingsEqual(settings)); - - // Should be able to set existing keys to other values without going over - // quota. - to_set.Set("a", byte_value_16_->DeepCopy()); - EXPECT_FALSE(storage_->Set(DEFAULTS, to_set)->HasError()); - settings.Set("a", byte_value_16_->DeepCopy()); - EXPECT_TRUE(SettingsEqual(settings)); - - EXPECT_TRUE(storage_->Set(DEFAULTS, "c", *byte_value_256_)->HasError()); - EXPECT_TRUE(SettingsEqual(settings)); -} - -TEST_F(ExtensionSettingsQuotaTest, RemovingExistingSettings) { - DictionaryValue settings; - CreateStorage(266, UINT_MAX, 2); - - storage_->Set(DEFAULTS, "b", *byte_value_16_); - settings.Set("b", byte_value_16_->DeepCopy()); - // Not enough quota. - storage_->Set(DEFAULTS, "c", *byte_value_256_); - EXPECT_TRUE(SettingsEqual(settings)); - - // Try again with "b" removed, enough quota. - EXPECT_FALSE(storage_->Remove("b")->HasError()); - settings.Remove("b", NULL); - EXPECT_FALSE(storage_->Set(DEFAULTS, "c", *byte_value_256_)->HasError()); - settings.Set("c", byte_value_256_->DeepCopy()); - EXPECT_TRUE(SettingsEqual(settings)); - - // Enough byte quota but max keys not high enough. - EXPECT_FALSE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); - settings.Set("a", byte_value_1_->DeepCopy()); - EXPECT_TRUE(SettingsEqual(settings)); - - EXPECT_TRUE(storage_->Set(DEFAULTS, "b", *byte_value_1_)->HasError()); - EXPECT_TRUE(SettingsEqual(settings)); - - // Back under max keys. - EXPECT_FALSE(storage_->Remove("a")->HasError()); - settings.Remove("a", NULL); - EXPECT_FALSE(storage_->Set(DEFAULTS, "b", *byte_value_1_)->HasError()); - settings.Set("b", byte_value_1_->DeepCopy()); - EXPECT_TRUE(SettingsEqual(settings)); -} - -TEST_F(ExtensionSettingsQuotaTest, RemovingNonexistentSettings) { - DictionaryValue settings; - CreateStorage(36, UINT_MAX, 3); - - // Max out bytes. - DictionaryValue to_set; - to_set.Set("b1", byte_value_16_->DeepCopy()); - to_set.Set("b2", byte_value_16_->DeepCopy()); - storage_->Set(DEFAULTS, to_set); - settings.Set("b1", byte_value_16_->DeepCopy()); - settings.Set("b2", byte_value_16_->DeepCopy()); - EXPECT_TRUE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); - EXPECT_TRUE(SettingsEqual(settings)); - - // Remove some settings that don't exist. - std::vector to_remove; - to_remove.push_back("a1"); - to_remove.push_back("a2"); - EXPECT_FALSE(storage_->Remove(to_remove)->HasError()); - EXPECT_FALSE(storage_->Remove("b")->HasError()); - EXPECT_TRUE(SettingsEqual(settings)); - - // Still no quota. - EXPECT_TRUE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); - EXPECT_TRUE(SettingsEqual(settings)); - - // Max out key count. - to_set.Clear(); - to_set.Set("b1", byte_value_1_->DeepCopy()); - to_set.Set("b2", byte_value_1_->DeepCopy()); - storage_->Set(DEFAULTS, to_set); - settings.Set("b1", byte_value_1_->DeepCopy()); - settings.Set("b2", byte_value_1_->DeepCopy()); - storage_->Set(DEFAULTS, "b3", *byte_value_1_); - settings.Set("b3", byte_value_1_->DeepCopy()); - EXPECT_TRUE(SettingsEqual(settings)); - - // Remove some settings that don't exist. - to_remove.clear(); - to_remove.push_back("a1"); - to_remove.push_back("a2"); - EXPECT_FALSE(storage_->Remove(to_remove)->HasError()); - EXPECT_FALSE(storage_->Remove("b")->HasError()); - EXPECT_TRUE(SettingsEqual(settings)); - - // Still no quota. - EXPECT_TRUE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); - EXPECT_TRUE(SettingsEqual(settings)); -} - -TEST_F(ExtensionSettingsQuotaTest, Clear) { - DictionaryValue settings; - CreateStorage(40, UINT_MAX, 5); - - // Test running out of byte quota. - { - DictionaryValue to_set; - to_set.Set("a", byte_value_16_->DeepCopy()); - to_set.Set("b", byte_value_16_->DeepCopy()); - EXPECT_FALSE(storage_->Set(DEFAULTS, to_set)->HasError()); - EXPECT_TRUE(storage_->Set(DEFAULTS, "c", *byte_value_16_)->HasError()); - - EXPECT_FALSE(storage_->Clear()->HasError()); - - // (repeat) - EXPECT_FALSE(storage_->Set(DEFAULTS, to_set)->HasError()); - EXPECT_TRUE(storage_->Set(DEFAULTS, "c", *byte_value_16_)->HasError()); - } - - // Test reaching max keys. - storage_->Clear(); - { - DictionaryValue to_set; - to_set.Set("a", byte_value_1_->DeepCopy()); - to_set.Set("b", byte_value_1_->DeepCopy()); - to_set.Set("c", byte_value_1_->DeepCopy()); - to_set.Set("d", byte_value_1_->DeepCopy()); - to_set.Set("e", byte_value_1_->DeepCopy()); - EXPECT_FALSE(storage_->Set(DEFAULTS, to_set)->HasError()); - EXPECT_TRUE(storage_->Set(DEFAULTS, "f", *byte_value_1_)->HasError()); - - storage_->Clear(); - - // (repeat) - EXPECT_FALSE(storage_->Set(DEFAULTS, to_set)->HasError()); - EXPECT_TRUE(storage_->Set(DEFAULTS, "f", *byte_value_1_)->HasError()); - } -} - -TEST_F(ExtensionSettingsQuotaTest, ChangingUsedBytesWithSet) { - DictionaryValue settings; - CreateStorage(20, UINT_MAX, UINT_MAX); - - // Change a setting to make it go over quota. - storage_->Set(DEFAULTS, "a", *byte_value_16_); - settings.Set("a", byte_value_16_->DeepCopy()); - EXPECT_TRUE(SettingsEqual(settings)); - - EXPECT_TRUE(storage_->Set(DEFAULTS, "a", *byte_value_256_)->HasError()); - EXPECT_TRUE(SettingsEqual(settings)); - - // Change a setting to reduce usage and room for another setting. - EXPECT_TRUE(storage_->Set(DEFAULTS, "foobar", *byte_value_1_)->HasError()); - storage_->Set(DEFAULTS, "a", *byte_value_1_); - settings.Set("a", byte_value_1_->DeepCopy()); - - EXPECT_FALSE(storage_->Set(DEFAULTS, "foobar", *byte_value_1_)->HasError()); - settings.Set("foobar", byte_value_1_->DeepCopy()); - EXPECT_TRUE(SettingsEqual(settings)); -} - -TEST_F(ExtensionSettingsQuotaTest, SetsOnlyEntirelyCompletedWithByteQuota) { - DictionaryValue settings; - CreateStorage(40, UINT_MAX, UINT_MAX); - - storage_->Set(DEFAULTS, "a", *byte_value_16_); - settings.Set("a", byte_value_16_->DeepCopy()); - - // The entire change is over quota. - DictionaryValue to_set; - to_set.Set("b", byte_value_16_->DeepCopy()); - to_set.Set("c", byte_value_16_->DeepCopy()); - EXPECT_TRUE(storage_->Set(DEFAULTS, to_set)->HasError()); - EXPECT_TRUE(SettingsEqual(settings)); - - // The entire change is over quota, but quota reduced in existing key. - to_set.Set("a", byte_value_1_->DeepCopy()); - EXPECT_FALSE(storage_->Set(DEFAULTS, to_set)->HasError()); - settings.Set("a", byte_value_1_->DeepCopy()); - settings.Set("b", byte_value_16_->DeepCopy()); - settings.Set("c", byte_value_16_->DeepCopy()); - EXPECT_TRUE(SettingsEqual(settings)); -} - -TEST_F(ExtensionSettingsQuotaTest, SetsOnlyEntireCompletedWithMaxKeys) { - DictionaryValue settings; - CreateStorage(UINT_MAX, UINT_MAX, 2); - - storage_->Set(DEFAULTS, "a", *byte_value_1_); - settings.Set("a", byte_value_1_->DeepCopy()); - - DictionaryValue to_set; - to_set.Set("b", byte_value_16_->DeepCopy()); - to_set.Set("c", byte_value_16_->DeepCopy()); - EXPECT_TRUE(storage_->Set(DEFAULTS, to_set)->HasError()); - EXPECT_TRUE(SettingsEqual(settings)); -} - -TEST_F(ExtensionSettingsQuotaTest, WithInitialDataAndByteQuota) { - DictionaryValue settings; - delegate_->Set(DEFAULTS, "a", *byte_value_256_); - settings.Set("a", byte_value_256_->DeepCopy()); - - CreateStorage(280, UINT_MAX, UINT_MAX); - EXPECT_TRUE(SettingsEqual(settings)); - - // Add some data. - EXPECT_FALSE(storage_->Set(DEFAULTS, "b", *byte_value_16_)->HasError()); - settings.Set("b", byte_value_16_->DeepCopy()); - EXPECT_TRUE(SettingsEqual(settings)); - - // Not enough quota. - EXPECT_TRUE(storage_->Set(DEFAULTS, "c", *byte_value_16_)->HasError()); - EXPECT_TRUE(SettingsEqual(settings)); - - // Reduce usage of original setting so that "c" can fit. - EXPECT_FALSE(storage_->Set(DEFAULTS, "a", *byte_value_16_)->HasError()); - settings.Set("a", byte_value_16_->DeepCopy()); - EXPECT_TRUE(SettingsEqual(settings)); - - EXPECT_FALSE(storage_->Set(DEFAULTS, "c", *byte_value_16_)->HasError()); - settings.Set("c", byte_value_16_->DeepCopy()); - EXPECT_TRUE(SettingsEqual(settings)); - - // Remove to free up some more data. - EXPECT_TRUE(storage_->Set(DEFAULTS, "d", *byte_value_256_)->HasError()); - - std::vector to_remove; - to_remove.push_back("a"); - to_remove.push_back("b"); - storage_->Remove(to_remove); - settings.Remove("a", NULL); - settings.Remove("b", NULL); - EXPECT_TRUE(SettingsEqual(settings)); - - EXPECT_FALSE(storage_->Set(DEFAULTS, "d", *byte_value_256_)->HasError()); - settings.Set("d", byte_value_256_->DeepCopy()); - EXPECT_TRUE(SettingsEqual(settings)); -} - -TEST_F(ExtensionSettingsQuotaTest, WithInitialDataAndMaxKeys) { - DictionaryValue settings; - delegate_->Set(DEFAULTS, "a", *byte_value_1_); - settings.Set("a", byte_value_1_->DeepCopy()); - CreateStorage(UINT_MAX, UINT_MAX, 2); - - EXPECT_FALSE(storage_->Set(DEFAULTS, "b", *byte_value_1_)->HasError()); - settings.Set("b", byte_value_1_->DeepCopy()); - - EXPECT_TRUE(storage_->Set(DEFAULTS, "c", *byte_value_1_)->HasError()); - - EXPECT_TRUE(SettingsEqual(settings)); -} - -TEST_F(ExtensionSettingsQuotaTest, InitiallyOverByteQuota) { - DictionaryValue settings; - settings.Set("a", byte_value_16_->DeepCopy()); - settings.Set("b", byte_value_16_->DeepCopy()); - settings.Set("c", byte_value_16_->DeepCopy()); - delegate_->Set(DEFAULTS, settings); - - CreateStorage(40, UINT_MAX, UINT_MAX); - EXPECT_TRUE(SettingsEqual(settings)); - - EXPECT_TRUE(storage_->Set(DEFAULTS, "d", *byte_value_16_)->HasError()); - - // Take under quota by reducing size of an existing setting - EXPECT_FALSE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); - settings.Set("a", byte_value_1_->DeepCopy()); - EXPECT_TRUE(SettingsEqual(settings)); - - // Should be able set another small setting. - EXPECT_FALSE(storage_->Set(DEFAULTS, "d", *byte_value_1_)->HasError()); - settings.Set("d", byte_value_1_->DeepCopy()); - EXPECT_TRUE(SettingsEqual(settings)); -} - -TEST_F(ExtensionSettingsQuotaTest, InitiallyOverMaxKeys) { - DictionaryValue settings; - settings.Set("a", byte_value_16_->DeepCopy()); - settings.Set("b", byte_value_16_->DeepCopy()); - settings.Set("c", byte_value_16_->DeepCopy()); - delegate_->Set(DEFAULTS, settings); - - CreateStorage(UINT_MAX, UINT_MAX, 2); - EXPECT_TRUE(SettingsEqual(settings)); - - // Can't set either an existing or new setting. - EXPECT_TRUE(storage_->Set(DEFAULTS, "d", *byte_value_16_)->HasError()); - EXPECT_TRUE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); - EXPECT_TRUE(SettingsEqual(settings)); - - // Should be able after removing 2. - storage_->Remove("a"); - settings.Remove("a", NULL); - storage_->Remove("b"); - settings.Remove("b", NULL); - EXPECT_TRUE(SettingsEqual(settings)); - - EXPECT_FALSE(storage_->Set(DEFAULTS, "e", *byte_value_1_)->HasError()); - settings.Set("e", byte_value_1_->DeepCopy()); - EXPECT_TRUE(SettingsEqual(settings)); - - // Still can't set any. - EXPECT_TRUE(storage_->Set(DEFAULTS, "d", *byte_value_16_)->HasError()); - EXPECT_TRUE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); - EXPECT_TRUE(SettingsEqual(settings)); -} - -TEST_F(ExtensionSettingsQuotaTest, ZeroQuotaBytesPerSetting) { - DictionaryValue empty; - CreateStorage(UINT_MAX, 0, UINT_MAX); - - EXPECT_TRUE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); - EXPECT_FALSE(storage_->Remove("a")->HasError()); - EXPECT_FALSE(storage_->Remove("b")->HasError()); - EXPECT_TRUE(SettingsEqual(empty)); -} - -TEST_F(ExtensionSettingsQuotaTest, QuotaBytesPerSetting) { - DictionaryValue settings; - - CreateStorage(UINT_MAX, 20, UINT_MAX); - - EXPECT_FALSE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); - EXPECT_FALSE(storage_->Set(DEFAULTS, "a", *byte_value_16_)->HasError()); - settings.Set("a", byte_value_16_->DeepCopy()); - EXPECT_TRUE(storage_->Set(DEFAULTS, "a", *byte_value_256_)->HasError()); - - EXPECT_FALSE(storage_->Set(DEFAULTS, "b", *byte_value_1_)->HasError()); - EXPECT_FALSE(storage_->Set(DEFAULTS, "b", *byte_value_16_)->HasError()); - settings.Set("b", byte_value_16_->DeepCopy()); - EXPECT_TRUE(storage_->Set(DEFAULTS, "b", *byte_value_256_)->HasError()); - - EXPECT_TRUE(SettingsEqual(settings)); -} - -TEST_F(ExtensionSettingsQuotaTest, QuotaBytesPerSettingWithInitialSettings) { - DictionaryValue settings; - - delegate_->Set(DEFAULTS, "a", *byte_value_1_); - delegate_->Set(DEFAULTS, "b", *byte_value_16_); - delegate_->Set(DEFAULTS, "c", *byte_value_256_); - CreateStorage(UINT_MAX, 20, UINT_MAX); - - EXPECT_FALSE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); - EXPECT_FALSE(storage_->Set(DEFAULTS, "a", *byte_value_16_)->HasError()); - settings.Set("a", byte_value_16_->DeepCopy()); - EXPECT_TRUE(storage_->Set(DEFAULTS, "a", *byte_value_256_)->HasError()); - - EXPECT_FALSE(storage_->Set(DEFAULTS, "b", *byte_value_1_)->HasError()); - EXPECT_FALSE(storage_->Set(DEFAULTS, "b", *byte_value_16_)->HasError()); - settings.Set("b", byte_value_16_->DeepCopy()); - EXPECT_TRUE(storage_->Set(DEFAULTS, "b", *byte_value_256_)->HasError()); - - EXPECT_FALSE(storage_->Set(DEFAULTS, "c", *byte_value_1_)->HasError()); - EXPECT_FALSE(storage_->Set(DEFAULTS, "c", *byte_value_16_)->HasError()); - settings.Set("c", byte_value_16_->DeepCopy()); - EXPECT_TRUE(storage_->Set(DEFAULTS, "c", *byte_value_256_)->HasError()); - - EXPECT_TRUE(SettingsEqual(settings)); -} - -TEST_F(ExtensionSettingsQuotaTest, - QuotaBytesPerSettingWithInitialSettingsForced) { - // This is a lazy test to make sure IGNORE_QUOTA lets through changes: the - // test above copied, but using IGNORE_QUOTA and asserting nothing is ever - // rejected... - DictionaryValue settings; - - delegate_->Set(DEFAULTS, "a", *byte_value_1_); - delegate_->Set(DEFAULTS, "b", *byte_value_16_); - delegate_->Set(DEFAULTS, "c", *byte_value_256_); - CreateStorage(UINT_MAX, 20, UINT_MAX); - - EXPECT_FALSE(storage_->Set(IGNORE_QUOTA, "a", *byte_value_1_)->HasError()); - EXPECT_FALSE(storage_->Set(IGNORE_QUOTA, "a", *byte_value_16_)->HasError()); - EXPECT_FALSE(storage_->Set(IGNORE_QUOTA, "a", *byte_value_256_)->HasError()); - settings.Set("a", byte_value_256_->DeepCopy()); - - EXPECT_FALSE(storage_->Set(IGNORE_QUOTA, "b", *byte_value_1_)->HasError()); - EXPECT_FALSE(storage_->Set(IGNORE_QUOTA, "b", *byte_value_16_)->HasError()); - EXPECT_FALSE(storage_->Set(IGNORE_QUOTA, "b", *byte_value_256_)->HasError()); - settings.Set("b", byte_value_256_->DeepCopy()); - - EXPECT_FALSE(storage_->Set(IGNORE_QUOTA, "c", *byte_value_1_)->HasError()); - EXPECT_FALSE(storage_->Set(IGNORE_QUOTA, "c", *byte_value_16_)->HasError()); - settings.Set("c", byte_value_16_->DeepCopy()); - - // ... except the last. Make sure it can still fail. - EXPECT_TRUE(storage_->Set(DEFAULTS, "c", *byte_value_256_)->HasError()); - - EXPECT_TRUE(SettingsEqual(settings)); -} - -TEST_F(ExtensionSettingsQuotaTest, GetBytesInUse) { - // Just testing GetBytesInUse, no need for a quota. - CreateStorage(UINT_MAX, UINT_MAX, UINT_MAX); - - std::vector ab; - ab.push_back("a"); - ab.push_back("b"); - - EXPECT_EQ(0u, storage_->GetBytesInUse()); - EXPECT_EQ(0u, storage_->GetBytesInUse("a")); - EXPECT_EQ(0u, storage_->GetBytesInUse("b")); - EXPECT_EQ(0u, storage_->GetBytesInUse(ab)); - - storage_->Set(DEFAULTS, "a", *byte_value_1_); - - EXPECT_EQ(2u, storage_->GetBytesInUse()); - EXPECT_EQ(2u, storage_->GetBytesInUse("a")); - EXPECT_EQ(0u, storage_->GetBytesInUse("b")); - EXPECT_EQ(2u, storage_->GetBytesInUse(ab)); - - storage_->Set(DEFAULTS, "b", *byte_value_1_); - - EXPECT_EQ(4u, storage_->GetBytesInUse()); - EXPECT_EQ(2u, storage_->GetBytesInUse("a")); - EXPECT_EQ(2u, storage_->GetBytesInUse("b")); - EXPECT_EQ(4u, storage_->GetBytesInUse(ab)); - - storage_->Set(DEFAULTS, "c", *byte_value_1_); - - EXPECT_EQ(6u, storage_->GetBytesInUse()); - EXPECT_EQ(2u, storage_->GetBytesInUse("a")); - EXPECT_EQ(2u, storage_->GetBytesInUse("b")); - EXPECT_EQ(4u, storage_->GetBytesInUse(ab)); -} - -} // namespace extensions diff --git a/chrome/browser/extensions/api/storage/settings_storage_factory.h b/chrome/browser/extensions/api/storage/settings_storage_factory.h deleted file mode 100644 index 4fce4f6..0000000 --- a/chrome/browser/extensions/api/storage/settings_storage_factory.h +++ /dev/null @@ -1,36 +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_API_STORAGE_SETTINGS_STORAGE_FACTORY_H_ -#define CHROME_BROWSER_EXTENSIONS_API_STORAGE_SETTINGS_STORAGE_FACTORY_H_ - -#include - -#include "base/file_path.h" -#include "base/memory/ref_counted.h" - -class ValueStore; - -namespace extensions { - -// Factory for creating SettingStorage instances. -// -// Refcouted because it's just too messy to distribute these objects between -// SettingsBackend instances any other way. -class SettingsStorageFactory - : public base::RefCountedThreadSafe { - public: - // Creates a new ValueStore area for an extension under |base_path|. - // Return NULL to indicate failure. Must be called on the FILE thread. - virtual ValueStore* Create(const FilePath& base_path, - const std::string& extension_id) = 0; - - protected: - friend class base::RefCountedThreadSafe; - virtual ~SettingsStorageFactory() {} -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_STORAGE_SETTINGS_STORAGE_FACTORY_H_ diff --git a/chrome/browser/extensions/api/storage/settings_storage_quota_enforcer.cc b/chrome/browser/extensions/api/storage/settings_storage_quota_enforcer.cc deleted file mode 100644 index cc48ec7..0000000 --- a/chrome/browser/extensions/api/storage/settings_storage_quota_enforcer.cc +++ /dev/null @@ -1,235 +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/api/storage/settings_storage_quota_enforcer.h" - -#include "base/bind.h" -#include "base/json/json_writer.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop.h" -#include "base/metrics/histogram.h" -#include "chrome/common/extensions/api/extension_api.h" -#include "extensions/common/error_utils.h" - -namespace extensions { - -namespace { - -const char* kQuotaExceededError = "* quota exceeded."; - -// Resources there are a quota for. -enum Resource { - QUOTA_BYTES, - QUOTA_BYTES_PER_ITEM, - MAX_ITEMS -}; - -// Allocates a setting in a record of total and per-setting usage. -void Allocate( - const std::string& key, - const Value& value, - size_t* used_total, - std::map* used_per_setting) { - // Calculate the setting size based on its JSON serialization size. - // TODO(kalman): Does this work with different encodings? - // TODO(kalman): This is duplicating work that the leveldb delegate - // implementation is about to do, and it would be nice to avoid this. - std::string value_as_json; - base::JSONWriter::Write(&value, &value_as_json); - size_t new_size = key.size() + value_as_json.size(); - size_t existing_size = (*used_per_setting)[key]; - - *used_total += (new_size - existing_size); - (*used_per_setting)[key] = new_size; -} - -// Frees the allocation of a setting in a record of total and per-setting usage. -void Free( - size_t* used_total, - std::map* used_per_setting, - const std::string& key) { - *used_total -= (*used_per_setting)[key]; - used_per_setting->erase(key); -} - -// Returns an error result and logs the quota exceeded to UMA. -ValueStore::WriteResult QuotaExceededFor(Resource resource) { - std::string name; - switch (resource) { - case QUOTA_BYTES: - name = "QUOTA_BYTES"; - UMA_HISTOGRAM_COUNTS_100( - "Extensions.SettingsQuotaExceeded.TotalBytes", 1); - break; - case QUOTA_BYTES_PER_ITEM: - name = "QUOTA_BYTES_PER_ITEM"; - UMA_HISTOGRAM_COUNTS_100( - "Extensions.SettingsQuotaExceeded.BytesPerSetting", 1); - break; - case MAX_ITEMS: - name = "MAX_ITEMS"; - UMA_HISTOGRAM_COUNTS_100( - "Extensions.SettingsQuotaExceeded.KeyCount", 1); - break; - default: - NOTREACHED(); - } - return ValueStore::MakeWriteResult( - ErrorUtils::FormatErrorMessage(kQuotaExceededError, name)); -} - -} // namespace - -SettingsStorageQuotaEnforcer::SettingsStorageQuotaEnforcer( - const Limits& limits, ValueStore* delegate) - : limits_(limits), delegate_(delegate), used_total_(0) { - ReadResult maybe_settings = delegate_->Get(); - if (maybe_settings->HasError()) { - LOG(WARNING) << "Failed to get initial settings for quota: " << - maybe_settings->error(); - return; - } - - for (DictionaryValue::Iterator it(*maybe_settings->settings().get()); - it.HasNext(); it.Advance()) { - Allocate( - it.key(), it.value(), &used_total_, &used_per_setting_); - } -} - -SettingsStorageQuotaEnforcer::~SettingsStorageQuotaEnforcer() {} - -size_t SettingsStorageQuotaEnforcer::GetBytesInUse(const std::string& key) { - std::map::iterator maybe_used = - used_per_setting_.find(key); - return maybe_used == used_per_setting_.end() ? 0u : maybe_used->second; -} - -size_t SettingsStorageQuotaEnforcer::GetBytesInUse( - const std::vector& keys) { - size_t used = 0; - for (std::vector::const_iterator it = keys.begin(); - it != keys.end(); ++it) { - used += GetBytesInUse(*it); - } - return used; -} - -size_t SettingsStorageQuotaEnforcer::GetBytesInUse() { - // All ValueStore implementations rely on GetBytesInUse being - // implemented here. - return used_total_; -} - -ValueStore::ReadResult SettingsStorageQuotaEnforcer::Get( - const std::string& key) { - return delegate_->Get(key); -} - -ValueStore::ReadResult SettingsStorageQuotaEnforcer::Get( - const std::vector& keys) { - return delegate_->Get(keys); -} - -ValueStore::ReadResult SettingsStorageQuotaEnforcer::Get() { - return delegate_->Get(); -} - -ValueStore::WriteResult SettingsStorageQuotaEnforcer::Set( - WriteOptions options, const std::string& key, const Value& value) { - size_t new_used_total = used_total_; - std::map new_used_per_setting = used_per_setting_; - Allocate(key, value, &new_used_total, &new_used_per_setting); - - if (!(options & IGNORE_QUOTA)) { - if (new_used_total > limits_.quota_bytes) { - return QuotaExceededFor(QUOTA_BYTES); - } - if (new_used_per_setting[key] > limits_.quota_bytes_per_item) { - return QuotaExceededFor(QUOTA_BYTES_PER_ITEM); - } - if (new_used_per_setting.size() > limits_.max_items) { - return QuotaExceededFor(MAX_ITEMS); - } - } - - WriteResult result = delegate_->Set(options, key, value); - if (result->HasError()) { - return result.Pass(); - } - - used_total_ = new_used_total; - used_per_setting_.swap(new_used_per_setting); - return result.Pass(); -} - -ValueStore::WriteResult SettingsStorageQuotaEnforcer::Set( - WriteOptions options, const DictionaryValue& values) { - size_t new_used_total = used_total_; - std::map new_used_per_setting = used_per_setting_; - for (DictionaryValue::Iterator it(values); it.HasNext(); it.Advance()) { - Allocate(it.key(), it.value(), &new_used_total, &new_used_per_setting); - - if (!(options & IGNORE_QUOTA) && - new_used_per_setting[it.key()] > limits_.quota_bytes_per_item) { - return QuotaExceededFor(QUOTA_BYTES_PER_ITEM); - } - } - - if (!(options & IGNORE_QUOTA)) { - if (new_used_total > limits_.quota_bytes) { - return QuotaExceededFor(QUOTA_BYTES); - } - if (new_used_per_setting.size() > limits_.max_items) { - return QuotaExceededFor(MAX_ITEMS); - } - } - - WriteResult result = delegate_->Set(options, values); - if (result->HasError()) { - return result.Pass(); - } - - used_total_ = new_used_total; - used_per_setting_ = new_used_per_setting; - return result.Pass(); -} - -ValueStore::WriteResult SettingsStorageQuotaEnforcer::Remove( - const std::string& key) { - WriteResult result = delegate_->Remove(key); - if (result->HasError()) { - return result.Pass(); - } - Free(&used_total_, &used_per_setting_, key); - return result.Pass(); -} - -ValueStore::WriteResult SettingsStorageQuotaEnforcer::Remove( - const std::vector& keys) { - WriteResult result = delegate_->Remove(keys); - if (result->HasError()) { - return result.Pass(); - } - - for (std::vector::const_iterator it = keys.begin(); - it != keys.end(); ++it) { - Free(&used_total_, &used_per_setting_, *it); - } - return result.Pass(); -} - -ValueStore::WriteResult SettingsStorageQuotaEnforcer::Clear() { - WriteResult result = delegate_->Clear(); - if (result->HasError()) { - return result.Pass(); - } - - while (!used_per_setting_.empty()) { - Free(&used_total_, &used_per_setting_, used_per_setting_.begin()->first); - } - return result.Pass(); -} - -} // namespace extensions diff --git a/chrome/browser/extensions/api/storage/settings_storage_quota_enforcer.h b/chrome/browser/extensions/api/storage/settings_storage_quota_enforcer.h deleted file mode 100644 index 537ee33..0000000 --- a/chrome/browser/extensions/api/storage/settings_storage_quota_enforcer.h +++ /dev/null @@ -1,69 +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_API_STORAGE_SETTINGS_STORAGE_QUOTA_ENFORCER_H_ -#define CHROME_BROWSER_EXTENSIONS_API_STORAGE_SETTINGS_STORAGE_QUOTA_ENFORCER_H_ - -#include "base/compiler_specific.h" -#include "base/memory/weak_ptr.h" -#include "chrome/browser/value_store/value_store.h" - -namespace extensions { - -// Enforces total quota and a per-setting quota in bytes, and a maximum number -// of setting keys, for a delegate storage area. -class SettingsStorageQuotaEnforcer : public ValueStore { - public: - struct Limits { - // The total quota in bytes. - size_t quota_bytes; - - // The quota for each individual item in bytes. - size_t quota_bytes_per_item; - - // The maximum number of items allowed. - size_t max_items; - }; - - SettingsStorageQuotaEnforcer(const Limits& limits, ValueStore* delegate); - - virtual ~SettingsStorageQuotaEnforcer(); - - // ValueStore implementation. - virtual size_t GetBytesInUse(const std::string& key) OVERRIDE; - virtual size_t GetBytesInUse(const std::vector& keys) OVERRIDE; - virtual size_t GetBytesInUse() OVERRIDE; - virtual ReadResult Get(const std::string& key) OVERRIDE; - virtual ReadResult Get(const std::vector& keys) OVERRIDE; - virtual ReadResult Get() OVERRIDE; - virtual WriteResult Set( - WriteOptions options, - const std::string& key, - const Value& value) OVERRIDE; - virtual WriteResult Set( - WriteOptions options, const DictionaryValue& values) OVERRIDE; - virtual WriteResult Remove(const std::string& key) OVERRIDE; - virtual WriteResult Remove(const std::vector& keys) OVERRIDE; - virtual WriteResult Clear() OVERRIDE; - - private: - // Limits configuration. - const Limits limits_; - - // The delegate storage area. - scoped_ptr const delegate_; - - // Total bytes in used by |delegate_|. Includes both key lengths and - // JSON-encoded values. - size_t used_total_; - - // Map of item key to its size, including the key itself. - std::map used_per_setting_; - - DISALLOW_COPY_AND_ASSIGN(SettingsStorageQuotaEnforcer); -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_STORAGE_SETTINGS_STORAGE_QUOTA_ENFORCER_H_ diff --git a/chrome/browser/extensions/api/storage/settings_sync_processor.cc b/chrome/browser/extensions/api/storage/settings_sync_processor.cc deleted file mode 100644 index 94de1b6..0000000 --- a/chrome/browser/extensions/api/storage/settings_sync_processor.cc +++ /dev/null @@ -1,110 +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/api/storage/settings_namespace.h" -#include "chrome/browser/extensions/api/storage/settings_sync_processor.h" -#include "chrome/browser/extensions/api/storage/settings_sync_util.h" -#include "content/public/browser/browser_thread.h" -#include "sync/api/sync_change_processor.h" -#include "sync/api/sync_data.h" -#include "sync/protocol/extension_setting_specifics.pb.h" - -using content::BrowserThread; - -namespace extensions { - -SettingsSyncProcessor::SettingsSyncProcessor( - const std::string& extension_id, - syncer::ModelType type, - syncer::SyncChangeProcessor* sync_processor) - : extension_id_(extension_id), - type_(type), - sync_processor_(sync_processor), - initialized_(false) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - CHECK(type == syncer::EXTENSION_SETTINGS || type == syncer::APP_SETTINGS); - CHECK(sync_processor); -} - -SettingsSyncProcessor::~SettingsSyncProcessor() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); -} - -void SettingsSyncProcessor::Init(const DictionaryValue& initial_state) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - CHECK(!initialized_) << "Init called multiple times"; - - for (DictionaryValue::Iterator i(initial_state); i.HasNext(); i.Advance()) - synced_keys_.insert(i.key()); - - initialized_ = true; -} - -syncer::SyncError SettingsSyncProcessor::SendChanges( - const ValueStoreChangeList& changes) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - CHECK(initialized_) << "Init not called"; - - syncer::SyncChangeList sync_changes; - std::set added_keys; - std::set deleted_keys; - - for (ValueStoreChangeList::const_iterator i = changes.begin(); - i != changes.end(); ++i) { - const std::string& key = i->key(); - const Value* value = i->new_value(); - if (value) { - if (synced_keys_.count(key)) { - // New value, key is synced; send ACTION_UPDATE. - sync_changes.push_back(settings_sync_util::CreateUpdate( - extension_id_, key, *value, type_)); - } else { - // New value, key is not synced; send ACTION_ADD. - sync_changes.push_back(settings_sync_util::CreateAdd( - extension_id_, key, *value, type_)); - added_keys.insert(key); - } - } else { - if (synced_keys_.count(key)) { - // Clearing value, key is synced; send ACTION_DELETE. - sync_changes.push_back(settings_sync_util::CreateDelete( - extension_id_, key, type_)); - deleted_keys.insert(key); - } else { - LOG(WARNING) << "Deleted " << key << " but not in synced_keys_"; - } - } - } - - if (sync_changes.empty()) - return syncer::SyncError(); - - syncer::SyncError error = - sync_processor_->ProcessSyncChanges(FROM_HERE, sync_changes); - if (error.IsSet()) - return error; - - synced_keys_.insert(added_keys.begin(), added_keys.end()); - for (std::set::iterator i = deleted_keys.begin(); - i != deleted_keys.end(); ++i) { - synced_keys_.erase(*i); - } - - return syncer::SyncError(); -} - -void SettingsSyncProcessor::NotifyChanges(const ValueStoreChangeList& changes) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - CHECK(initialized_) << "Init not called"; - - for (ValueStoreChangeList::const_iterator i = changes.begin(); - i != changes.end(); ++i) { - if (i->new_value()) - synced_keys_.insert(i->key()); - else - synced_keys_.erase(i->key()); - } -} - -} // namespace extensions diff --git a/chrome/browser/extensions/api/storage/settings_sync_processor.h b/chrome/browser/extensions/api/storage/settings_sync_processor.h deleted file mode 100644 index 834e026..0000000 --- a/chrome/browser/extensions/api/storage/settings_sync_processor.h +++ /dev/null @@ -1,67 +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_API_STORAGE_SETTINGS_SYNC_PROCESSOR_H_ -#define CHROME_BROWSER_EXTENSIONS_API_STORAGE_SETTINGS_SYNC_PROCESSOR_H_ - -#include -#include - -#include "chrome/browser/value_store/value_store_change.h" -#include "sync/api/sync_error.h" - -namespace syncer { -class SyncChangeProcessor; -} // namespace syncer - -namespace extensions { - -// A wrapper for a SyncChangeProcessor that deals specifically with the syncing -// of a single extension's settings. Handles: -// - translating SettingChanges into calls into the Sync API. -// - deciding whether to ADD/REMOVE/SET depending on the current state of -// settings. -// - rate limiting (inherently per-extension, which is what we want). -class SettingsSyncProcessor { - public: - SettingsSyncProcessor(const std::string& extension_id, - syncer::ModelType type, - syncer::SyncChangeProcessor* sync_processor); - ~SettingsSyncProcessor(); - - // Initializes this with the initial state of sync. - void Init(const DictionaryValue& initial_state); - - // Sends |changes| to sync. - syncer::SyncError SendChanges(const ValueStoreChangeList& changes); - - // Informs this that |changes| have been receieved from sync. No action will - // be taken, but this must be notified for internal bookkeeping. - void NotifyChanges(const ValueStoreChangeList& changes); - - syncer::ModelType type() { return type_; } - - private: - // ID of the extension the changes are for. - const std::string extension_id_; - - // Sync model type. Either EXTENSION_SETTING or APP_SETTING. - const syncer::ModelType type_; - - // The sync processor used to send changes to sync. - syncer::SyncChangeProcessor* const sync_processor_; - - // Whether Init() has been called. - bool initialized_; - - // Keys of the settings that are currently being synced. Used to decide what - // kind of action (ADD, UPDATE, REMOVE) to send to sync. - std::set synced_keys_; - - DISALLOW_COPY_AND_ASSIGN(SettingsSyncProcessor); -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_STORAGE_SETTINGS_SYNC_PROCESSOR_H_ diff --git a/chrome/browser/extensions/api/storage/settings_sync_unittest.cc b/chrome/browser/extensions/api/storage/settings_sync_unittest.cc deleted file mode 100644 index f8d65f0..0000000 --- a/chrome/browser/extensions/api/storage/settings_sync_unittest.cc +++ /dev/null @@ -1,1425 +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 "base/bind.h" -#include "base/files/scoped_temp_dir.h" -#include "base/json/json_reader.h" -#include "base/json/json_writer.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop.h" -#include "chrome/browser/extensions/api/storage/leveldb_settings_storage_factory.h" -#include "chrome/browser/extensions/api/storage/settings_frontend.h" -#include "chrome/browser/extensions/api/storage/settings_storage_factory.h" -#include "chrome/browser/extensions/api/storage/settings_sync_util.h" -#include "chrome/browser/extensions/api/storage/settings_test_util.h" -#include "chrome/browser/extensions/api/storage/syncable_settings_storage.h" -#include "chrome/browser/extensions/extension_system.h" -#include "chrome/browser/extensions/test_extension_service.h" -#include "chrome/browser/value_store/testing_value_store.h" -#include "content/public/test/test_browser_thread.h" -#include "sync/api/sync_change_processor.h" -#include "sync/api/sync_error_factory.h" -#include "sync/api/sync_error_factory_mock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using content::BrowserThread; - -namespace extensions { - -namespace util = settings_test_util; - -namespace { - -// To save typing ValueStore::DEFAULTS everywhere. -const ValueStore::WriteOptions DEFAULTS = ValueStore::DEFAULTS; - -// Gets the pretty-printed JSON for a value. -static std::string GetJson(const Value& value) { - std::string json; - base::JSONWriter::WriteWithOptions(&value, - base::JSONWriter::OPTIONS_PRETTY_PRINT, - &json); - return json; -} - -// Returns whether two Values are equal. -testing::AssertionResult ValuesEq( - const char* _1, const char* _2, - const Value* expected, - const Value* actual) { - if (expected == actual) { - return testing::AssertionSuccess(); - } - if (!expected && actual) { - return testing::AssertionFailure() << - "Expected NULL, actual: " << GetJson(*actual); - } - if (expected && !actual) { - return testing::AssertionFailure() << - "Expected: " << GetJson(*expected) << ", actual NULL"; - } - if (!expected->Equals(actual)) { - return testing::AssertionFailure() << - "Expected: " << GetJson(*expected) << ", actual: " << GetJson(*actual); - } - return testing::AssertionSuccess(); -} - -// Returns whether the result of a storage operation is an expected value. -// Logs when different. -testing::AssertionResult SettingsEq( - const char* _1, const char* _2, - const DictionaryValue& expected, - ValueStore::ReadResult actual) { - if (actual->HasError()) { - return testing::AssertionFailure() << - "Expected: " << GetJson(expected) << - ", actual has error: " << actual->error(); - } - return ValuesEq(_1, _2, &expected, actual->settings().get()); -} - -// SyncChangeProcessor which just records the changes made, accessed after -// being converted to the more useful SettingSyncData via changes(). -class MockSyncChangeProcessor : public syncer::SyncChangeProcessor { - public: - MockSyncChangeProcessor() : fail_all_requests_(false) {} - - // syncer::SyncChangeProcessor implementation. - virtual syncer::SyncError ProcessSyncChanges( - const tracked_objects::Location& from_here, - const syncer::SyncChangeList& change_list) OVERRIDE { - if (fail_all_requests_) { - return syncer::SyncError( - FROM_HERE, - "MockSyncChangeProcessor: configured to fail", - change_list[0].sync_data().GetDataType()); - } - for (syncer::SyncChangeList::const_iterator it = change_list.begin(); - it != change_list.end(); ++it) { - changes_.push_back(SettingSyncData(*it)); - } - return syncer::SyncError(); - } - - // Mock methods. - - const SettingSyncDataList& changes() { return changes_; } - - void ClearChanges() { - changes_.clear(); - } - - void SetFailAllRequests(bool fail_all_requests) { - fail_all_requests_ = fail_all_requests; - } - - // Returns the only change for a given extension setting. If there is not - // exactly 1 change for that key, a test assertion will fail. - SettingSyncData GetOnlyChange( - const std::string& extension_id, const std::string& key) { - SettingSyncDataList matching_changes; - for (SettingSyncDataList::iterator it = changes_.begin(); - it != changes_.end(); ++it) { - if (it->extension_id() == extension_id && it->key() == key) { - matching_changes.push_back(*it); - } - } - if (matching_changes.empty()) { - ADD_FAILURE() << "No matching changes for " << extension_id << "/" << - key << " (out of " << changes_.size() << ")"; - return SettingSyncData( - syncer::SyncChange::ACTION_INVALID, "", "", - scoped_ptr(new DictionaryValue())); - } - if (matching_changes.size() != 1u) { - ADD_FAILURE() << matching_changes.size() << " matching changes for " << - extension_id << "/" << key << " (out of " << changes_.size() << ")"; - } - return matching_changes[0]; - } - - private: - SettingSyncDataList changes_; - bool fail_all_requests_; -}; - -class SyncChangeProcessorDelegate : public syncer::SyncChangeProcessor { - public: - explicit SyncChangeProcessorDelegate(syncer::SyncChangeProcessor* recipient) - : recipient_(recipient) { - DCHECK(recipient_); - } - virtual ~SyncChangeProcessorDelegate() {} - - // syncer::SyncChangeProcessor implementation. - virtual syncer::SyncError ProcessSyncChanges( - const tracked_objects::Location& from_here, - const syncer::SyncChangeList& change_list) OVERRIDE { - return recipient_->ProcessSyncChanges(from_here, change_list); - } - - private: - // The recipient of all sync changes. - syncer::SyncChangeProcessor* recipient_; - - DISALLOW_COPY_AND_ASSIGN(SyncChangeProcessorDelegate); -}; - -// SettingsStorageFactory which always returns TestingValueStore objects, -// and allows individually created objects to be returned. -class TestingValueStoreFactory : public SettingsStorageFactory { - public: - TestingValueStore* GetExisting(const std::string& extension_id) { - DCHECK(created_.count(extension_id)); - return created_[extension_id]; - } - - // SettingsStorageFactory implementation. - virtual ValueStore* Create(const FilePath& base_path, - const std::string& extension_id) OVERRIDE { - TestingValueStore* new_storage = new TestingValueStore(); - DCHECK(!created_.count(extension_id)); - created_[extension_id] = new_storage; - return new_storage; - } - - private: - // SettingsStorageFactory is refcounted. - virtual ~TestingValueStoreFactory() {} - - // None of these storage areas are owned by this factory, so care must be - // taken when calling GetExisting. - std::map created_; -}; - -} // namespace - -class ExtensionSettingsSyncTest : public testing::Test { - public: - ExtensionSettingsSyncTest() - : ui_thread_(BrowserThread::UI, MessageLoop::current()), - file_thread_(BrowserThread::FILE, MessageLoop::current()), - storage_factory_(new util::ScopedSettingsStorageFactory()), - sync_processor_(new MockSyncChangeProcessor), - sync_processor_delegate_(new SyncChangeProcessorDelegate( - sync_processor_.get())) {} - - virtual void SetUp() OVERRIDE { - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - profile_.reset(new util::MockProfile(temp_dir_.path())); - storage_factory_->Reset(new LeveldbSettingsStorageFactory()); - frontend_.reset( - SettingsFrontend::Create(storage_factory_.get(), profile_.get())); - } - - virtual void TearDown() OVERRIDE { - frontend_.reset(); - profile_.reset(); - // Execute any pending deletion tasks. - message_loop_.RunUntilIdle(); - } - - protected: - // Adds a record of an extension or app to the extension service, then returns - // its storage area. - ValueStore* AddExtensionAndGetStorage( - const std::string& id, Extension::Type type) { - ExtensionServiceInterface* esi = - extensions::ExtensionSystem::Get(profile_.get())->extension_service(); - static_cast(esi)-> - AddExtensionWithId(id, type); - return util::GetStorage(id, frontend_.get()); - } - - // Gets the syncer::SyncableService for the given sync type. - syncer::SyncableService* GetSyncableService(syncer::ModelType model_type) { - MessageLoop::current()->RunUntilIdle(); - return frontend_->GetBackendForSync(model_type); - } - - // Gets all the sync data from the SyncableService for a sync type as a map - // from extension id to its sync data. - std::map GetAllSyncData( - syncer::ModelType model_type) { - syncer::SyncDataList as_list = - GetSyncableService(model_type)->GetAllSyncData(model_type); - std::map as_map; - for (syncer::SyncDataList::iterator it = as_list.begin(); - it != as_list.end(); ++it) { - SettingSyncData sync_data(*it); - as_map[sync_data.extension_id()].push_back(sync_data); - } - return as_map; - } - - // Need these so that the DCHECKs for running on FILE or UI threads pass. - MessageLoop message_loop_; - content::TestBrowserThread ui_thread_; - content::TestBrowserThread file_thread_; - - base::ScopedTempDir temp_dir_; - scoped_ptr profile_; - scoped_ptr frontend_; - scoped_refptr storage_factory_; - scoped_ptr sync_processor_; - scoped_ptr sync_processor_delegate_; -}; - -// Get a semblance of coverage for both EXTENSION_SETTINGS and APP_SETTINGS -// sync by roughly alternative which one to test. - -TEST_F(ExtensionSettingsSyncTest, NoDataDoesNotInvokeSync) { - syncer::ModelType model_type = syncer::EXTENSION_SETTINGS; - Extension::Type type = Extension::TYPE_EXTENSION; - - EXPECT_EQ(0u, GetAllSyncData(model_type).size()); - - // Have one extension created before sync is set up, the other created after. - AddExtensionAndGetStorage("s1", type); - EXPECT_EQ(0u, GetAllSyncData(model_type).size()); - - GetSyncableService(model_type)->MergeDataAndStartSyncing( - model_type, - syncer::SyncDataList(), - sync_processor_delegate_.PassAs(), - scoped_ptr(new syncer::SyncErrorFactoryMock())); - - AddExtensionAndGetStorage("s2", type); - EXPECT_EQ(0u, GetAllSyncData(model_type).size()); - - GetSyncableService(model_type)->StopSyncing(model_type); - - EXPECT_EQ(0u, sync_processor_->changes().size()); - EXPECT_EQ(0u, GetAllSyncData(model_type).size()); -} - -TEST_F(ExtensionSettingsSyncTest, InSyncDataDoesNotInvokeSync) { - syncer::ModelType model_type = syncer::APP_SETTINGS; - Extension::Type type = Extension::TYPE_LEGACY_PACKAGED_APP; - - StringValue value1("fooValue"); - ListValue value2; - value2.Append(StringValue::CreateStringValue("barValue")); - - ValueStore* storage1 = AddExtensionAndGetStorage("s1", type); - ValueStore* storage2 = AddExtensionAndGetStorage("s2", type); - - storage1->Set(DEFAULTS, "foo", value1); - storage2->Set(DEFAULTS, "bar", value2); - - std::map all_sync_data = - GetAllSyncData(model_type); - EXPECT_EQ(2u, all_sync_data.size()); - EXPECT_EQ(1u, all_sync_data["s1"].size()); - EXPECT_PRED_FORMAT2(ValuesEq, &value1, &all_sync_data["s1"][0].value()); - EXPECT_EQ(1u, all_sync_data["s2"].size()); - EXPECT_PRED_FORMAT2(ValuesEq, &value2, &all_sync_data["s2"][0].value()); - - syncer::SyncDataList sync_data; - sync_data.push_back(settings_sync_util::CreateData( - "s1", "foo", value1, model_type)); - sync_data.push_back(settings_sync_util::CreateData( - "s2", "bar", value2, model_type)); - - GetSyncableService(model_type)->MergeDataAndStartSyncing( - model_type, sync_data, - sync_processor_delegate_.PassAs(), - scoped_ptr(new syncer::SyncErrorFactoryMock())); - - // Already in sync, so no changes. - EXPECT_EQ(0u, sync_processor_->changes().size()); - - // Regression test: not-changing the synced value shouldn't result in a sync - // change, and changing the synced value should result in an update. - storage1->Set(DEFAULTS, "foo", value1); - EXPECT_EQ(0u, sync_processor_->changes().size()); - - storage1->Set(DEFAULTS, "foo", value2); - EXPECT_EQ(1u, sync_processor_->changes().size()); - SettingSyncData change = sync_processor_->GetOnlyChange("s1", "foo"); - EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change.change_type()); - EXPECT_TRUE(value2.Equals(&change.value())); - - GetSyncableService(model_type)->StopSyncing(model_type); -} - -TEST_F(ExtensionSettingsSyncTest, LocalDataWithNoSyncDataIsPushedToSync) { - syncer::ModelType model_type = syncer::EXTENSION_SETTINGS; - Extension::Type type = Extension::TYPE_EXTENSION; - - StringValue value1("fooValue"); - ListValue value2; - value2.Append(StringValue::CreateStringValue("barValue")); - - ValueStore* storage1 = AddExtensionAndGetStorage("s1", type); - ValueStore* storage2 = AddExtensionAndGetStorage("s2", type); - - storage1->Set(DEFAULTS, "foo", value1); - storage2->Set(DEFAULTS, "bar", value2); - - GetSyncableService(model_type)->MergeDataAndStartSyncing( - model_type, - syncer::SyncDataList(), - sync_processor_delegate_.PassAs(), - scoped_ptr(new syncer::SyncErrorFactoryMock())); - - // All settings should have been pushed to sync. - EXPECT_EQ(2u, sync_processor_->changes().size()); - SettingSyncData change = sync_processor_->GetOnlyChange("s1", "foo"); - EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change.change_type()); - EXPECT_TRUE(value1.Equals(&change.value())); - change = sync_processor_->GetOnlyChange("s2", "bar"); - EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change.change_type()); - EXPECT_TRUE(value2.Equals(&change.value())); - - GetSyncableService(model_type)->StopSyncing(model_type); -} - -TEST_F(ExtensionSettingsSyncTest, AnySyncDataOverwritesLocalData) { - syncer::ModelType model_type = syncer::APP_SETTINGS; - Extension::Type type = Extension::TYPE_LEGACY_PACKAGED_APP; - - StringValue value1("fooValue"); - ListValue value2; - value2.Append(StringValue::CreateStringValue("barValue")); - - // Maintain dictionaries mirrored to the expected values of the settings in - // each storage area. - DictionaryValue expected1, expected2; - - // Pre-populate one of the storage areas. - ValueStore* storage1 = AddExtensionAndGetStorage("s1", type); - storage1->Set(DEFAULTS, "overwriteMe", value1); - - syncer::SyncDataList sync_data; - sync_data.push_back(settings_sync_util::CreateData( - "s1", "foo", value1, model_type)); - sync_data.push_back(settings_sync_util::CreateData( - "s2", "bar", value2, model_type)); - GetSyncableService(model_type)->MergeDataAndStartSyncing( - model_type, sync_data, - sync_processor_delegate_.PassAs(), - scoped_ptr(new syncer::SyncErrorFactoryMock())); - expected1.Set("foo", value1.DeepCopy()); - expected2.Set("bar", value2.DeepCopy()); - - ValueStore* storage2 = AddExtensionAndGetStorage("s2", type); - - // All changes should be local, so no sync changes. - EXPECT_EQ(0u, sync_processor_->changes().size()); - - // Sync settings should have been pushed to local settings. - EXPECT_PRED_FORMAT2(SettingsEq, expected1, storage1->Get()); - EXPECT_PRED_FORMAT2(SettingsEq, expected2, storage2->Get()); - - GetSyncableService(model_type)->StopSyncing(model_type); -} - -TEST_F(ExtensionSettingsSyncTest, ProcessSyncChanges) { - syncer::ModelType model_type = syncer::EXTENSION_SETTINGS; - Extension::Type type = Extension::TYPE_EXTENSION; - - StringValue value1("fooValue"); - ListValue value2; - value2.Append(StringValue::CreateStringValue("barValue")); - - // Maintain dictionaries mirrored to the expected values of the settings in - // each storage area. - DictionaryValue expected1, expected2; - - // Make storage1 initialised from local data, storage2 initialised from sync. - ValueStore* storage1 = AddExtensionAndGetStorage("s1", type); - ValueStore* storage2 = AddExtensionAndGetStorage("s2", type); - - storage1->Set(DEFAULTS, "foo", value1); - expected1.Set("foo", value1.DeepCopy()); - - syncer::SyncDataList sync_data; - sync_data.push_back(settings_sync_util::CreateData( - "s2", "bar", value2, model_type)); - - GetSyncableService(model_type)->MergeDataAndStartSyncing( - model_type, sync_data, - sync_processor_delegate_.PassAs(), - scoped_ptr(new syncer::SyncErrorFactoryMock())); - expected2.Set("bar", value2.DeepCopy()); - - // Make sync add some settings. - syncer::SyncChangeList change_list; - change_list.push_back(settings_sync_util::CreateAdd( - "s1", "bar", value2, model_type)); - change_list.push_back(settings_sync_util::CreateAdd( - "s2", "foo", value1, model_type)); - GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); - expected1.Set("bar", value2.DeepCopy()); - expected2.Set("foo", value1.DeepCopy()); - - EXPECT_PRED_FORMAT2(SettingsEq, expected1, storage1->Get()); - EXPECT_PRED_FORMAT2(SettingsEq, expected2, storage2->Get()); - - // Make sync update some settings, storage1 the new setting, storage2 the - // initial setting. - change_list.clear(); - change_list.push_back(settings_sync_util::CreateUpdate( - "s1", "bar", value2, model_type)); - change_list.push_back(settings_sync_util::CreateUpdate( - "s2", "bar", value1, model_type)); - GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); - expected1.Set("bar", value2.DeepCopy()); - expected2.Set("bar", value1.DeepCopy()); - - EXPECT_PRED_FORMAT2(SettingsEq, expected1, storage1->Get()); - EXPECT_PRED_FORMAT2(SettingsEq, expected2, storage2->Get()); - - // Make sync remove some settings, storage1 the initial setting, storage2 the - // new setting. - change_list.clear(); - change_list.push_back(settings_sync_util::CreateDelete( - "s1", "foo", model_type)); - change_list.push_back(settings_sync_util::CreateDelete( - "s2", "foo", model_type)); - GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); - expected1.Remove("foo", NULL); - expected2.Remove("foo", NULL); - - EXPECT_PRED_FORMAT2(SettingsEq, expected1, storage1->Get()); - EXPECT_PRED_FORMAT2(SettingsEq, expected2, storage2->Get()); - - GetSyncableService(model_type)->StopSyncing(model_type); -} - -TEST_F(ExtensionSettingsSyncTest, PushToSync) { - syncer::ModelType model_type = syncer::APP_SETTINGS; - Extension::Type type = Extension::TYPE_LEGACY_PACKAGED_APP; - - StringValue value1("fooValue"); - ListValue value2; - value2.Append(StringValue::CreateStringValue("barValue")); - - // Make storage1/2 initialised from local data, storage3/4 initialised from - // sync. - ValueStore* storage1 = AddExtensionAndGetStorage("s1", type); - ValueStore* storage2 = AddExtensionAndGetStorage("s2", type); - ValueStore* storage3 = AddExtensionAndGetStorage("s3", type); - ValueStore* storage4 = AddExtensionAndGetStorage("s4", type); - - storage1->Set(DEFAULTS, "foo", value1); - storage2->Set(DEFAULTS, "foo", value1); - - syncer::SyncDataList sync_data; - sync_data.push_back(settings_sync_util::CreateData( - "s3", "bar", value2, model_type)); - sync_data.push_back(settings_sync_util::CreateData( - "s4", "bar", value2, model_type)); - - GetSyncableService(model_type)->MergeDataAndStartSyncing( - model_type, sync_data, - sync_processor_delegate_.PassAs(), - scoped_ptr(new syncer::SyncErrorFactoryMock())); - - // Add something locally. - storage1->Set(DEFAULTS, "bar", value2); - storage2->Set(DEFAULTS, "bar", value2); - storage3->Set(DEFAULTS, "foo", value1); - storage4->Set(DEFAULTS, "foo", value1); - - SettingSyncData change = sync_processor_->GetOnlyChange("s1", "bar"); - EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change.change_type()); - EXPECT_TRUE(value2.Equals(&change.value())); - sync_processor_->GetOnlyChange("s2", "bar"); - EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change.change_type()); - EXPECT_TRUE(value2.Equals(&change.value())); - change = sync_processor_->GetOnlyChange("s3", "foo"); - EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change.change_type()); - EXPECT_TRUE(value1.Equals(&change.value())); - change = sync_processor_->GetOnlyChange("s4", "foo"); - EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change.change_type()); - EXPECT_TRUE(value1.Equals(&change.value())); - - // Change something locally, storage1/3 the new setting and storage2/4 the - // initial setting, for all combinations of local vs sync intialisation and - // new vs initial. - sync_processor_->ClearChanges(); - storage1->Set(DEFAULTS, "bar", value1); - storage2->Set(DEFAULTS, "foo", value2); - storage3->Set(DEFAULTS, "bar", value1); - storage4->Set(DEFAULTS, "foo", value2); - - change = sync_processor_->GetOnlyChange("s1", "bar"); - EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change.change_type()); - EXPECT_TRUE(value1.Equals(&change.value())); - change = sync_processor_->GetOnlyChange("s2", "foo"); - EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change.change_type()); - EXPECT_TRUE(value2.Equals(&change.value())); - change = sync_processor_->GetOnlyChange("s3", "bar"); - EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change.change_type()); - EXPECT_TRUE(value1.Equals(&change.value())); - change = sync_processor_->GetOnlyChange("s4", "foo"); - EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change.change_type()); - EXPECT_TRUE(value2.Equals(&change.value())); - - // Remove something locally, storage1/3 the new setting and storage2/4 the - // initial setting, for all combinations of local vs sync intialisation and - // new vs initial. - sync_processor_->ClearChanges(); - storage1->Remove("foo"); - storage2->Remove("bar"); - storage3->Remove("foo"); - storage4->Remove("bar"); - - EXPECT_EQ( - syncer::SyncChange::ACTION_DELETE, - sync_processor_->GetOnlyChange("s1", "foo").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_DELETE, - sync_processor_->GetOnlyChange("s2", "bar").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_DELETE, - sync_processor_->GetOnlyChange("s3", "foo").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_DELETE, - sync_processor_->GetOnlyChange("s4", "bar").change_type()); - - // Remove some nonexistent settings. - sync_processor_->ClearChanges(); - storage1->Remove("foo"); - storage2->Remove("bar"); - storage3->Remove("foo"); - storage4->Remove("bar"); - - EXPECT_EQ(0u, sync_processor_->changes().size()); - - // Clear the rest of the settings. Add the removed ones back first so that - // more than one setting is cleared. - storage1->Set(DEFAULTS, "foo", value1); - storage2->Set(DEFAULTS, "bar", value2); - storage3->Set(DEFAULTS, "foo", value1); - storage4->Set(DEFAULTS, "bar", value2); - - sync_processor_->ClearChanges(); - storage1->Clear(); - storage2->Clear(); - storage3->Clear(); - storage4->Clear(); - - EXPECT_EQ( - syncer::SyncChange::ACTION_DELETE, - sync_processor_->GetOnlyChange("s1", "foo").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_DELETE, - sync_processor_->GetOnlyChange("s1", "bar").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_DELETE, - sync_processor_->GetOnlyChange("s2", "foo").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_DELETE, - sync_processor_->GetOnlyChange("s2", "bar").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_DELETE, - sync_processor_->GetOnlyChange("s3", "foo").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_DELETE, - sync_processor_->GetOnlyChange("s3", "bar").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_DELETE, - sync_processor_->GetOnlyChange("s4", "foo").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_DELETE, - sync_processor_->GetOnlyChange("s4", "bar").change_type()); - - GetSyncableService(model_type)->StopSyncing(model_type); -} - -TEST_F(ExtensionSettingsSyncTest, ExtensionAndAppSettingsSyncSeparately) { - StringValue value1("fooValue"); - ListValue value2; - value2.Append(StringValue::CreateStringValue("barValue")); - - // storage1 is an extension, storage2 is an app. - ValueStore* storage1 = AddExtensionAndGetStorage( - "s1", Extension::TYPE_EXTENSION); - ValueStore* storage2 = AddExtensionAndGetStorage( - "s2", Extension::TYPE_LEGACY_PACKAGED_APP); - - storage1->Set(DEFAULTS, "foo", value1); - storage2->Set(DEFAULTS, "bar", value2); - - std::map extension_sync_data = - GetAllSyncData(syncer::EXTENSION_SETTINGS); - EXPECT_EQ(1u, extension_sync_data.size()); - EXPECT_EQ(1u, extension_sync_data["s1"].size()); - EXPECT_PRED_FORMAT2(ValuesEq, &value1, &extension_sync_data["s1"][0].value()); - - std::map app_sync_data = - GetAllSyncData(syncer::APP_SETTINGS); - EXPECT_EQ(1u, app_sync_data.size()); - EXPECT_EQ(1u, app_sync_data["s2"].size()); - EXPECT_PRED_FORMAT2(ValuesEq, &value2, &app_sync_data["s2"][0].value()); - - // Stop each separately, there should be no changes either time. - syncer::SyncDataList sync_data; - sync_data.push_back(settings_sync_util::CreateData( - "s1", "foo", value1, syncer::EXTENSION_SETTINGS)); - - GetSyncableService(syncer::EXTENSION_SETTINGS)->MergeDataAndStartSyncing( - syncer::EXTENSION_SETTINGS, - sync_data, - sync_processor_delegate_.PassAs(), - scoped_ptr(new syncer::SyncErrorFactoryMock())); - GetSyncableService(syncer::EXTENSION_SETTINGS)-> - StopSyncing(syncer::EXTENSION_SETTINGS); - EXPECT_EQ(0u, sync_processor_->changes().size()); - - sync_data.clear(); - sync_data.push_back(settings_sync_util::CreateData( - "s2", "bar", value2, syncer::APP_SETTINGS)); - - scoped_ptr app_settings_delegate_( - new SyncChangeProcessorDelegate(sync_processor_.get())); - GetSyncableService(syncer::APP_SETTINGS)->MergeDataAndStartSyncing( - syncer::APP_SETTINGS, - sync_data, - app_settings_delegate_.PassAs(), - scoped_ptr(new syncer::SyncErrorFactoryMock())); - GetSyncableService(syncer::APP_SETTINGS)-> - StopSyncing(syncer::APP_SETTINGS); - EXPECT_EQ(0u, sync_processor_->changes().size()); -} - -TEST_F(ExtensionSettingsSyncTest, FailingStartSyncingDisablesSync) { - syncer::ModelType model_type = syncer::EXTENSION_SETTINGS; - Extension::Type type = Extension::TYPE_EXTENSION; - - StringValue fooValue("fooValue"); - StringValue barValue("barValue"); - - // There is a bit of a convoluted method to get storage areas that can fail; - // hand out TestingValueStore object then toggle them failing/succeeding - // as necessary. - TestingValueStoreFactory* testing_factory = new TestingValueStoreFactory(); - storage_factory_->Reset(testing_factory); - - ValueStore* good = AddExtensionAndGetStorage("good", type); - ValueStore* bad = AddExtensionAndGetStorage("bad", type); - - // Make bad fail for incoming sync changes. - testing_factory->GetExisting("bad")->SetFailAllRequests(true); - { - syncer::SyncDataList sync_data; - sync_data.push_back(settings_sync_util::CreateData( - "good", "foo", fooValue, model_type)); - sync_data.push_back(settings_sync_util::CreateData( - "bad", "foo", fooValue, model_type)); - GetSyncableService(model_type)->MergeDataAndStartSyncing( - model_type, - sync_data, - sync_processor_delegate_.PassAs(), - scoped_ptr( - new syncer::SyncErrorFactoryMock())); - } - testing_factory->GetExisting("bad")->SetFailAllRequests(false); - - { - DictionaryValue dict; - dict.Set("foo", fooValue.DeepCopy()); - EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); - } - { - DictionaryValue dict; - EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); - } - - // Changes made to good should be sent to sync, changes from bad shouldn't. - sync_processor_->ClearChanges(); - good->Set(DEFAULTS, "bar", barValue); - bad->Set(DEFAULTS, "bar", barValue); - - EXPECT_EQ( - syncer::SyncChange::ACTION_ADD, - sync_processor_->GetOnlyChange("good", "bar").change_type()); - EXPECT_EQ(1u, sync_processor_->changes().size()); - - { - DictionaryValue dict; - dict.Set("foo", fooValue.DeepCopy()); - dict.Set("bar", barValue.DeepCopy()); - EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); - } - { - DictionaryValue dict; - dict.Set("bar", barValue.DeepCopy()); - EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); - } - - // Changes received from sync should go to good but not bad (even when it's - // not failing). - { - syncer::SyncChangeList change_list; - change_list.push_back(settings_sync_util::CreateUpdate( - "good", "foo", barValue, model_type)); - // (Sending UPDATE here even though it's adding, since that's what the state - // of sync is. In any case, it won't work.) - change_list.push_back(settings_sync_util::CreateUpdate( - "bad", "foo", barValue, model_type)); - GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); - } - - { - DictionaryValue dict; - dict.Set("foo", barValue.DeepCopy()); - dict.Set("bar", barValue.DeepCopy()); - EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); - } - { - DictionaryValue dict; - dict.Set("bar", barValue.DeepCopy()); - EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); - } - - // Changes made to bad still shouldn't go to sync, even though it didn't fail - // last time. - sync_processor_->ClearChanges(); - good->Set(DEFAULTS, "bar", fooValue); - bad->Set(DEFAULTS, "bar", fooValue); - - EXPECT_EQ( - syncer::SyncChange::ACTION_UPDATE, - sync_processor_->GetOnlyChange("good", "bar").change_type()); - EXPECT_EQ(1u, sync_processor_->changes().size()); - - { - DictionaryValue dict; - dict.Set("foo", barValue.DeepCopy()); - dict.Set("bar", fooValue.DeepCopy()); - EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); - } - { - DictionaryValue dict; - dict.Set("bar", fooValue.DeepCopy()); - EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); - } - - // Failing ProcessSyncChanges shouldn't go to the storage. - testing_factory->GetExisting("bad")->SetFailAllRequests(true); - { - syncer::SyncChangeList change_list; - change_list.push_back(settings_sync_util::CreateUpdate( - "good", "foo", fooValue, model_type)); - // (Ditto.) - change_list.push_back(settings_sync_util::CreateUpdate( - "bad", "foo", fooValue, model_type)); - GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); - } - testing_factory->GetExisting("bad")->SetFailAllRequests(false); - - { - DictionaryValue dict; - dict.Set("foo", fooValue.DeepCopy()); - dict.Set("bar", fooValue.DeepCopy()); - EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); - } - { - DictionaryValue dict; - dict.Set("bar", fooValue.DeepCopy()); - EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); - } - - // Restarting sync should make bad start syncing again. - sync_processor_->ClearChanges(); - GetSyncableService(model_type)->StopSyncing(model_type); - sync_processor_delegate_.reset(new SyncChangeProcessorDelegate( - sync_processor_.get())); - GetSyncableService(model_type)->MergeDataAndStartSyncing( - model_type, - syncer::SyncDataList(), - sync_processor_delegate_.PassAs(), - scoped_ptr(new syncer::SyncErrorFactoryMock())); - - // Local settings will have been pushed to sync, since it's empty (in this - // test; presumably it wouldn't be live, since we've been getting changes). - EXPECT_EQ( - syncer::SyncChange::ACTION_ADD, - sync_processor_->GetOnlyChange("good", "foo").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_ADD, - sync_processor_->GetOnlyChange("good", "bar").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_ADD, - sync_processor_->GetOnlyChange("bad", "bar").change_type()); - EXPECT_EQ(3u, sync_processor_->changes().size()); - - // Live local changes now get pushed, too. - sync_processor_->ClearChanges(); - good->Set(DEFAULTS, "bar", barValue); - bad->Set(DEFAULTS, "bar", barValue); - - EXPECT_EQ( - syncer::SyncChange::ACTION_UPDATE, - sync_processor_->GetOnlyChange("good", "bar").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_UPDATE, - sync_processor_->GetOnlyChange("bad", "bar").change_type()); - EXPECT_EQ(2u, sync_processor_->changes().size()); - - // And ProcessSyncChanges work, too. - { - syncer::SyncChangeList change_list; - change_list.push_back(settings_sync_util::CreateUpdate( - "good", "bar", fooValue, model_type)); - change_list.push_back(settings_sync_util::CreateUpdate( - "bad", "bar", fooValue, model_type)); - GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); - } - - { - DictionaryValue dict; - dict.Set("foo", fooValue.DeepCopy()); - dict.Set("bar", fooValue.DeepCopy()); - EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); - } - { - DictionaryValue dict; - dict.Set("bar", fooValue.DeepCopy()); - EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); - } -} - -TEST_F(ExtensionSettingsSyncTest, FailingProcessChangesDisablesSync) { - // The test above tests a failing ProcessSyncChanges too, but here test with - // an initially passing MergeDataAndStartSyncing. - syncer::ModelType model_type = syncer::APP_SETTINGS; - Extension::Type type = Extension::TYPE_LEGACY_PACKAGED_APP; - - StringValue fooValue("fooValue"); - StringValue barValue("barValue"); - - TestingValueStoreFactory* testing_factory = new TestingValueStoreFactory(); - storage_factory_->Reset(testing_factory); - - ValueStore* good = AddExtensionAndGetStorage("good", type); - ValueStore* bad = AddExtensionAndGetStorage("bad", type); - - // Unlike before, initially succeeding MergeDataAndStartSyncing. - { - syncer::SyncDataList sync_data; - sync_data.push_back(settings_sync_util::CreateData( - "good", "foo", fooValue, model_type)); - sync_data.push_back(settings_sync_util::CreateData( - "bad", "foo", fooValue, model_type)); - GetSyncableService(model_type)->MergeDataAndStartSyncing( - model_type, - sync_data, - sync_processor_delegate_.PassAs(), - scoped_ptr( - new syncer::SyncErrorFactoryMock())); - } - - EXPECT_EQ(0u, sync_processor_->changes().size()); - - { - DictionaryValue dict; - dict.Set("foo", fooValue.DeepCopy()); - EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); - } - { - DictionaryValue dict; - dict.Set("foo", fooValue.DeepCopy()); - EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); - } - - // Now fail ProcessSyncChanges for bad. - testing_factory->GetExisting("bad")->SetFailAllRequests(true); - { - syncer::SyncChangeList change_list; - change_list.push_back(settings_sync_util::CreateAdd( - "good", "bar", barValue, model_type)); - change_list.push_back(settings_sync_util::CreateAdd( - "bad", "bar", barValue, model_type)); - GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); - } - testing_factory->GetExisting("bad")->SetFailAllRequests(false); - - { - DictionaryValue dict; - dict.Set("foo", fooValue.DeepCopy()); - dict.Set("bar", barValue.DeepCopy()); - EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); - } - { - DictionaryValue dict; - dict.Set("foo", fooValue.DeepCopy()); - EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); - } - - // No more changes sent to sync for bad. - sync_processor_->ClearChanges(); - good->Set(DEFAULTS, "foo", barValue); - bad->Set(DEFAULTS, "foo", barValue); - - EXPECT_EQ( - syncer::SyncChange::ACTION_UPDATE, - sync_processor_->GetOnlyChange("good", "foo").change_type()); - EXPECT_EQ(1u, sync_processor_->changes().size()); - - // No more changes received from sync should go to bad. - { - syncer::SyncChangeList change_list; - change_list.push_back(settings_sync_util::CreateAdd( - "good", "foo", fooValue, model_type)); - change_list.push_back(settings_sync_util::CreateAdd( - "bad", "foo", fooValue, model_type)); - GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); - } - - { - DictionaryValue dict; - dict.Set("foo", fooValue.DeepCopy()); - dict.Set("bar", barValue.DeepCopy()); - EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); - } - { - DictionaryValue dict; - dict.Set("foo", barValue.DeepCopy()); - EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); - } -} - -TEST_F(ExtensionSettingsSyncTest, FailingGetAllSyncDataDoesntStopSync) { - syncer::ModelType model_type = syncer::EXTENSION_SETTINGS; - Extension::Type type = Extension::TYPE_EXTENSION; - - StringValue fooValue("fooValue"); - StringValue barValue("barValue"); - - TestingValueStoreFactory* testing_factory = new TestingValueStoreFactory(); - storage_factory_->Reset(testing_factory); - - ValueStore* good = AddExtensionAndGetStorage("good", type); - ValueStore* bad = AddExtensionAndGetStorage("bad", type); - - good->Set(DEFAULTS, "foo", fooValue); - bad->Set(DEFAULTS, "foo", fooValue); - - // Even though bad will fail to get all sync data, sync data should still - // include that from good. - testing_factory->GetExisting("bad")->SetFailAllRequests(true); - { - syncer::SyncDataList all_sync_data = - GetSyncableService(model_type)->GetAllSyncData(model_type); - EXPECT_EQ(1u, all_sync_data.size()); - EXPECT_EQ("good/foo", all_sync_data[0].GetTag()); - } - testing_factory->GetExisting("bad")->SetFailAllRequests(false); - - // Sync shouldn't be disabled for good (nor bad -- but this is unimportant). - GetSyncableService(model_type)->MergeDataAndStartSyncing( - model_type, - syncer::SyncDataList(), - sync_processor_delegate_.PassAs(), - scoped_ptr(new syncer::SyncErrorFactoryMock())); - - EXPECT_EQ( - syncer::SyncChange::ACTION_ADD, - sync_processor_->GetOnlyChange("good", "foo").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_ADD, - sync_processor_->GetOnlyChange("bad", "foo").change_type()); - EXPECT_EQ(2u, sync_processor_->changes().size()); - - sync_processor_->ClearChanges(); - good->Set(DEFAULTS, "bar", barValue); - bad->Set(DEFAULTS, "bar", barValue); - - EXPECT_EQ( - syncer::SyncChange::ACTION_ADD, - sync_processor_->GetOnlyChange("good", "bar").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_ADD, - sync_processor_->GetOnlyChange("bad", "bar").change_type()); - EXPECT_EQ(2u, sync_processor_->changes().size()); -} - -TEST_F(ExtensionSettingsSyncTest, FailureToReadChangesToPushDisablesSync) { - syncer::ModelType model_type = syncer::APP_SETTINGS; - Extension::Type type = Extension::TYPE_LEGACY_PACKAGED_APP; - - StringValue fooValue("fooValue"); - StringValue barValue("barValue"); - - TestingValueStoreFactory* testing_factory = new TestingValueStoreFactory(); - storage_factory_->Reset(testing_factory); - - ValueStore* good = AddExtensionAndGetStorage("good", type); - ValueStore* bad = AddExtensionAndGetStorage("bad", type); - - good->Set(DEFAULTS, "foo", fooValue); - bad->Set(DEFAULTS, "foo", fooValue); - - // good will successfully push foo:fooValue to sync, but bad will fail to - // get them so won't. - testing_factory->GetExisting("bad")->SetFailAllRequests(true); - GetSyncableService(model_type)->MergeDataAndStartSyncing( - model_type, - syncer::SyncDataList(), - sync_processor_delegate_.PassAs(), - scoped_ptr(new syncer::SyncErrorFactoryMock())); - testing_factory->GetExisting("bad")->SetFailAllRequests(false); - - EXPECT_EQ( - syncer::SyncChange::ACTION_ADD, - sync_processor_->GetOnlyChange("good", "foo").change_type()); - EXPECT_EQ(1u, sync_processor_->changes().size()); - - // bad should now be disabled for sync. - sync_processor_->ClearChanges(); - good->Set(DEFAULTS, "bar", barValue); - bad->Set(DEFAULTS, "bar", barValue); - - EXPECT_EQ( - syncer::SyncChange::ACTION_ADD, - sync_processor_->GetOnlyChange("good", "bar").change_type()); - EXPECT_EQ(1u, sync_processor_->changes().size()); - - { - syncer::SyncChangeList change_list; - change_list.push_back(settings_sync_util::CreateUpdate( - "good", "foo", barValue, model_type)); - // (Sending ADD here even though it's updating, since that's what the state - // of sync is. In any case, it won't work.) - change_list.push_back(settings_sync_util::CreateAdd( - "bad", "foo", barValue, model_type)); - GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); - } - - { - DictionaryValue dict; - dict.Set("foo", barValue.DeepCopy()); - dict.Set("bar", barValue.DeepCopy()); - EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); - } - { - DictionaryValue dict; - dict.Set("foo", fooValue.DeepCopy()); - dict.Set("bar", barValue.DeepCopy()); - EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); - } - - // Re-enabling sync without failing should cause the local changes from bad - // to be pushed to sync successfully, as should future changes to bad. - sync_processor_->ClearChanges(); - GetSyncableService(model_type)->StopSyncing(model_type); - sync_processor_delegate_.reset(new SyncChangeProcessorDelegate( - sync_processor_.get())); - GetSyncableService(model_type)->MergeDataAndStartSyncing( - model_type, - syncer::SyncDataList(), - sync_processor_delegate_.PassAs(), - scoped_ptr(new syncer::SyncErrorFactoryMock())); - - EXPECT_EQ( - syncer::SyncChange::ACTION_ADD, - sync_processor_->GetOnlyChange("good", "foo").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_ADD, - sync_processor_->GetOnlyChange("good", "bar").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_ADD, - sync_processor_->GetOnlyChange("bad", "foo").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_ADD, - sync_processor_->GetOnlyChange("bad", "bar").change_type()); - EXPECT_EQ(4u, sync_processor_->changes().size()); - - sync_processor_->ClearChanges(); - good->Set(DEFAULTS, "bar", fooValue); - bad->Set(DEFAULTS, "bar", fooValue); - - EXPECT_EQ( - syncer::SyncChange::ACTION_UPDATE, - sync_processor_->GetOnlyChange("good", "bar").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_UPDATE, - sync_processor_->GetOnlyChange("good", "bar").change_type()); - EXPECT_EQ(2u, sync_processor_->changes().size()); -} - -TEST_F(ExtensionSettingsSyncTest, FailureToPushLocalStateDisablesSync) { - syncer::ModelType model_type = syncer::EXTENSION_SETTINGS; - Extension::Type type = Extension::TYPE_EXTENSION; - - StringValue fooValue("fooValue"); - StringValue barValue("barValue"); - - TestingValueStoreFactory* testing_factory = new TestingValueStoreFactory(); - storage_factory_->Reset(testing_factory); - - ValueStore* good = AddExtensionAndGetStorage("good", type); - ValueStore* bad = AddExtensionAndGetStorage("bad", type); - - // Only set bad; setting good will cause it to fail below. - bad->Set(DEFAULTS, "foo", fooValue); - - sync_processor_->SetFailAllRequests(true); - GetSyncableService(model_type)->MergeDataAndStartSyncing( - model_type, - syncer::SyncDataList(), - sync_processor_delegate_.PassAs(), - scoped_ptr(new syncer::SyncErrorFactoryMock())); - sync_processor_->SetFailAllRequests(false); - - // Changes from good will be send to sync, changes from bad won't. - sync_processor_->ClearChanges(); - good->Set(DEFAULTS, "foo", barValue); - bad->Set(DEFAULTS, "foo", barValue); - - EXPECT_EQ( - syncer::SyncChange::ACTION_ADD, - sync_processor_->GetOnlyChange("good", "foo").change_type()); - EXPECT_EQ(1u, sync_processor_->changes().size()); - - // Changes from sync will be sent to good, not to bad. - { - syncer::SyncChangeList change_list; - change_list.push_back(settings_sync_util::CreateAdd( - "good", "bar", barValue, model_type)); - change_list.push_back(settings_sync_util::CreateAdd( - "bad", "bar", barValue, model_type)); - GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); - } - - { - DictionaryValue dict; - dict.Set("foo", barValue.DeepCopy()); - dict.Set("bar", barValue.DeepCopy()); - EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); - } - { - DictionaryValue dict; - dict.Set("foo", barValue.DeepCopy()); - EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); - } - - // Restarting sync makes everything work again. - sync_processor_->ClearChanges(); - GetSyncableService(model_type)->StopSyncing(model_type); - sync_processor_delegate_.reset(new SyncChangeProcessorDelegate( - sync_processor_.get())); - GetSyncableService(model_type)->MergeDataAndStartSyncing( - model_type, - syncer::SyncDataList(), - sync_processor_delegate_.PassAs(), - scoped_ptr(new syncer::SyncErrorFactoryMock())); - - EXPECT_EQ( - syncer::SyncChange::ACTION_ADD, - sync_processor_->GetOnlyChange("good", "foo").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_ADD, - sync_processor_->GetOnlyChange("good", "bar").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_ADD, - sync_processor_->GetOnlyChange("bad", "foo").change_type()); - EXPECT_EQ(3u, sync_processor_->changes().size()); - - sync_processor_->ClearChanges(); - good->Set(DEFAULTS, "foo", fooValue); - bad->Set(DEFAULTS, "foo", fooValue); - - EXPECT_EQ( - syncer::SyncChange::ACTION_UPDATE, - sync_processor_->GetOnlyChange("good", "foo").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_UPDATE, - sync_processor_->GetOnlyChange("good", "foo").change_type()); - EXPECT_EQ(2u, sync_processor_->changes().size()); -} - -TEST_F(ExtensionSettingsSyncTest, FailureToPushLocalChangeDisablesSync) { - syncer::ModelType model_type = syncer::EXTENSION_SETTINGS; - Extension::Type type = Extension::TYPE_EXTENSION; - - StringValue fooValue("fooValue"); - StringValue barValue("barValue"); - - TestingValueStoreFactory* testing_factory = new TestingValueStoreFactory(); - storage_factory_->Reset(testing_factory); - - ValueStore* good = AddExtensionAndGetStorage("good", type); - ValueStore* bad = AddExtensionAndGetStorage("bad", type); - - GetSyncableService(model_type)->MergeDataAndStartSyncing( - model_type, - syncer::SyncDataList(), - sync_processor_delegate_.PassAs(), - scoped_ptr(new syncer::SyncErrorFactoryMock())); - - // bad will fail to send changes. - good->Set(DEFAULTS, "foo", fooValue); - sync_processor_->SetFailAllRequests(true); - bad->Set(DEFAULTS, "foo", fooValue); - sync_processor_->SetFailAllRequests(false); - - EXPECT_EQ( - syncer::SyncChange::ACTION_ADD, - sync_processor_->GetOnlyChange("good", "foo").change_type()); - EXPECT_EQ(1u, sync_processor_->changes().size()); - - // No further changes should be sent from bad. - sync_processor_->ClearChanges(); - good->Set(DEFAULTS, "foo", barValue); - bad->Set(DEFAULTS, "foo", barValue); - - EXPECT_EQ( - syncer::SyncChange::ACTION_UPDATE, - sync_processor_->GetOnlyChange("good", "foo").change_type()); - EXPECT_EQ(1u, sync_processor_->changes().size()); - - // Changes from sync will be sent to good, not to bad. - { - syncer::SyncChangeList change_list; - change_list.push_back(settings_sync_util::CreateAdd( - "good", "bar", barValue, model_type)); - change_list.push_back(settings_sync_util::CreateAdd( - "bad", "bar", barValue, model_type)); - GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); - } - - { - DictionaryValue dict; - dict.Set("foo", barValue.DeepCopy()); - dict.Set("bar", barValue.DeepCopy()); - EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); - } - { - DictionaryValue dict; - dict.Set("foo", barValue.DeepCopy()); - EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); - } - - // Restarting sync makes everything work again. - sync_processor_->ClearChanges(); - GetSyncableService(model_type)->StopSyncing(model_type); - sync_processor_delegate_.reset(new SyncChangeProcessorDelegate( - sync_processor_.get())); - GetSyncableService(model_type)->MergeDataAndStartSyncing( - model_type, - syncer::SyncDataList(), - sync_processor_delegate_.PassAs(), - scoped_ptr(new syncer::SyncErrorFactoryMock())); - - EXPECT_EQ( - syncer::SyncChange::ACTION_ADD, - sync_processor_->GetOnlyChange("good", "foo").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_ADD, - sync_processor_->GetOnlyChange("good", "bar").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_ADD, - sync_processor_->GetOnlyChange("bad", "foo").change_type()); - EXPECT_EQ(3u, sync_processor_->changes().size()); - - sync_processor_->ClearChanges(); - good->Set(DEFAULTS, "foo", fooValue); - bad->Set(DEFAULTS, "foo", fooValue); - - EXPECT_EQ( - syncer::SyncChange::ACTION_UPDATE, - sync_processor_->GetOnlyChange("good", "foo").change_type()); - EXPECT_EQ( - syncer::SyncChange::ACTION_UPDATE, - sync_processor_->GetOnlyChange("good", "foo").change_type()); - EXPECT_EQ(2u, sync_processor_->changes().size()); -} - -TEST_F(ExtensionSettingsSyncTest, - LargeOutgoingChangeRejectedButIncomingAccepted) { - syncer::ModelType model_type = syncer::APP_SETTINGS; - Extension::Type type = Extension::TYPE_LEGACY_PACKAGED_APP; - - // This value should be larger than the limit in settings_backend.cc. - std::string string_5k; - for (size_t i = 0; i < 5000; ++i) { - string_5k.append("a"); - } - StringValue large_value(string_5k); - - GetSyncableService(model_type)->MergeDataAndStartSyncing( - model_type, - syncer::SyncDataList(), - sync_processor_delegate_.PassAs(), - scoped_ptr(new syncer::SyncErrorFactoryMock())); - - // Large local change rejected and doesn't get sent out. - ValueStore* storage1 = AddExtensionAndGetStorage("s1", type); - EXPECT_TRUE(storage1->Set(DEFAULTS, "large_value", large_value)->HasError()); - EXPECT_EQ(0u, sync_processor_->changes().size()); - - // Large incoming change should still get accepted. - ValueStore* storage2 = AddExtensionAndGetStorage("s2", type); - { - syncer::SyncChangeList change_list; - change_list.push_back(settings_sync_util::CreateAdd( - "s1", "large_value", large_value, model_type)); - change_list.push_back(settings_sync_util::CreateAdd( - "s2", "large_value", large_value, model_type)); - GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); - } - { - DictionaryValue expected; - expected.Set("large_value", large_value.DeepCopy()); - EXPECT_PRED_FORMAT2(SettingsEq, expected, storage1->Get()); - EXPECT_PRED_FORMAT2(SettingsEq, expected, storage2->Get()); - } - - GetSyncableService(model_type)->StopSyncing(model_type); -} - -TEST_F(ExtensionSettingsSyncTest, Dots) { - syncer::ModelType model_type = syncer::EXTENSION_SETTINGS; - Extension::Type type = Extension::TYPE_EXTENSION; - - ValueStore* storage = AddExtensionAndGetStorage("ext", type); - - { - syncer::SyncDataList sync_data_list; - scoped_ptr string_value(Value::CreateStringValue("value")); - sync_data_list.push_back(settings_sync_util::CreateData( - "ext", "key.with.dot", *string_value, model_type)); - - GetSyncableService(model_type)->MergeDataAndStartSyncing( - model_type, - sync_data_list, - sync_processor_delegate_.PassAs(), - scoped_ptr( - new syncer::SyncErrorFactoryMock())); - } - - // Test dots in keys that come from sync. - { - ValueStore::ReadResult data = storage->Get(); - ASSERT_FALSE(data->HasError()); - - DictionaryValue expected_data; - expected_data.SetWithoutPathExpansion( - "key.with.dot", - Value::CreateStringValue("value")); - EXPECT_TRUE(Value::Equals(&expected_data, data->settings().get())); - } - - // Test dots in keys going to sync. - { - scoped_ptr string_value(Value::CreateStringValue("spot")); - storage->Set(DEFAULTS, "key.with.spot", *string_value); - - ASSERT_EQ(1u, sync_processor_->changes().size()); - SettingSyncData sync_data = sync_processor_->changes()[0]; - EXPECT_EQ(syncer::SyncChange::ACTION_ADD, sync_data.change_type()); - EXPECT_EQ("ext", sync_data.extension_id()); - EXPECT_EQ("key.with.spot", sync_data.key()); - EXPECT_TRUE(sync_data.value().Equals(string_value.get())); - } -} - -} // namespace extensions diff --git a/chrome/browser/extensions/api/storage/settings_sync_util.cc b/chrome/browser/extensions/api/storage/settings_sync_util.cc deleted file mode 100644 index b9246e0..0000000 --- a/chrome/browser/extensions/api/storage/settings_sync_util.cc +++ /dev/null @@ -1,110 +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/api/storage/settings_sync_util.h" - -#include "base/json/json_writer.h" -#include "base/values.h" -#include "sync/protocol/app_setting_specifics.pb.h" -#include "sync/protocol/extension_setting_specifics.pb.h" -#include "sync/protocol/sync.pb.h" - -namespace extensions { - -namespace settings_sync_util { - -namespace { - -void PopulateExtensionSettingSpecifics( - const std::string& extension_id, - const std::string& key, - const Value& value, - sync_pb::ExtensionSettingSpecifics* specifics) { - specifics->set_extension_id(extension_id); - specifics->set_key(key); - { - std::string value_as_json; - base::JSONWriter::Write(&value, &value_as_json); - specifics->set_value(value_as_json); - } -} - -void PopulateAppSettingSpecifics( - const std::string& extension_id, - const std::string& key, - const Value& value, - sync_pb::AppSettingSpecifics* specifics) { - PopulateExtensionSettingSpecifics( - extension_id, key, value, specifics->mutable_extension_setting()); -} - -} // namespace - -syncer::SyncData CreateData( - const std::string& extension_id, - const std::string& key, - const Value& value, - syncer::ModelType type) { - sync_pb::EntitySpecifics specifics; - switch (type) { - case syncer::EXTENSION_SETTINGS: - PopulateExtensionSettingSpecifics( - extension_id, - key, - value, - specifics.mutable_extension_setting()); - break; - - case syncer::APP_SETTINGS: - PopulateAppSettingSpecifics( - extension_id, - key, - value, - specifics.mutable_app_setting()); - break; - - default: - NOTREACHED(); - } - - return syncer::SyncData::CreateLocalData( - extension_id + "/" + key, key, specifics); -} - -syncer::SyncChange CreateAdd( - const std::string& extension_id, - const std::string& key, - const Value& value, - syncer::ModelType type) { - return syncer::SyncChange( - FROM_HERE, - syncer::SyncChange::ACTION_ADD, - CreateData(extension_id, key, value, type)); -} - -syncer::SyncChange CreateUpdate( - const std::string& extension_id, - const std::string& key, - const Value& value, - syncer::ModelType type) { - return syncer::SyncChange( - FROM_HERE, - syncer::SyncChange::ACTION_UPDATE, - CreateData(extension_id, key, value, type)); -} - -syncer::SyncChange CreateDelete( - const std::string& extension_id, - const std::string& key, - syncer::ModelType type) { - DictionaryValue no_value; - return syncer::SyncChange( - FROM_HERE, - syncer::SyncChange::ACTION_DELETE, - CreateData(extension_id, key, no_value, type)); -} - -} // namespace settings_sync_util - -} // namespace extensions diff --git a/chrome/browser/extensions/api/storage/settings_sync_util.h b/chrome/browser/extensions/api/storage/settings_sync_util.h deleted file mode 100644 index 0c6df046..0000000 --- a/chrome/browser/extensions/api/storage/settings_sync_util.h +++ /dev/null @@ -1,51 +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_API_STORAGE_SETTINGS_SYNC_UTIL_H_ -#define CHROME_BROWSER_EXTENSIONS_API_STORAGE_SETTINGS_SYNC_UTIL_H_ - - -#include "sync/api/sync_change.h" -#include "sync/api/sync_data.h" - -namespace base { -class Value; -} // namespace base - -namespace extensions { - -namespace settings_sync_util { - -// Creates a syncer::SyncData object for an extension or app setting. -syncer::SyncData CreateData( - const std::string& extension_id, - const std::string& key, - const base::Value& value, - syncer::ModelType type); - -// Creates an "add" sync change for an extension or app setting. -syncer::SyncChange CreateAdd( - const std::string& extension_id, - const std::string& key, - const base::Value& value, - syncer::ModelType type); - -// Creates an "update" sync change for an extension or app setting. -syncer::SyncChange CreateUpdate( - const std::string& extension_id, - const std::string& key, - const base::Value& value, - syncer::ModelType type); - -// Creates a "delete" sync change for an extension or app setting. -syncer::SyncChange CreateDelete( - const std::string& extension_id, - const std::string& key, - syncer::ModelType type); - -} // namespace settings_sync_util - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_STORAGE_SETTINGS_SYNC_UTIL_H_ diff --git a/chrome/browser/extensions/api/storage/settings_test_util.cc b/chrome/browser/extensions/api/storage/settings_test_util.cc deleted file mode 100644 index bd90ae9..0000000 --- a/chrome/browser/extensions/api/storage/settings_test_util.cc +++ /dev/null @@ -1,165 +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/api/storage/settings_test_util.h" - -#include "base/file_path.h" -#include "chrome/browser/extensions/api/storage/settings_frontend.h" -#include "chrome/browser/extensions/extension_system_factory.h" -#include "chrome/common/extensions/extension.h" - -namespace extensions { - -namespace settings_test_util { - -// Intended as a StorageCallback from GetStorage. -static void AssignStorage(ValueStore** dst, ValueStore* src) { - *dst = src; -} - -ValueStore* GetStorage( - const std::string& extension_id, - settings_namespace::Namespace settings_namespace, - SettingsFrontend* frontend) { - ValueStore* storage = NULL; - frontend->RunWithStorage( - extension_id, - settings_namespace, - base::Bind(&AssignStorage, &storage)); - MessageLoop::current()->RunUntilIdle(); - return storage; -} - -ValueStore* GetStorage( - const std::string& extension_id, SettingsFrontend* frontend) { - return GetStorage(extension_id, settings_namespace::SYNC, frontend); -} - -// MockExtensionService - -MockExtensionService::MockExtensionService() {} - -MockExtensionService::~MockExtensionService() {} - -const Extension* MockExtensionService::GetExtensionById( - const std::string& id, bool include_disabled) const { - std::map >::const_iterator - maybe_extension = extensions_.find(id); - return maybe_extension == extensions_.end() ? - NULL : maybe_extension->second.get(); -} - -void MockExtensionService::AddExtensionWithId( - const std::string& id, Extension::Type type) { - std::set empty_permissions; - AddExtensionWithIdAndPermissions(id, type, empty_permissions); -} - -void MockExtensionService::AddExtensionWithIdAndPermissions( - const std::string& id, - Extension::Type type, - const std::set& permissions_set) { - DictionaryValue manifest; - manifest.SetString("name", std::string("Test extension ") + id); - manifest.SetString("version", "1.0"); - - scoped_ptr permissions(new ListValue()); - for (std::set::const_iterator it = permissions_set.begin(); - it != permissions_set.end(); ++it) { - permissions->Append(Value::CreateStringValue(*it)); - } - manifest.Set("permissions", permissions.release()); - - switch (type) { - case Extension::TYPE_EXTENSION: - break; - - case Extension::TYPE_LEGACY_PACKAGED_APP: { - DictionaryValue* app = new DictionaryValue(); - DictionaryValue* app_launch = new DictionaryValue(); - app_launch->SetString("local_path", "fake.html"); - app->Set("launch", app_launch); - manifest.Set("app", app); - break; - } - - default: - NOTREACHED(); - } - - std::string error; - scoped_refptr extension(Extension::Create( - FilePath(), - Extension::INTERNAL, - manifest, - Extension::NO_FLAGS, - id, - &error)); - DCHECK(extension.get()); - DCHECK(error.empty()); - extensions_[id] = extension; - - for (std::set::const_iterator it = permissions_set.begin(); - it != permissions_set.end(); ++it) { - DCHECK(extension->HasAPIPermission(*it)); - } -} - -// MockExtensionSystem - -MockExtensionSystem::MockExtensionSystem(Profile* profile) - : TestExtensionSystem(profile) {} -MockExtensionSystem::~MockExtensionSystem() {} - -EventRouter* MockExtensionSystem::event_router() { - if (!event_router_.get()) - event_router_.reset(new EventRouter(profile_, NULL)); - return event_router_.get(); -} - -ExtensionService* MockExtensionSystem::extension_service() { - ExtensionServiceInterface* as_interface = - static_cast(&extension_service_); - return static_cast(as_interface); -} - -ProfileKeyedService* BuildMockExtensionSystem(Profile* profile) { - return new MockExtensionSystem(profile); -} - -// MockProfile - -MockProfile::MockProfile(const FilePath& file_path) - : TestingProfile(file_path) { - ExtensionSystemFactory::GetInstance()->SetTestingFactoryAndUse(this, - &BuildMockExtensionSystem); -} - -MockProfile::~MockProfile() {} - -// ScopedSettingsFactory - -ScopedSettingsStorageFactory::ScopedSettingsStorageFactory() {} - -ScopedSettingsStorageFactory::ScopedSettingsStorageFactory( - const scoped_refptr& delegate) - : delegate_(delegate) {} - -ScopedSettingsStorageFactory::~ScopedSettingsStorageFactory() {} - -void ScopedSettingsStorageFactory::Reset( - const scoped_refptr& delegate) { - delegate_ = delegate; -} - -ValueStore* ScopedSettingsStorageFactory::Create( - const FilePath& base_path, - const std::string& extension_id) { - DCHECK(delegate_.get()); - return delegate_->Create(base_path, extension_id); -} - -} // namespace settings_test_util - -} // namespace extensions diff --git a/chrome/browser/extensions/api/storage/settings_test_util.h b/chrome/browser/extensions/api/storage/settings_test_util.h deleted file mode 100644 index ee85b4e..0000000 --- a/chrome/browser/extensions/api/storage/settings_test_util.h +++ /dev/null @@ -1,117 +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_API_STORAGE_SETTINGS_TEST_UTIL_H_ -#define CHROME_BROWSER_EXTENSIONS_API_STORAGE_SETTINGS_TEST_UTIL_H_ - -#include -#include - -#include "base/compiler_specific.h" -#include "base/memory/linked_ptr.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "chrome/browser/extensions/api/storage/settings_namespace.h" -#include "chrome/browser/extensions/api/storage/settings_storage_factory.h" -#include "chrome/browser/extensions/event_router.h" -#include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/extensions/test_extension_service.h" -#include "chrome/browser/extensions/test_extension_system.h" -#include "chrome/common/extensions/extension.h" -#include "chrome/test/base/testing_profile.h" - -class ValueStore; - -namespace extensions { - -class SettingsFrontend; -// Utilities for extension settings API tests. -namespace settings_test_util { - -// Synchronously gets the storage area for an extension from |frontend|. -ValueStore* GetStorage( - const std::string& extension_id, - settings_namespace::Namespace setting_namespace, - SettingsFrontend* frontend); - -// Synchronously gets the SYNC storage for an extension from |frontend|. -ValueStore* GetStorage( - const std::string& extension_id, - SettingsFrontend* frontend); - -// An ExtensionService which allows extensions to be hand-added to be returned -// by GetExtensionById. -class MockExtensionService : public TestExtensionService { - public: - MockExtensionService(); - virtual ~MockExtensionService(); - - // Adds an extension with id |id| to be returned by GetExtensionById. - void AddExtensionWithId(const std::string& id, Extension::Type type); - - // Adds an extension with id |id| to be returned by GetExtensionById, with - // a set of permissions. - void AddExtensionWithIdAndPermissions( - const std::string& id, - Extension::Type type, - const std::set& permissions); - - virtual const Extension* GetExtensionById( - const std::string& id, bool include_disabled) const OVERRIDE; - - private: - std::map > extensions_; -}; - -// A mock ExtensionSystem to serve an EventRouter. -class MockExtensionSystem : public TestExtensionSystem { - public: - explicit MockExtensionSystem(Profile* profile); - virtual ~MockExtensionSystem(); - - virtual EventRouter* event_router() OVERRIDE; - virtual ExtensionService* extension_service() OVERRIDE; - - private: - scoped_ptr event_router_; - MockExtensionService extension_service_; - - DISALLOW_COPY_AND_ASSIGN(MockExtensionSystem); -}; - -// A Profile which returns an ExtensionService with enough functionality for -// the tests. -class MockProfile : public TestingProfile { - public: - explicit MockProfile(const FilePath& file_path); - virtual ~MockProfile(); -}; - -// SettingsStorageFactory which acts as a wrapper for other factories. -class ScopedSettingsStorageFactory : public SettingsStorageFactory { - public: - ScopedSettingsStorageFactory(); - - explicit ScopedSettingsStorageFactory( - const scoped_refptr& delegate); - - // Sets the delegate factory (equivalent to scoped_ptr::reset). - void Reset(const scoped_refptr& delegate); - - // SettingsStorageFactory implementation. - virtual ValueStore* Create(const FilePath& base_path, - const std::string& extension_id) OVERRIDE; - - private: - // SettingsStorageFactory is refcounted. - virtual ~ScopedSettingsStorageFactory(); - - scoped_refptr delegate_; -}; - -} // namespace settings_test_util - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_STORAGE_SETTINGS_TEST_UTIL_H_ diff --git a/chrome/browser/extensions/api/storage/storage_api.cc b/chrome/browser/extensions/api/storage/storage_api.cc deleted file mode 100644 index 8be3087..0000000 --- a/chrome/browser/extensions/api/storage/storage_api.cc +++ /dev/null @@ -1,286 +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/api/storage/storage_api.h" - -#include -#include - -#include "base/bind.h" -#include "base/stringprintf.h" -#include "base/values.h" -#include "chrome/browser/extensions/api/storage/settings_frontend.h" -#include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/extensions/extensions_quota_service.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/common/extensions/api/storage.h" -#include "content/public/browser/browser_thread.h" - -namespace extensions { - -using content::BrowserThread; - -namespace { -const char kUnsupportedArgumentType[] = "Unsupported argument type"; -const char kInvalidNamespaceErrorMessage[] = - "\"%s\" is not available in this instance of Chrome"; -const char kManagedNamespaceDisabledErrorMessage[] = - "\"managed\" is disabled. Use \"--%s\" to enable it."; -const char kStorageErrorMessage[] = "Storage error"; -} // namespace - -// SettingsFunction - -SettingsFunction::SettingsFunction() - : settings_namespace_(settings_namespace::INVALID) {} - -SettingsFunction::~SettingsFunction() {} - -bool SettingsFunction::ShouldSkipQuotaLimiting() const { - // Only apply quota if this is for sync storage. - std::string settings_namespace_string; - if (!args_->GetString(0, &settings_namespace_string)) { - // This is an error but it will be caught in RunImpl(), there is no - // mechanism to signify an error from this function. - return false; - } - return settings_namespace_string != "sync"; -} - -bool SettingsFunction::RunImpl() { - std::string settings_namespace_string; - EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &settings_namespace_string)); - args_->Remove(0, NULL); - settings_namespace_ = - settings_namespace::FromString(settings_namespace_string); - EXTENSION_FUNCTION_VALIDATE( - settings_namespace_ != settings_namespace::INVALID); - - SettingsFrontend* frontend = - profile()->GetExtensionService()->settings_frontend(); - if (!frontend->IsStorageEnabled(settings_namespace_)) { - error_ = base::StringPrintf(kInvalidNamespaceErrorMessage, - settings_namespace_string.c_str()); - return false; - } - - observers_ = frontend->GetObservers(); - frontend->RunWithStorage( - extension_id(), - settings_namespace_, - base::Bind(&SettingsFunction::AsyncRunWithStorage, this)); - return true; -} - -void SettingsFunction::AsyncRunWithStorage(ValueStore* storage) { - bool success = RunWithStorage(storage); - BrowserThread::PostTask( - BrowserThread::UI, - FROM_HERE, - base::Bind(&SettingsFunction::SendResponse, this, success)); -} - -bool SettingsFunction::UseReadResult(ValueStore::ReadResult result) { - if (result->HasError()) { - error_ = result->error(); - return false; - } - - SetResult(result->settings().release()); - return true; -} - -bool SettingsFunction::UseWriteResult(ValueStore::WriteResult result) { - if (result->HasError()) { - error_ = result->error(); - return false; - } - - if (!result->changes().empty()) { - observers_->Notify( - &SettingsObserver::OnSettingsChanged, - extension_id(), - settings_namespace_, - ValueStoreChange::ToJson(result->changes())); - } - - return true; -} - -// Concrete settings functions - -namespace { - -// Adds all StringValues from a ListValue to a vector of strings. -void AddAllStringValues(const ListValue& from, std::vector* to) { - DCHECK(to->empty()); - std::string as_string; - for (ListValue::const_iterator it = from.begin(); it != from.end(); ++it) { - if ((*it)->GetAsString(&as_string)) { - to->push_back(as_string); - } - } -} - -// Gets the keys of a DictionaryValue. -std::vector GetKeys(const DictionaryValue& dict) { - std::vector keys; - for (DictionaryValue::key_iterator it = dict.begin_keys(); - it != dict.end_keys(); ++it) { - keys.push_back(*it); - } - return keys; -} - -// Creates quota heuristics for settings modification. -void GetModificationQuotaLimitHeuristics(QuotaLimitHeuristics* heuristics) { - QuotaLimitHeuristic::Config longLimitConfig = { - // See storage.json for current value. - api::storage::sync::MAX_WRITE_OPERATIONS_PER_HOUR, - base::TimeDelta::FromHours(1) - }; - heuristics->push_back( - new ExtensionsQuotaService::TimedLimit( - longLimitConfig, - new QuotaLimitHeuristic::SingletonBucketMapper(), - "MAX_WRITE_OPERATIONS_PER_HOUR")); - - // A max of 10 operations per minute, sustained over 10 minutes. - QuotaLimitHeuristic::Config shortLimitConfig = { - // See storage.json for current value. - api::storage::sync::MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE, - base::TimeDelta::FromMinutes(1) - }; - heuristics->push_back( - new ExtensionsQuotaService::SustainedLimit( - base::TimeDelta::FromMinutes(10), - shortLimitConfig, - new QuotaLimitHeuristic::SingletonBucketMapper(), - "MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE")); -}; - -} // namespace - -bool StorageGetFunction::RunWithStorage(ValueStore* storage) { - Value* input = NULL; - EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &input)); - - switch (input->GetType()) { - case Value::TYPE_NULL: - return UseReadResult(storage->Get()); - - case Value::TYPE_STRING: { - std::string as_string; - input->GetAsString(&as_string); - return UseReadResult(storage->Get(as_string)); - } - - case Value::TYPE_LIST: { - std::vector as_string_list; - AddAllStringValues(*static_cast(input), &as_string_list); - return UseReadResult(storage->Get(as_string_list)); - } - - case Value::TYPE_DICTIONARY: { - DictionaryValue* as_dict = static_cast(input); - ValueStore::ReadResult result = storage->Get(GetKeys(*as_dict)); - if (result->HasError()) { - return UseReadResult(result.Pass()); - } - - DictionaryValue* with_default_values = as_dict->DeepCopy(); - with_default_values->MergeDictionary(result->settings().get()); - return UseReadResult( - ValueStore::MakeReadResult(with_default_values)); - } - - default: - return UseReadResult( - ValueStore::MakeReadResult(kUnsupportedArgumentType)); - } -} - -bool StorageGetBytesInUseFunction::RunWithStorage(ValueStore* storage) { - Value* input = NULL; - EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &input)); - - size_t bytes_in_use = 0; - - switch (input->GetType()) { - case Value::TYPE_NULL: - bytes_in_use = storage->GetBytesInUse(); - break; - - case Value::TYPE_STRING: { - std::string as_string; - input->GetAsString(&as_string); - bytes_in_use = storage->GetBytesInUse(as_string); - break; - } - - case Value::TYPE_LIST: { - std::vector as_string_list; - AddAllStringValues(*static_cast(input), &as_string_list); - bytes_in_use = storage->GetBytesInUse(as_string_list); - break; - } - - default: - error_ = kUnsupportedArgumentType; - return false; - } - - SetResult(Value::CreateIntegerValue(bytes_in_use)); - return true; -} - -bool StorageSetFunction::RunWithStorage(ValueStore* storage) { - DictionaryValue* input = NULL; - EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &input)); - return UseWriteResult(storage->Set(ValueStore::DEFAULTS, *input)); -} - -void StorageSetFunction::GetQuotaLimitHeuristics( - QuotaLimitHeuristics* heuristics) const { - GetModificationQuotaLimitHeuristics(heuristics); -} - -bool StorageRemoveFunction::RunWithStorage(ValueStore* storage) { - Value* input = NULL; - EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &input)); - - switch (input->GetType()) { - case Value::TYPE_STRING: { - std::string as_string; - input->GetAsString(&as_string); - return UseWriteResult(storage->Remove(as_string)); - } - - case Value::TYPE_LIST: { - std::vector as_string_list; - AddAllStringValues(*static_cast(input), &as_string_list); - return UseWriteResult(storage->Remove(as_string_list)); - } - - default: - return UseWriteResult( - ValueStore::MakeWriteResult(kUnsupportedArgumentType)); - }; -} - -void StorageRemoveFunction::GetQuotaLimitHeuristics( - QuotaLimitHeuristics* heuristics) const { - GetModificationQuotaLimitHeuristics(heuristics); -} - -bool StorageClearFunction::RunWithStorage(ValueStore* storage) { - return UseWriteResult(storage->Clear()); -} - -void StorageClearFunction::GetQuotaLimitHeuristics( - QuotaLimitHeuristics* heuristics) const { - GetModificationQuotaLimitHeuristics(heuristics); -} - -} // namespace extensions diff --git a/chrome/browser/extensions/api/storage/storage_api.h b/chrome/browser/extensions/api/storage/storage_api.h deleted file mode 100644 index b217f6d..0000000 --- a/chrome/browser/extensions/api/storage/storage_api.h +++ /dev/null @@ -1,124 +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_API_STORAGE_STORAGE_API_H_ -#define CHROME_BROWSER_EXTENSIONS_API_STORAGE_STORAGE_API_H_ - -#include "base/compiler_specific.h" -#include "base/memory/ref_counted.h" -#include "chrome/browser/extensions/api/storage/settings_namespace.h" -#include "chrome/browser/extensions/api/storage/settings_observer.h" -#include "chrome/browser/extensions/extension_function.h" -#include "chrome/browser/value_store/value_store.h" - -namespace extensions { - -// Superclass of all settings functions. -class SettingsFunction : public AsyncExtensionFunction { - protected: - SettingsFunction(); - virtual ~SettingsFunction(); - - // ExtensionFunction: - virtual bool ShouldSkipQuotaLimiting() const OVERRIDE; - virtual bool RunImpl() OVERRIDE; - - // Extension settings function implementations should do their work here. - // The SettingsFrontend makes sure this is posted to the appropriate thread. - // Implementations should fill in args themselves, though (like RunImpl) - // may return false to imply failure. - virtual bool RunWithStorage(ValueStore* storage) = 0; - - // Sets error_ or result_ depending on the value of a storage ReadResult, and - // returns whether the result implies success (i.e. !error). - bool UseReadResult(ValueStore::ReadResult result); - - // Sets error_ depending on the value of a storage WriteResult, sends a - // change notification if needed, and returns whether the result implies - // success (i.e. !error). - bool UseWriteResult(ValueStore::WriteResult result); - - private: - // Called via PostTask from RunImpl. Calls RunWithStorage and then - // SendResponse with its success value. - void AsyncRunWithStorage(ValueStore* storage); - - // The settings namespace the call was for. For example, SYNC if the API - // call was chrome.settings.experimental.sync..., LOCAL if .local, etc. - settings_namespace::Namespace settings_namespace_; - - // Observers, cached so that it's only grabbed from the UI thread. - scoped_refptr observers_; -}; - -class StorageGetFunction : public SettingsFunction { - public: - DECLARE_EXTENSION_FUNCTION_NAME("storage.get"); - - protected: - virtual ~StorageGetFunction() {} - - // SettingsFunction: - virtual bool RunWithStorage(ValueStore* storage) OVERRIDE; -}; - -class StorageSetFunction : public SettingsFunction { - public: - DECLARE_EXTENSION_FUNCTION_NAME("storage.set"); - - protected: - virtual ~StorageSetFunction() {} - - // SettingsFunction: - virtual bool RunWithStorage(ValueStore* storage) OVERRIDE; - - // ExtensionFunction: - virtual void GetQuotaLimitHeuristics( - QuotaLimitHeuristics* heuristics) const OVERRIDE; -}; - -class StorageRemoveFunction : public SettingsFunction { - public: - DECLARE_EXTENSION_FUNCTION_NAME("storage.remove"); - - protected: - virtual ~StorageRemoveFunction() {} - - // SettingsFunction: - virtual bool RunWithStorage(ValueStore* storage) OVERRIDE; - - // ExtensionFunction: - virtual void GetQuotaLimitHeuristics( - QuotaLimitHeuristics* heuristics) const OVERRIDE; -}; - -class StorageClearFunction : public SettingsFunction { - public: - DECLARE_EXTENSION_FUNCTION_NAME("storage.clear"); - - protected: - virtual ~StorageClearFunction() {} - - // SettingsFunction: - virtual bool RunWithStorage(ValueStore* storage) OVERRIDE; - - // ExtensionFunction: - virtual void GetQuotaLimitHeuristics( - QuotaLimitHeuristics* heuristics) const OVERRIDE; -}; - -class StorageGetBytesInUseFunction : public SettingsFunction { - public: - DECLARE_EXTENSION_FUNCTION_NAME("storage.getBytesInUse"); - - protected: - virtual ~StorageGetBytesInUseFunction() {} - - // SettingsFunction: - virtual bool RunWithStorage(ValueStore* storage) OVERRIDE; -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_STORAGE_STORAGE_API_H_ diff --git a/chrome/browser/extensions/api/storage/sync_or_local_value_store_cache.cc b/chrome/browser/extensions/api/storage/sync_or_local_value_store_cache.cc deleted file mode 100644 index 7855b46..0000000 --- a/chrome/browser/extensions/api/storage/sync_or_local_value_store_cache.cc +++ /dev/null @@ -1,115 +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/api/storage/sync_or_local_value_store_cache.h" - -#include "base/bind.h" -#include "base/callback.h" -#include "base/file_path.h" -#include "base/sequenced_task_runner.h" -#include "chrome/browser/extensions/api/storage/settings_backend.h" -#include "chrome/browser/extensions/api/storage/settings_frontend.h" -#include "chrome/browser/extensions/api/storage/settings_storage_quota_enforcer.h" -#include "chrome/browser/extensions/api/storage/weak_unlimited_settings_storage.h" -#include "chrome/browser/extensions/extension_service.h" -#include "chrome/common/extensions/extension.h" -#include "chrome/common/extensions/permissions/api_permission.h" -#include "content/public/browser/browser_thread.h" - -using content::BrowserThread; - -namespace extensions { - -SyncOrLocalValueStoreCache::SyncOrLocalValueStoreCache( - settings_namespace::Namespace settings_namespace, - const scoped_refptr& factory, - const SettingsStorageQuotaEnforcer::Limits& quota, - const scoped_refptr& observers, - const FilePath& profile_path) - : settings_namespace_(settings_namespace) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK(settings_namespace_ == settings_namespace::LOCAL || - settings_namespace_ == settings_namespace::SYNC); - - // This post is safe since the destructor can only be invoked from the - // same message loop, and any potential post of a deletion task must come - // after the constructor returns. - BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, - base::Bind(&SyncOrLocalValueStoreCache::InitOnFileThread, - base::Unretained(this), - factory, quota, observers, profile_path)); -} - -SyncOrLocalValueStoreCache::~SyncOrLocalValueStoreCache() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); -} - -SettingsBackend* SyncOrLocalValueStoreCache::GetAppBackend() const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - DCHECK(app_backend_.get()); - return app_backend_.get(); -} - -SettingsBackend* SyncOrLocalValueStoreCache::GetExtensionBackend() const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - DCHECK(extension_backend_.get()); - return extension_backend_.get(); -} - -void SyncOrLocalValueStoreCache::RunWithValueStoreForExtension( - const StorageCallback& callback, - scoped_refptr extension) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - DCHECK(app_backend_.get()); - DCHECK(extension_backend_.get()); - SettingsBackend* backend = - extension->is_app() ? app_backend_.get() : extension_backend_.get(); - ValueStore* storage = backend->GetStorage(extension->id()); - - // A neat way to implement unlimited storage; if the extension has the - // unlimited storage permission, force through all calls to Set() (in the - // same way that writes from sync ignore quota). - // But only if it's local storage (bad stuff would happen if sync'ed - // storage is allowed to be unlimited). - bool is_unlimited = - settings_namespace_ == settings_namespace::LOCAL && - extension->HasAPIPermission(APIPermission::kUnlimitedStorage); - - if (is_unlimited) { - WeakUnlimitedSettingsStorage unlimited_storage(storage); - callback.Run(&unlimited_storage); - } else { - callback.Run(storage); - } -} - -void SyncOrLocalValueStoreCache::DeleteStorageSoon( - const std::string& extension_id) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - app_backend_->DeleteStorage(extension_id); - extension_backend_->DeleteStorage(extension_id); -} - -void SyncOrLocalValueStoreCache::InitOnFileThread( - const scoped_refptr& factory, - const SettingsStorageQuotaEnforcer::Limits& quota, - const scoped_refptr& observers, - const FilePath& profile_path) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - DCHECK(!app_backend_.get()); - DCHECK(!extension_backend_.get()); - const bool local = settings_namespace_ == settings_namespace::LOCAL; - const FilePath app_path = profile_path.AppendASCII( - local ? ExtensionService::kLocalAppSettingsDirectoryName - : ExtensionService::kSyncAppSettingsDirectoryName); - const FilePath extension_path = profile_path.AppendASCII( - local ? ExtensionService::kLocalExtensionSettingsDirectoryName - : ExtensionService::kSyncExtensionSettingsDirectoryName); - app_backend_.reset(new SettingsBackend(factory, app_path, quota, observers)); - extension_backend_.reset( - new SettingsBackend(factory, extension_path, quota, observers)); -} - -} // namespace extensions diff --git a/chrome/browser/extensions/api/storage/sync_or_local_value_store_cache.h b/chrome/browser/extensions/api/storage/sync_or_local_value_store_cache.h deleted file mode 100644 index 8438b45..0000000 --- a/chrome/browser/extensions/api/storage/sync_or_local_value_store_cache.h +++ /dev/null @@ -1,62 +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_API_STORAGE_SYNC_OR_LOCAL_VALUE_STORE_CACHE_H_ -#define CHROME_BROWSER_EXTENSIONS_API_STORAGE_SYNC_OR_LOCAL_VALUE_STORE_CACHE_H_ - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "chrome/browser/extensions/api/storage/settings_observer.h" -#include "chrome/browser/extensions/api/storage/settings_storage_quota_enforcer.h" -#include "chrome/browser/extensions/api/storage/value_store_cache.h" - -class FilePath; - -namespace extensions { - -class SettingsBackend; -class SettingsStorageFactory; - -// ValueStoreCache for the LOCAL and SYNC namespaces. It owns a backend for -// apps and another for extensions. Each backend takes care of persistence and -// syncing. -class SyncOrLocalValueStoreCache : public ValueStoreCache { - public: - SyncOrLocalValueStoreCache( - settings_namespace::Namespace settings_namespace, - const scoped_refptr& factory, - const SettingsStorageQuotaEnforcer::Limits& quota, - const scoped_refptr& observers, - const FilePath& profile_path); - virtual ~SyncOrLocalValueStoreCache(); - - SettingsBackend* GetAppBackend() const; - SettingsBackend* GetExtensionBackend() const; - - // ValueStoreCache implementation: - - virtual void RunWithValueStoreForExtension( - const StorageCallback& callback, - scoped_refptr extension) OVERRIDE; - - virtual void DeleteStorageSoon(const std::string& extension_id) OVERRIDE; - - private: - void InitOnFileThread(const scoped_refptr& factory, - const SettingsStorageQuotaEnforcer::Limits& quota, - const scoped_refptr& observers, - const FilePath& profile_path); - - settings_namespace::Namespace settings_namespace_; - scoped_ptr app_backend_; - scoped_ptr extension_backend_; - - DISALLOW_COPY_AND_ASSIGN(SyncOrLocalValueStoreCache); -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_STORAGE_SYNC_OR_LOCAL_VALUE_STORE_CACHE_H_ diff --git a/chrome/browser/extensions/api/storage/syncable_settings_storage.cc b/chrome/browser/extensions/api/storage/syncable_settings_storage.cc deleted file mode 100644 index 8f413a8..0000000 --- a/chrome/browser/extensions/api/storage/syncable_settings_storage.cc +++ /dev/null @@ -1,384 +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/api/storage/syncable_settings_storage.h" - -#include "chrome/browser/extensions/api/storage/settings_namespace.h" -#include "chrome/browser/extensions/api/storage/settings_sync_processor.h" -#include "chrome/browser/extensions/api/storage/settings_sync_util.h" -#include "content/public/browser/browser_thread.h" -#include "sync/api/sync_data.h" -#include "sync/protocol/extension_setting_specifics.pb.h" - -namespace extensions { - -using content::BrowserThread; - -SyncableSettingsStorage::SyncableSettingsStorage( - const scoped_refptr >& - observers, - const std::string& extension_id, - ValueStore* delegate) - : observers_(observers), - extension_id_(extension_id), - delegate_(delegate) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); -} - -SyncableSettingsStorage::~SyncableSettingsStorage() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); -} - -size_t SyncableSettingsStorage::GetBytesInUse(const std::string& key) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - return delegate_->GetBytesInUse(key); -} - -size_t SyncableSettingsStorage::GetBytesInUse( - const std::vector& keys) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - return delegate_->GetBytesInUse(keys); -} - -size_t SyncableSettingsStorage::GetBytesInUse() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - return delegate_->GetBytesInUse(); -} - -ValueStore::ReadResult SyncableSettingsStorage::Get( - const std::string& key) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - return delegate_->Get(key); -} - -ValueStore::ReadResult SyncableSettingsStorage::Get( - const std::vector& keys) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - return delegate_->Get(keys); -} - -ValueStore::ReadResult SyncableSettingsStorage::Get() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - return delegate_->Get(); -} - -ValueStore::WriteResult SyncableSettingsStorage::Set( - WriteOptions options, const std::string& key, const Value& value) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - WriteResult result = delegate_->Set(options, key, value); - if (result->HasError()) { - return result.Pass(); - } - SyncResultIfEnabled(result); - return result.Pass(); -} - -ValueStore::WriteResult SyncableSettingsStorage::Set( - WriteOptions options, const DictionaryValue& values) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - WriteResult result = delegate_->Set(options, values); - if (result->HasError()) { - return result.Pass(); - } - SyncResultIfEnabled(result); - return result.Pass(); -} - -ValueStore::WriteResult SyncableSettingsStorage::Remove( - const std::string& key) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - WriteResult result = delegate_->Remove(key); - if (result->HasError()) { - return result.Pass(); - } - SyncResultIfEnabled(result); - return result.Pass(); -} - -ValueStore::WriteResult SyncableSettingsStorage::Remove( - const std::vector& keys) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - WriteResult result = delegate_->Remove(keys); - if (result->HasError()) { - return result.Pass(); - } - SyncResultIfEnabled(result); - return result.Pass(); -} - -ValueStore::WriteResult SyncableSettingsStorage::Clear() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - WriteResult result = delegate_->Clear(); - if (result->HasError()) { - return result.Pass(); - } - SyncResultIfEnabled(result); - return result.Pass(); -} - -void SyncableSettingsStorage::SyncResultIfEnabled( - const ValueStore::WriteResult& result) { - if (sync_processor_.get() && !result->changes().empty()) { - syncer::SyncError error = sync_processor_->SendChanges(result->changes()); - if (error.IsSet()) - StopSyncing(); - } -} - -// Sync-related methods. - -syncer::SyncError SyncableSettingsStorage::StartSyncing( - const DictionaryValue& sync_state, - scoped_ptr sync_processor) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - DCHECK(!sync_processor_.get()); - - sync_processor_ = sync_processor.Pass(); - sync_processor_->Init(sync_state); - - ReadResult maybe_settings = delegate_->Get(); - if (maybe_settings->HasError()) { - return syncer::SyncError( - FROM_HERE, - std::string("Failed to get settings: ") + maybe_settings->error(), - sync_processor_->type()); - } - - const DictionaryValue& settings = *maybe_settings->settings().get(); - if (sync_state.empty()) - return SendLocalSettingsToSync(settings); - else - return OverwriteLocalSettingsWithSync(sync_state, settings); -} - -syncer::SyncError SyncableSettingsStorage::SendLocalSettingsToSync( - const DictionaryValue& settings) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - - ValueStoreChangeList changes; - for (DictionaryValue::Iterator i(settings); i.HasNext(); i.Advance()) { - changes.push_back(ValueStoreChange(i.key(), NULL, i.value().DeepCopy())); - } - - if (changes.empty()) - return syncer::SyncError(); - - syncer::SyncError error = sync_processor_->SendChanges(changes); - if (error.IsSet()) - StopSyncing(); - - return error; -} - -syncer::SyncError SyncableSettingsStorage::OverwriteLocalSettingsWithSync( - const DictionaryValue& sync_state, const DictionaryValue& settings) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - // Treat this as a list of changes to sync and use ProcessSyncChanges. - // This gives notifications etc for free. - scoped_ptr new_sync_state(sync_state.DeepCopy()); - - SettingSyncDataList changes; - for (DictionaryValue::Iterator it(settings); it.HasNext(); it.Advance()) { - Value* orphaned_sync_value = NULL; - if (new_sync_state->RemoveWithoutPathExpansion( - it.key(), &orphaned_sync_value)) { - scoped_ptr sync_value(orphaned_sync_value); - if (sync_value->Equals(&it.value())) { - // Sync and local values are the same, no changes to send. - } else { - // Sync value is different, update local setting with new value. - changes.push_back( - SettingSyncData( - syncer::SyncChange::ACTION_UPDATE, - extension_id_, - it.key(), - sync_value.Pass())); - } - } else { - // Not synced, delete local setting. - changes.push_back( - SettingSyncData( - syncer::SyncChange::ACTION_DELETE, - extension_id_, - it.key(), - scoped_ptr(new DictionaryValue()))); - } - } - - // Add all new settings to local settings. - while (!new_sync_state->empty()) { - std::string key = *new_sync_state->begin_keys(); - Value* value = NULL; - CHECK(new_sync_state->RemoveWithoutPathExpansion(key, &value)); - changes.push_back( - SettingSyncData( - syncer::SyncChange::ACTION_ADD, - extension_id_, - key, - scoped_ptr(value))); - } - - if (changes.empty()) - return syncer::SyncError(); - - return ProcessSyncChanges(changes); -} - -void SyncableSettingsStorage::StopSyncing() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - sync_processor_.reset(); -} - -syncer::SyncError SyncableSettingsStorage::ProcessSyncChanges( - const SettingSyncDataList& sync_changes) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - DCHECK(!sync_changes.empty()) << "No sync changes for " << extension_id_; - - if (!sync_processor_.get()) { - return syncer::SyncError( - FROM_HERE, - std::string("Sync is inactive for ") + extension_id_, - syncer::UNSPECIFIED); - } - - std::vector errors; - ValueStoreChangeList changes; - - for (SettingSyncDataList::const_iterator it = sync_changes.begin(); - it != sync_changes.end(); ++it) { - DCHECK_EQ(extension_id_, it->extension_id()); - - const std::string& key = it->key(); - const Value& value = it->value(); - - scoped_ptr current_value; - { - ReadResult maybe_settings = Get(it->key()); - if (maybe_settings->HasError()) { - errors.push_back(syncer::SyncError( - FROM_HERE, - std::string("Error getting current sync state for ") + - extension_id_ + "/" + key + ": " + maybe_settings->error(), - sync_processor_->type())); - continue; - } - Value* value = NULL; - if (maybe_settings->settings()->GetWithoutPathExpansion(key, &value)) { - current_value.reset(value->DeepCopy()); - } - } - - syncer::SyncError error; - - switch (it->change_type()) { - case syncer::SyncChange::ACTION_ADD: - if (!current_value.get()) { - error = OnSyncAdd(key, value.DeepCopy(), &changes); - } else { - // Already a value; hopefully a local change has beaten sync in a - // race and it's not a bug, so pretend it's an update. - LOG(WARNING) << "Got add from sync for existing setting " << - extension_id_ << "/" << key; - error = OnSyncUpdate( - key, current_value.release(), value.DeepCopy(), &changes); - } - break; - - case syncer::SyncChange::ACTION_UPDATE: - if (current_value.get()) { - error = OnSyncUpdate( - key, current_value.release(), value.DeepCopy(), &changes); - } else { - // Similarly, pretend it's an add. - LOG(WARNING) << "Got update from sync for nonexistent setting" << - extension_id_ << "/" << key; - error = OnSyncAdd(key, value.DeepCopy(), &changes); - } - break; - - case syncer::SyncChange::ACTION_DELETE: - if (current_value.get()) { - error = OnSyncDelete(key, current_value.release(), &changes); - } else { - // Similarly, ignore it. - LOG(WARNING) << "Got delete from sync for nonexistent setting " << - extension_id_ << "/" << key; - } - break; - - default: - NOTREACHED(); - } - - if (error.IsSet()) { - errors.push_back(error); - } - } - - sync_processor_->NotifyChanges(changes); - - observers_->Notify( - &SettingsObserver::OnSettingsChanged, - extension_id_, - settings_namespace::SYNC, - ValueStoreChange::ToJson(changes)); - - // TODO(kalman): Something sensible with multiple errors. - return errors.empty() ? syncer::SyncError() : errors[0]; -} - -syncer::SyncError SyncableSettingsStorage::OnSyncAdd( - const std::string& key, - Value* new_value, - ValueStoreChangeList* changes) { - DCHECK(new_value); - WriteResult result = delegate_->Set(IGNORE_QUOTA, key, *new_value); - if (result->HasError()) { - return syncer::SyncError( - FROM_HERE, - std::string("Error pushing sync add to local settings: ") + - result->error(), - sync_processor_->type()); - } - changes->push_back(ValueStoreChange(key, NULL, new_value)); - return syncer::SyncError(); -} - -syncer::SyncError SyncableSettingsStorage::OnSyncUpdate( - const std::string& key, - Value* old_value, - Value* new_value, - ValueStoreChangeList* changes) { - DCHECK(old_value); - DCHECK(new_value); - WriteResult result = delegate_->Set(IGNORE_QUOTA, key, *new_value); - if (result->HasError()) { - return syncer::SyncError( - FROM_HERE, - std::string("Error pushing sync update to local settings: ") + - result->error(), - sync_processor_->type()); - } - changes->push_back(ValueStoreChange(key, old_value, new_value)); - return syncer::SyncError(); -} - -syncer::SyncError SyncableSettingsStorage::OnSyncDelete( - const std::string& key, - Value* old_value, - ValueStoreChangeList* changes) { - DCHECK(old_value); - WriteResult result = delegate_->Remove(key); - if (result->HasError()) { - return syncer::SyncError( - FROM_HERE, - std::string("Error pushing sync remove to local settings: ") + - result->error(), - sync_processor_->type()); - } - changes->push_back(ValueStoreChange(key, old_value, NULL)); - return syncer::SyncError(); -} - -} // namespace extensions diff --git a/chrome/browser/extensions/api/storage/syncable_settings_storage.h b/chrome/browser/extensions/api/storage/syncable_settings_storage.h deleted file mode 100644 index ec57f83..0000000 --- a/chrome/browser/extensions/api/storage/syncable_settings_storage.h +++ /dev/null @@ -1,111 +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_API_STORAGE_SYNCABLE_SETTINGS_STORAGE_H_ -#define CHROME_BROWSER_EXTENSIONS_API_STORAGE_SYNCABLE_SETTINGS_STORAGE_H_ - -#include "base/compiler_specific.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/observer_list_threadsafe.h" -#include "base/values.h" -#include "chrome/browser/extensions/api/storage/setting_sync_data.h" -#include "chrome/browser/extensions/api/storage/settings_observer.h" -#include "chrome/browser/value_store/value_store.h" -#include "sync/api/sync_change.h" -#include "sync/api/syncable_service.h" - -namespace extensions { - -class SettingsSyncProcessor; - -// Decorates a ValueStore with sync behaviour. -class SyncableSettingsStorage : public ValueStore { - public: - SyncableSettingsStorage( - const scoped_refptr& observers, - const std::string& extension_id, - // Ownership taken. - ValueStore* delegate); - - virtual ~SyncableSettingsStorage(); - - // ValueStore implementation. - virtual size_t GetBytesInUse(const std::string& key) OVERRIDE; - virtual size_t GetBytesInUse(const std::vector& keys) OVERRIDE; - virtual size_t GetBytesInUse() OVERRIDE; - virtual ReadResult Get(const std::string& key) OVERRIDE; - virtual ReadResult Get(const std::vector& keys) OVERRIDE; - virtual ReadResult Get() OVERRIDE; - virtual WriteResult Set( - WriteOptions options, - const std::string& key, - const Value& value) OVERRIDE; - virtual WriteResult Set( - WriteOptions options, const DictionaryValue& values) OVERRIDE; - virtual WriteResult Remove(const std::string& key) OVERRIDE; - virtual WriteResult Remove(const std::vector& keys) OVERRIDE; - virtual WriteResult Clear() OVERRIDE; - - // Sync-related methods, analogous to those on SyncableService (handled by - // ExtensionSettings), but with looser guarantees about when the methods - // can be called. - - // Must only be called if sync isn't already active. - syncer::SyncError StartSyncing( - const DictionaryValue& sync_state, - scoped_ptr sync_processor); - - // May be called at any time (idempotent). - void StopSyncing(); - - // May be called at any time; changes will be ignored if sync isn't active. - syncer::SyncError ProcessSyncChanges(const SettingSyncDataList& sync_changes); - - private: - // Sends the changes from |result| to sync if it's enabled. - void SyncResultIfEnabled(const ValueStore::WriteResult& result); - - // Sends all local settings to sync (synced settings assumed to be empty). - syncer::SyncError SendLocalSettingsToSync( - const DictionaryValue& settings); - - // Overwrites local state with sync state. - syncer::SyncError OverwriteLocalSettingsWithSync( - const DictionaryValue& sync_state, const DictionaryValue& settings); - - // Called when an Add/Update/Remove comes from sync. Ownership of Value*s - // are taken. - syncer::SyncError OnSyncAdd( - const std::string& key, - Value* new_value, - ValueStoreChangeList* changes); - syncer::SyncError OnSyncUpdate( - const std::string& key, - Value* old_value, - Value* new_value, - ValueStoreChangeList* changes); - syncer::SyncError OnSyncDelete( - const std::string& key, - Value* old_value, - ValueStoreChangeList* changes); - - // List of observers to settings changes. - const scoped_refptr observers_; - - // Id of the extension these settings are for. - std::string const extension_id_; - - // Storage area to sync. - const scoped_ptr delegate_; - - // Object which sends changes to sync. - scoped_ptr sync_processor_; - - DISALLOW_COPY_AND_ASSIGN(SyncableSettingsStorage); -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_STORAGE_SYNCABLE_SETTINGS_STORAGE_H_ diff --git a/chrome/browser/extensions/api/storage/value_store_cache.cc b/chrome/browser/extensions/api/storage/value_store_cache.cc deleted file mode 100644 index bf68f7f..0000000 --- a/chrome/browser/extensions/api/storage/value_store_cache.cc +++ /dev/null @@ -1,13 +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/api/storage/value_store_cache.h" - -namespace extensions { - -ValueStoreCache::~ValueStoreCache() {} - -void ValueStoreCache::ShutdownOnUI() {} - -} // namespace extensions diff --git a/chrome/browser/extensions/api/storage/value_store_cache.h b/chrome/browser/extensions/api/storage/value_store_cache.h deleted file mode 100644 index 9aacc76..0000000 --- a/chrome/browser/extensions/api/storage/value_store_cache.h +++ /dev/null @@ -1,57 +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_API_STORAGE_VALUE_STORE_CACHE_H_ -#define CHROME_BROWSER_EXTENSIONS_API_STORAGE_VALUE_STORE_CACHE_H_ - -#include - -#include "base/callback.h" -#include "base/memory/ref_counted.h" - -class ValueStore; - -namespace extensions { - -class Extension; - -// Each namespace of the storage API implements this interface. -// Instances are created on the UI thread, but from then on live on the FILE -// thread. At shutdown, ShutdownOnUI() is first invoked on the UI thread, and -// the destructor is invoked soon after on the FILE thread. This gives -// implementations the chance to work with ValueStores on FILE but observe -// events on UI. -// It also means that any methods invoked on UI *before ShutdownOnUI()* can -// safely post other methods to the FILE thread, since the deletion task is only -// posted to FILE after ShutdownOnUI(). -class ValueStoreCache { - public: - typedef base::Callback StorageCallback; - - // Invoked on FILE. - virtual ~ValueStoreCache(); - - // This is invoked from the UI thread during destruction of the Profile that - // ultimately owns this object. Any Profile-related cleanups should be - // performed in this method, since the destructor will execute later, after - // the Profile is already gone. - virtual void ShutdownOnUI(); - - // Requests the cache to invoke |callback| with the appropriate ValueStore - // for the given |extension|. |callback| should be invoked with a NULL - // ValueStore in case of errors. - // |extension| is passed in a scoped_refptr<> because this method is - // asynchronously posted as a task to the loop returned by GetMessageLoop(), - // and this guarantees the Extension is still valid when the method executes. - virtual void RunWithValueStoreForExtension( - const StorageCallback& callback, - scoped_refptr extension) = 0; - - // Requests the cache to delete any storage used by |extension_id|. - virtual void DeleteStorageSoon(const std::string& extension_id) = 0; -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_STORAGE_VALUE_STORE_CACHE_H_ diff --git a/chrome/browser/extensions/api/storage/weak_unlimited_settings_storage.cc b/chrome/browser/extensions/api/storage/weak_unlimited_settings_storage.cc deleted file mode 100644 index 7adf5f4..0000000 --- a/chrome/browser/extensions/api/storage/weak_unlimited_settings_storage.cc +++ /dev/null @@ -1,67 +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/api/storage/weak_unlimited_settings_storage.h" - -namespace extensions { - -WeakUnlimitedSettingsStorage::WeakUnlimitedSettingsStorage( - ValueStore* delegate) - : delegate_(delegate) {} - -WeakUnlimitedSettingsStorage::~WeakUnlimitedSettingsStorage() {} - -size_t WeakUnlimitedSettingsStorage::GetBytesInUse(const std::string& key) { - return delegate_->GetBytesInUse(key); -} - -size_t WeakUnlimitedSettingsStorage::GetBytesInUse( - const std::vector& keys) { - return delegate_->GetBytesInUse(keys); -} - - -size_t WeakUnlimitedSettingsStorage::GetBytesInUse() { - return delegate_->GetBytesInUse(); -} - -ValueStore::ReadResult WeakUnlimitedSettingsStorage::Get( - const std::string& key) { - return delegate_->Get(key); -} - -ValueStore::ReadResult WeakUnlimitedSettingsStorage::Get( - const std::vector& keys) { - return delegate_->Get(keys); -} - -ValueStore::ReadResult WeakUnlimitedSettingsStorage::Get() { - return delegate_->Get(); -} - -ValueStore::WriteResult WeakUnlimitedSettingsStorage::Set( - WriteOptions options, const std::string& key, const Value& value) { - return delegate_->Set(IGNORE_QUOTA, key, value); -} - -ValueStore::WriteResult WeakUnlimitedSettingsStorage::Set( - WriteOptions options, const DictionaryValue& values) { - return delegate_->Set(IGNORE_QUOTA, values); -} - -ValueStore::WriteResult WeakUnlimitedSettingsStorage::Remove( - const std::string& key) { - return delegate_->Remove(key); -} - -ValueStore::WriteResult WeakUnlimitedSettingsStorage::Remove( - const std::vector& keys) { - return delegate_->Remove(keys); -} - -ValueStore::WriteResult WeakUnlimitedSettingsStorage::Clear() { - return delegate_->Clear(); -} - -} // namespace extensions diff --git a/chrome/browser/extensions/api/storage/weak_unlimited_settings_storage.h b/chrome/browser/extensions/api/storage/weak_unlimited_settings_storage.h deleted file mode 100644 index c74d57c..0000000 --- a/chrome/browser/extensions/api/storage/weak_unlimited_settings_storage.h +++ /dev/null @@ -1,49 +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_API_STORAGE_WEAK_UNLIMITED_SETTINGS_STORAGE_H_ -#define CHROME_BROWSER_EXTENSIONS_API_STORAGE_WEAK_UNLIMITED_SETTINGS_STORAGE_H_ - -#include "base/compiler_specific.h" -#include "chrome/browser/value_store/value_store.h" - -namespace extensions { - -// A ValueStore decorator which makes calls through |Set| ignore quota. -// "Weak" because ownership of the delegate isn't taken; this is designed to be -// temporarily attached to storage areas. -class WeakUnlimitedSettingsStorage : public ValueStore { - public: - // Ownership of |delegate| NOT taken. - explicit WeakUnlimitedSettingsStorage(ValueStore* delegate); - - virtual ~WeakUnlimitedSettingsStorage(); - - // ValueStore implementation. - virtual size_t GetBytesInUse(const std::string& key) OVERRIDE; - virtual size_t GetBytesInUse(const std::vector& keys) OVERRIDE; - virtual size_t GetBytesInUse() OVERRIDE; - virtual ReadResult Get(const std::string& key) OVERRIDE; - virtual ReadResult Get(const std::vector& keys) OVERRIDE; - virtual ReadResult Get() OVERRIDE; - virtual WriteResult Set( - WriteOptions options, - const std::string& key, - const Value& value) OVERRIDE; - virtual WriteResult Set( - WriteOptions options, const DictionaryValue& values) OVERRIDE; - virtual WriteResult Remove(const std::string& key) OVERRIDE; - virtual WriteResult Remove(const std::vector& keys) OVERRIDE; - virtual WriteResult Clear() OVERRIDE; - - private: - // The delegate storage area, NOT OWNED. - ValueStore* const delegate_; - - DISALLOW_COPY_AND_ASSIGN(WeakUnlimitedSettingsStorage); -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_STORAGE_WEAK_UNLIMITED_SETTINGS_STORAGE_H_ diff --git a/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.cc b/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.cc new file mode 100644 index 0000000..29a57d9 --- /dev/null +++ b/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.cc @@ -0,0 +1,277 @@ +// 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/api/tabs/execute_code_in_tab_function.h" + +#include "base/bind.h" +#include "base/string_util.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/extensions/api/tabs/tabs.h" +#include "chrome/browser/extensions/api/tabs/tabs_constants.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_tab_util.h" +#include "chrome/browser/extensions/file_reader.h" +#include "chrome/browser/extensions/script_executor.h" +#include "chrome/browser/extensions/tab_helper.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/common/extensions/api/tabs.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_constants.h" +#include "chrome/common/extensions/extension_file_util.h" +#include "chrome/common/extensions/extension_l10n_util.h" +#include "chrome/common/extensions/extension_manifest_constants.h" +#include "chrome/common/extensions/extension_messages.h" +#include "chrome/common/extensions/message_bundle.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/web_contents.h" +#include "extensions/common/error_utils.h" + +using content::BrowserThread; +using extensions::api::tabs::InjectDetails; +using extensions::ErrorUtils; +using extensions::ScriptExecutor; +using extensions::UserScript; + +namespace keys = extensions::tabs_constants; + +ExecuteCodeInTabFunction::ExecuteCodeInTabFunction() + : execute_tab_id_(-1) { +} + +ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {} + +bool ExecuteCodeInTabFunction::HasPermission() { + if (Init() && + extension_->HasAPIPermissionForTab(execute_tab_id_, + extensions::APIPermission::kTab)) { + return true; + } + return ExtensionFunction::HasPermission(); +} + +bool ExecuteCodeInTabFunction::RunImpl() { + EXTENSION_FUNCTION_VALIDATE(Init()); + + if (!details_->code.get() && !details_->file.get()) { + error_ = keys::kNoCodeOrFileToExecuteError; + return false; + } + if (details_->code.get() && details_->file.get()) { + error_ = keys::kMoreThanOneValuesError; + return false; + } + + content::WebContents* contents = NULL; + + // If |tab_id| is specified, look for the tab. Otherwise default to selected + // tab in the current window. + CHECK_GE(execute_tab_id_, 0); + if (!ExtensionTabUtil::GetTabById(execute_tab_id_, profile(), + include_incognito(), + NULL, NULL, &contents, NULL)) { + return false; + } + + // NOTE: This can give the wrong answer due to race conditions, but it is OK, + // we check again in the renderer. + CHECK(contents); + if (!GetExtension()->CanExecuteScriptOnPage(contents->GetURL(), + contents->GetURL(), + execute_tab_id_, + NULL, + &error_)) { + return false; + } + + if (details_->code.get()) + return Execute(*details_->code); + + CHECK(details_->file.get()); + resource_ = GetExtension()->GetResource(*details_->file); + + if (resource_.extension_root().empty() || resource_.relative_path().empty()) { + error_ = keys::kNoCodeOrFileToExecuteError; + return false; + } + + scoped_refptr file_reader(new FileReader( + resource_, base::Bind(&ExecuteCodeInTabFunction::DidLoadFile, this))); + file_reader->Start(); + + return true; +} + +void ExecuteCodeInTabFunction::OnExecuteCodeFinished(const std::string& error, + int32 on_page_id, + const GURL& on_url, + const ListValue& result) { + if (!error.empty()) + SetError(error); + + SendResponse(error.empty()); +} + +void TabsExecuteScriptFunction::OnExecuteCodeFinished(const std::string& error, + int32 on_page_id, + const GURL& on_url, + const ListValue& result) { + if (error.empty()) + SetResult(result.DeepCopy()); + ExecuteCodeInTabFunction::OnExecuteCodeFinished(error, on_page_id, on_url, + result); +} + +bool ExecuteCodeInTabFunction::Init() { + if (details_.get()) + return true; + + // |tab_id| is optional so it's ok if it's not there. + int tab_id = -1; + args_->GetInteger(0, &tab_id); + + // |details| are not optional. + DictionaryValue* details_value = NULL; + if (!args_->GetDictionary(1, &details_value)) + return false; + scoped_ptr details(new InjectDetails()); + if (!InjectDetails::Populate(*details_value, details.get())) + return false; + + // If the tab ID is -1 then it needs to be converted to the currently active + // tab's ID. + if (tab_id == -1) { + Browser* browser = GetCurrentBrowser(); + if (!browser) + return false; + content::WebContents* web_contents = NULL; + if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, &tab_id)) + return false; + } + + execute_tab_id_ = tab_id; + details_ = details.Pass(); + return true; +} + +void ExecuteCodeInTabFunction::DidLoadFile(bool success, + const std::string& data) { + std::string function_name = name(); + const extensions::Extension* extension = GetExtension(); + + // Check if the file is CSS and needs localization. + if (success && + function_name == TabsInsertCSSFunction::function_name() && + extension != NULL && + data.find( + extensions::MessageBundle::kMessageBegin) != std::string::npos) { + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + base::Bind(&ExecuteCodeInTabFunction::LocalizeCSS, this, + data, + extension->id(), + extension->path(), + extension->default_locale())); + } else { + DidLoadAndLocalizeFile(success, data); + } +} + +void ExecuteCodeInTabFunction::LocalizeCSS( + const std::string& data, + const std::string& extension_id, + const FilePath& extension_path, + const std::string& extension_default_locale) { + scoped_ptr localization_messages( + extension_file_util::LoadMessageBundleSubstitutionMap( + extension_path, extension_id, extension_default_locale)); + + // We need to do message replacement on the data, so it has to be mutable. + std::string css_data = data; + std::string error; + extensions::MessageBundle::ReplaceMessagesWithExternalDictionary( + *localization_messages, &css_data, &error); + + // Call back DidLoadAndLocalizeFile on the UI thread. The success parameter + // is always true, because if loading had failed, we wouldn't have had + // anything to localize. + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&ExecuteCodeInTabFunction::DidLoadAndLocalizeFile, this, + true, css_data)); +} + +void ExecuteCodeInTabFunction::DidLoadAndLocalizeFile(bool success, + const std::string& data) { + if (success) { + if (!Execute(data)) + SendResponse(false); + } else { +#if defined(OS_POSIX) + // TODO(viettrungluu): bug: there's no particular reason the path should be + // UTF-8, in which case this may fail. + error_ = ErrorUtils::FormatErrorMessage(keys::kLoadFileError, + resource_.relative_path().value()); +#elif defined(OS_WIN) + error_ = ErrorUtils::FormatErrorMessage(keys::kLoadFileError, + WideToUTF8(resource_.relative_path().value())); +#endif // OS_WIN + SendResponse(false); + } +} + +bool ExecuteCodeInTabFunction::Execute(const std::string& code_string) { + content::WebContents* contents = NULL; + Browser* browser = NULL; + + bool success = ExtensionTabUtil::GetTabById( + execute_tab_id_, profile(), include_incognito(), &browser, NULL, + &contents, NULL) && contents && browser; + + if (!success) + return false; + + const extensions::Extension* extension = GetExtension(); + if (!extension) + return false; + + ScriptExecutor::ScriptType script_type = ScriptExecutor::JAVASCRIPT; + std::string function_name = name(); + if (function_name == TabsInsertCSSFunction::function_name()) { + script_type = ScriptExecutor::CSS; + } else if (function_name != TabsExecuteScriptFunction::function_name()) { + NOTREACHED(); + } + + ScriptExecutor::FrameScope frame_scope = + details_->all_frames.get() && *details_->all_frames ? + ScriptExecutor::ALL_FRAMES : + ScriptExecutor::TOP_FRAME; + + UserScript::RunLocation run_at = UserScript::UNDEFINED; + switch (details_->run_at) { + case InjectDetails::RUN_AT_NONE: + case InjectDetails::RUN_AT_DOCUMENT_IDLE: + run_at = UserScript::DOCUMENT_IDLE; + break; + case InjectDetails::RUN_AT_DOCUMENT_START: + run_at = UserScript::DOCUMENT_START; + break; + case InjectDetails::RUN_AT_DOCUMENT_END: + run_at = UserScript::DOCUMENT_END; + break; + } + CHECK_NE(UserScript::UNDEFINED, run_at); + + extensions::TabHelper::FromWebContents(contents)-> + script_executor()->ExecuteScript( + extension->id(), + script_type, + code_string, + frame_scope, + run_at, + ScriptExecutor::ISOLATED_WORLD, + base::Bind(&ExecuteCodeInTabFunction::OnExecuteCodeFinished, this)); + return true; +} diff --git a/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.h b/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.h new file mode 100644 index 0000000..7ce5ec1 --- /dev/null +++ b/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.h @@ -0,0 +1,94 @@ +// 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_API_TABS_EXECUTE_CODE_IN_TAB_FUNCTION_H__ +#define CHROME_BROWSER_EXTENSIONS_API_TABS_EXECUTE_CODE_IN_TAB_FUNCTION_H__ + +#include + +#include "chrome/browser/extensions/extension_function.h" +#include "chrome/common/extensions/extension_resource.h" +#include "chrome/common/extensions/user_script.h" + +namespace extensions { +namespace api { +namespace tabs { +struct InjectDetails; +} // namespace tabs +} // namespace api +} // namespace extensions + +// Implement API call tabs.executeScript and tabs.insertCSS. +class ExecuteCodeInTabFunction : public AsyncExtensionFunction { + public: + ExecuteCodeInTabFunction(); + + protected: + virtual ~ExecuteCodeInTabFunction(); + + // ExtensionFunction: + virtual bool HasPermission() OVERRIDE; + virtual bool RunImpl() OVERRIDE; + + // Message handler. + virtual void OnExecuteCodeFinished(const std::string& error, + int32 on_page_id, + const GURL& on_url, + const ListValue& script_result); + + private: + // Initialize the |execute_tab_id_| and |details_| if they haven't already + // been. Returns whether initialization was successful. + bool Init(); + + // Called when contents from the file whose path is specified in JSON + // arguments has been loaded. + void DidLoadFile(bool success, const std::string& data); + + // Runs on FILE thread. Loads message bundles for the extension and + // localizes the CSS data. Calls back DidLoadAndLocalizeFile on the UI thread. + void LocalizeCSS( + const std::string& data, + const std::string& extension_id, + const FilePath& extension_path, + const std::string& extension_default_locale); + + // Called when contents from the loaded file have been localized. + void DidLoadAndLocalizeFile(bool success, const std::string& data); + + // Run in UI thread. Code string contains the code to be executed. Returns + // true on success. If true is returned, this does an AddRef. + bool Execute(const std::string& code_string); + + // Id of tab which executes code. + int execute_tab_id_; + + // The injection details. + scoped_ptr details_; + + // Contains extension resource built from path of file which is + // specified in JSON arguments. + ExtensionResource resource_; +}; + +class TabsExecuteScriptFunction : public ExecuteCodeInTabFunction { + private: + virtual ~TabsExecuteScriptFunction() {} + + virtual void OnExecuteCodeFinished(const std::string& error, + int32 on_page_id, + const GURL& on_url, + const ListValue& script_result) OVERRIDE; + + DECLARE_EXTENSION_FUNCTION_NAME("tabs.executeScript") +}; + +class TabsInsertCSSFunction : public ExecuteCodeInTabFunction { + private: + virtual ~TabsInsertCSSFunction() {} + + DECLARE_EXTENSION_FUNCTION_NAME("tabs.insertCSS") +}; + +#endif // CHROME_BROWSER_EXTENSIONS_API_TABS_EXECUTE_CODE_IN_TAB_FUNCTION_H__ diff --git a/chrome/browser/extensions/api/tabs/tabs.cc b/chrome/browser/extensions/api/tabs/tabs.cc new file mode 100644 index 0000000..342b630 --- /dev/null +++ b/chrome/browser/extensions/api/tabs/tabs.cc @@ -0,0 +1,1886 @@ +// 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/api/tabs/tabs.h" + +#include +#include +#include + +#include "base/base64.h" +#include "base/bind.h" +#include "base/command_line.h" +#include "base/logging.h" +#include "base/memory/ref_counted_memory.h" +#include "base/message_loop.h" +#include "base/stl_util.h" +#include "base/string16.h" +#include "base/string_number_conversions.h" +#include "base/string_util.h" +#include "base/stringprintf.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/extensions/api/tabs/tabs_constants.h" +#include "chrome/browser/extensions/extension_function_dispatcher.h" +#include "chrome/browser/extensions/extension_function_util.h" +#include "chrome/browser/extensions/extension_host.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_tab_util.h" +#include "chrome/browser/extensions/script_executor.h" +#include "chrome/browser/extensions/tab_helper.h" +#include "chrome/browser/extensions/window_controller.h" +#include "chrome/browser/extensions/window_controller_list.h" +#include "chrome/browser/prefs/incognito_mode_prefs.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/translate/translate_tab_helper.h" +#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_list.h" +#include "chrome/browser/ui/browser_navigator.h" +#include "chrome/browser/ui/browser_tabstrip.h" +#include "chrome/browser/ui/browser_window.h" +#include "chrome/browser/ui/extensions/shell_window.h" +#include "chrome/browser/ui/host_desktop.h" +#include "chrome/browser/ui/panels/panel_manager.h" +#include "chrome/browser/ui/snapshot_tab_helper.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/window_sizer/window_sizer.h" +#include "chrome/browser/web_applications/web_app.h" +#include "chrome/common/chrome_notification_types.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/extensions/api/windows.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_manifest_constants.h" +#include "chrome/common/extensions/extension_messages.h" +#include "chrome/common/extensions/user_script.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/url_constants.h" +#include "content/public/browser/navigation_controller.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/browser/notification_details.h" +#include "content/public/browser/notification_source.h" +#include "content/public/browser/render_view_host.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" +#include "content/public/common/url_constants.h" +#include "extensions/common/constants.h" +#include "extensions/common/error_utils.h" +#include "skia/ext/image_operations.h" +#include "skia/ext/platform_canvas.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/base/models/list_selection_model.h" +#include "ui/base/ui_base_types.h" +#include "ui/gfx/codec/jpeg_codec.h" +#include "ui/gfx/codec/png_codec.h" + +#if defined(OS_WIN) +#include "win8/util/win8_util.h" +#endif // OS_WIN + +namespace Get = extensions::api::windows::Get; +namespace GetAll = extensions::api::windows::GetAll; +namespace GetCurrent = extensions::api::windows::GetCurrent; +namespace GetLastFocused = extensions::api::windows::GetLastFocused; +namespace errors = extension_manifest_errors; +namespace keys = extensions::tabs_constants; + +using content::NavigationController; +using content::NavigationEntry; +using content::OpenURLParams; +using content::Referrer; +using content::RenderViewHost; +using content::WebContents; +using extensions::ErrorUtils; +using extensions::ScriptExecutor; +using extensions::WindowController; +using extensions::WindowControllerList; + +const int CaptureVisibleTabFunction::kDefaultQuality = 90; + +namespace { + +// |error_message| can optionally be passed in a will be set with an appropriate +// message if the window cannot be found by id. +Browser* GetBrowserInProfileWithId(Profile* profile, + const int window_id, + bool include_incognito, + std::string* error_message) { + Profile* incognito_profile = + include_incognito && profile->HasOffTheRecordProfile() ? + profile->GetOffTheRecordProfile() : NULL; + for (BrowserList::const_iterator browser = BrowserList::begin(); + browser != BrowserList::end(); ++browser) { + if (((*browser)->profile() == profile || + (*browser)->profile() == incognito_profile) && + ExtensionTabUtil::GetWindowId(*browser) == window_id && + ((*browser)->window())) + return *browser; + } + + if (error_message) + *error_message = ErrorUtils::FormatErrorMessage( + keys::kWindowNotFoundError, base::IntToString(window_id)); + + return NULL; +} + +bool GetBrowserFromWindowID( + UIThreadExtensionFunction* function, int window_id, Browser** browser) { + if (window_id == extension_misc::kCurrentWindowId) { + *browser = function->GetCurrentBrowser(); + if (!(*browser) || !(*browser)->window()) { + function->SetError(keys::kNoCurrentWindowError); + return false; + } + } else { + std::string error; + *browser = GetBrowserInProfileWithId( + function->profile(), window_id, function->include_incognito(), &error); + if (!*browser) { + function->SetError(error); + return false; + } + } + return true; +} + +bool GetWindowFromWindowID(UIThreadExtensionFunction* function, + int window_id, + WindowController** controller) { + if (window_id == extension_misc::kCurrentWindowId) { + WindowController* extension_window_controller = + function->dispatcher()->delegate()->GetExtensionWindowController(); + // If there is a window controller associated with this extension, use that. + if (extension_window_controller) { + *controller = extension_window_controller; + } else { + // Otherwise get the focused or most recently added window. + *controller = WindowControllerList::GetInstance()-> + CurrentWindowForFunction(function); + } + if (!(*controller)) { + function->SetError(keys::kNoCurrentWindowError); + return false; + } + } else { + *controller = WindowControllerList::GetInstance()-> + FindWindowForFunctionById(function, window_id); + if (!(*controller)) { + function->SetError(ErrorUtils::FormatErrorMessage( + keys::kWindowNotFoundError, base::IntToString(window_id))); + return false; + } + } + return true; +} + +// |error_message| can optionally be passed in and will be set with an +// appropriate message if the tab cannot be found by id. +bool GetTabById(int tab_id, + Profile* profile, + bool include_incognito, + Browser** browser, + TabStripModel** tab_strip, + content::WebContents** contents, + int* tab_index, + std::string* error_message) { + if (ExtensionTabUtil::GetTabById(tab_id, profile, include_incognito, + browser, tab_strip, contents, tab_index)) + return true; + + if (error_message) + *error_message = ErrorUtils::FormatErrorMessage( + keys::kTabNotFoundError, base::IntToString(tab_id)); + + return false; +} + +// A three state enum to distinguish between when a boolean query argument is +// set or not. +enum QueryArg { + NOT_SET = -1, + MATCH_FALSE, + MATCH_TRUE +}; + +bool MatchesQueryArg(QueryArg arg, bool value) { + if (arg == NOT_SET) + return true; + + return (arg == MATCH_TRUE && value) || (arg == MATCH_FALSE && !value); +} + +QueryArg ParseBoolQueryArg(base::DictionaryValue* query, const char* key) { + if (query->HasKey(key)) { + bool value = false; + CHECK(query->GetBoolean(key, &value)); + return value ? MATCH_TRUE : MATCH_FALSE; + } + return NOT_SET; +} + +Browser* CreateBrowserWindow(const Browser::CreateParams& params, + Profile* profile, + const std::string& extension_id) { + bool use_existing_browser_window = false; + +#if defined(OS_WIN) + // In windows 8 metro mode we don't allow windows to be created. + if (win8::IsSingleWindowMetroMode()) + use_existing_browser_window = true; +#endif // OS_WIN + + Browser* new_window = NULL; + if (use_existing_browser_window) + // The false parameter passed below is to ensure that we find a browser + // object matching the profile passed in, instead of the original profile + new_window = chrome::FindTabbedBrowser(profile, false, + params.host_desktop_type); + + if (!new_window) + new_window = new Browser(params); + return new_window; +} + +} // namespace + +// Windows --------------------------------------------------------------------- + +bool GetWindowFunction::RunImpl() { + scoped_ptr params(Get::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + bool populate_tabs = false; + if (params->get_info.get() && params->get_info->populate.get()) + populate_tabs = *params->get_info->populate; + + WindowController* controller; + if (!GetWindowFromWindowID(this, params->window_id, &controller)) + return false; + + if (populate_tabs) + SetResult(controller->CreateWindowValueWithTabs(GetExtension())); + else + SetResult(controller->CreateWindowValue()); + return true; +} + +bool GetCurrentWindowFunction::RunImpl() { + scoped_ptr params(GetCurrent::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + bool populate_tabs = false; + if (params->get_info.get() && params->get_info->populate.get()) + populate_tabs = *params->get_info->populate; + + WindowController* controller; + if (!GetWindowFromWindowID(this, + extension_misc::kCurrentWindowId, + &controller)) { + return false; + } + if (populate_tabs) + SetResult(controller->CreateWindowValueWithTabs(GetExtension())); + else + SetResult(controller->CreateWindowValue()); + return true; +} + +bool GetLastFocusedWindowFunction::RunImpl() { + scoped_ptr params( + GetLastFocused::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + bool populate_tabs = false; + if (params->get_info.get() && params->get_info->populate.get()) + populate_tabs = *params->get_info->populate; + + // Note: currently this returns the last active browser. If we decide to + // include other window types (e.g. panels), we will need to add logic to + // WindowControllerList that mirrors the active behavior of BrowserList. + Browser* browser = chrome::FindAnyBrowser( + profile(), include_incognito(), chrome::GetActiveDesktop()); + if (!browser || !browser->window()) { + error_ = keys::kNoLastFocusedWindowError; + return false; + } + WindowController* controller = + browser->extension_window_controller(); + if (populate_tabs) + SetResult(controller->CreateWindowValueWithTabs(GetExtension())); + else + SetResult(controller->CreateWindowValue()); + return true; +} + +bool GetAllWindowsFunction::RunImpl() { + scoped_ptr params(GetAll::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + bool populate_tabs = false; + if (params->get_info.get() && params->get_info->populate.get()) + populate_tabs = *params->get_info->populate; + + ListValue* window_list = new ListValue(); + const WindowControllerList::ControllerList& windows = + WindowControllerList::GetInstance()->windows(); + for (WindowControllerList::ControllerList::const_iterator iter = + windows.begin(); + iter != windows.end(); ++iter) { + if (!this->CanOperateOnWindow(*iter)) + continue; + if (populate_tabs) + window_list->Append((*iter)->CreateWindowValueWithTabs(GetExtension())); + else + window_list->Append((*iter)->CreateWindowValue()); + } + SetResult(window_list); + return true; +} + +bool CreateWindowFunction::ShouldOpenIncognitoWindow( + const base::DictionaryValue* args, + std::vector* urls, + bool* is_error) { + *is_error = false; + const IncognitoModePrefs::Availability incognito_availability = + IncognitoModePrefs::GetAvailability(profile_->GetPrefs()); + bool incognito = false; + if (args && args->HasKey(keys::kIncognitoKey)) { + EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kIncognitoKey, + &incognito)); + if (incognito && incognito_availability == IncognitoModePrefs::DISABLED) { + error_ = keys::kIncognitoModeIsDisabled; + *is_error = true; + return false; + } + if (!incognito && incognito_availability == IncognitoModePrefs::FORCED) { + error_ = keys::kIncognitoModeIsForced; + *is_error = true; + return false; + } + } else if (incognito_availability == IncognitoModePrefs::FORCED) { + // If incognito argument is not specified explicitly, we default to + // incognito when forced so by policy. + incognito = true; + } + + // Remove all URLs that are not allowed in an incognito session. Note that a + // ChromeOS guest session is not considered incognito in this case. + if (incognito && !profile_->IsGuestSession()) { + std::string first_url_erased; + for (size_t i = 0; i < urls->size();) { + if (chrome::IsURLAllowedInIncognito((*urls)[i], profile())) { + i++; + } else { + if (first_url_erased.empty()) + first_url_erased = (*urls)[i].spec(); + urls->erase(urls->begin() + i); + } + } + if (urls->empty() && !first_url_erased.empty()) { + error_ = ErrorUtils::FormatErrorMessage( + keys::kURLsNotAllowedInIncognitoError, first_url_erased); + *is_error = true; + return false; + } + } + return incognito; +} + +bool CreateWindowFunction::RunImpl() { + DictionaryValue* args = NULL; + std::vector urls; + WebContents* contents = NULL; + + if (HasOptionalArgument(0)) + EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args)); + + // Look for optional url. + if (args) { + if (args->HasKey(keys::kUrlKey)) { + Value* url_value; + std::vector url_strings; + args->Get(keys::kUrlKey, &url_value); + + // First, get all the URLs the client wants to open. + if (url_value->IsType(Value::TYPE_STRING)) { + std::string url_string; + url_value->GetAsString(&url_string); + url_strings.push_back(url_string); + } else if (url_value->IsType(Value::TYPE_LIST)) { + const ListValue* url_list = static_cast(url_value); + for (size_t i = 0; i < url_list->GetSize(); ++i) { + std::string url_string; + EXTENSION_FUNCTION_VALIDATE(url_list->GetString(i, &url_string)); + url_strings.push_back(url_string); + } + } + + // Second, resolve, validate and convert them to GURLs. + for (std::vector::iterator i = url_strings.begin(); + i != url_strings.end(); ++i) { + GURL url = ExtensionTabUtil::ResolvePossiblyRelativeURL( + *i, GetExtension()); + if (!url.is_valid()) { + error_ = ErrorUtils::FormatErrorMessage( + keys::kInvalidUrlError, *i); + return false; + } + // Don't let the extension crash the browser or renderers. + if (ExtensionTabUtil::IsCrashURL(url)) { + error_ = keys::kNoCrashBrowserError; + return false; + } + urls.push_back(url); + } + } + } + + // Look for optional tab id. + if (args) { + int tab_id = -1; + if (args->HasKey(keys::kTabIdKey)) { + EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kTabIdKey, &tab_id)); + + // Find the tab and detach it from the original window. + TabStripModel* source_tab_strip = NULL; + int tab_index = -1; + if (!GetTabById(tab_id, profile(), include_incognito(), + NULL, &source_tab_strip, + NULL, &tab_index, &error_)) + return false; + contents = source_tab_strip->DetachWebContentsAt(tab_index); + if (!contents) { + error_ = ErrorUtils::FormatErrorMessage( + keys::kTabNotFoundError, base::IntToString(tab_id)); + return false; + } + } + } + + Profile* window_profile = profile(); + Browser::Type window_type = Browser::TYPE_TABBED; + + // panel_create_mode only applies if window is TYPE_PANEL. + PanelManager::CreateMode panel_create_mode = PanelManager::CREATE_AS_DOCKED; + + gfx::Rect window_bounds; + bool focused = true; + bool saw_focus_key = false; + std::string extension_id; + + // Decide whether we are opening a normal window or an incognito window. + bool is_error = true; + bool open_incognito_window = ShouldOpenIncognitoWindow(args, &urls, + &is_error); + if (is_error) { + // error_ member variable is set inside of ShouldOpenIncognitoWindow. + return false; + } + if (open_incognito_window) { + window_profile = window_profile->GetOffTheRecordProfile(); + } + + if (args) { + // Figure out window type before figuring out bounds so that default + // bounds can be set according to the window type. + std::string type_str; + if (args->HasKey(keys::kWindowTypeKey)) { + EXTENSION_FUNCTION_VALIDATE(args->GetString(keys::kWindowTypeKey, + &type_str)); + if (type_str == keys::kWindowTypeValuePopup) { + window_type = Browser::TYPE_POPUP; + extension_id = GetExtension()->id(); + } else if (type_str == keys::kWindowTypeValuePanel || + type_str == keys::kWindowTypeValueDetachedPanel) { + extension_id = GetExtension()->id(); + bool use_panels = false; +#if !defined(OS_ANDROID) + use_panels = PanelManager::ShouldUsePanels(extension_id); +#endif + if (use_panels) { + window_type = Browser::TYPE_PANEL; +#if !defined(OS_CHROMEOS) + // Non-ChromeOS has both docked and detached panel types. + if (type_str == keys::kWindowTypeValueDetachedPanel) + panel_create_mode = PanelManager::CREATE_AS_DETACHED; +#endif + } else { + window_type = Browser::TYPE_POPUP; + } + } else if (type_str != keys::kWindowTypeValueNormal) { + error_ = keys::kInvalidWindowTypeError; + return false; + } + } + + // Initialize default window bounds according to window type. + // In ChromiumOS the default popup bounds is 0x0 which indicates default + // window sizes in PanelBrowserView. In other OSs use the same default + // bounds as windows. +#if !defined(OS_CHROMEOS) + if (Browser::TYPE_TABBED == window_type || + Browser::TYPE_POPUP == window_type) { +#else + if (Browser::TYPE_TABBED == window_type) { +#endif + // Try to position the new browser relative to its originating + // browser window. The call offsets the bounds by kWindowTilePixels + // (defined in WindowSizer to be 10). + // + // NOTE(rafaelw): It's ok if GetCurrentBrowser() returns NULL here. + // GetBrowserWindowBounds will default to saved "default" values for + // the app. + ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT; + WindowSizer::GetBrowserWindowBoundsAndShowState(std::string(), + gfx::Rect(), + GetCurrentBrowser(), + &window_bounds, + &show_state); + } + + if (Browser::TYPE_PANEL == window_type && + PanelManager::CREATE_AS_DETACHED == panel_create_mode) { + window_bounds.set_origin( + PanelManager::GetInstance()->GetDefaultDetachedPanelOrigin()); + } + + // Any part of the bounds can optionally be set by the caller. + int bounds_val = -1; + if (args->HasKey(keys::kLeftKey)) { + EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kLeftKey, + &bounds_val)); + window_bounds.set_x(bounds_val); + } + + if (args->HasKey(keys::kTopKey)) { + EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kTopKey, + &bounds_val)); + window_bounds.set_y(bounds_val); + } + + if (args->HasKey(keys::kWidthKey)) { + EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kWidthKey, + &bounds_val)); + window_bounds.set_width(bounds_val); + } + + if (args->HasKey(keys::kHeightKey)) { + EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kHeightKey, + &bounds_val)); + window_bounds.set_height(bounds_val); + } + + if (args->HasKey(keys::kFocusedKey)) { + EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kFocusedKey, + &focused)); + saw_focus_key = true; + } + } + +#if !defined(OS_CHROMEOS) + if (window_type == Browser::TYPE_PANEL) { + std::string title = + web_app::GenerateApplicationNameFromExtensionId(extension_id); + // Note: Panels ignore all but the first url provided. + Panel* panel = PanelManager::GetInstance()->CreatePanel( + title, window_profile, urls[0], window_bounds, panel_create_mode); + + // Unlike other window types, Panels do not take focus by default. + if (!saw_focus_key || !focused) + panel->ShowInactive(); + else + panel->Show(); + + SetResult( + panel->extension_window_controller()->CreateWindowValueWithTabs( + GetExtension())); + return true; + } +#endif + + // Create a new BrowserWindow. + chrome::HostDesktopType host_desktop_type = chrome::GetActiveDesktop(); + Browser::CreateParams create_params(window_type, window_profile, + host_desktop_type); + if (extension_id.empty()) { + create_params.initial_bounds = window_bounds; + } else { + create_params = Browser::CreateParams::CreateForApp( + window_type, + web_app::GenerateApplicationNameFromExtensionId(extension_id), + window_bounds, + window_profile); + } + create_params.initial_show_state = ui::SHOW_STATE_NORMAL; + create_params.host_desktop_type = chrome::GetActiveDesktop(); + + Browser* new_window = CreateBrowserWindow(create_params, window_profile, + extension_id); + + for (std::vector::iterator i = urls.begin(); i != urls.end(); ++i) { + WebContents* tab = chrome::AddSelectedTabWithURL( + new_window, *i, content::PAGE_TRANSITION_LINK); + if (window_type == Browser::TYPE_PANEL) { + extensions::TabHelper::FromWebContents(tab)-> + SetExtensionAppIconById(extension_id); + } + } + if (contents) { + TabStripModel* target_tab_strip = new_window->tab_strip_model(); + target_tab_strip->InsertWebContentsAt(urls.size(), contents, + TabStripModel::ADD_NONE); + } else if (urls.empty()) { + chrome::NewTab(new_window); + } + chrome::SelectNumberedTab(new_window, 0); + + // Unlike other window types, Panels do not take focus by default. + if (!saw_focus_key && window_type == Browser::TYPE_PANEL) + focused = false; + + if (focused) + new_window->window()->Show(); + else + new_window->window()->ShowInactive(); + + if (new_window->profile()->IsOffTheRecord() && !include_incognito()) { + // Don't expose incognito windows if the extension isn't allowed. + SetResult(Value::CreateNullValue()); + } else { + SetResult( + new_window->extension_window_controller()->CreateWindowValueWithTabs( + GetExtension())); + } + + return true; +} + +bool UpdateWindowFunction::RunImpl() { + int window_id = extension_misc::kUnknownWindowId; + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id)); + DictionaryValue* update_props; + EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &update_props)); + + WindowController* controller; + if (!GetWindowFromWindowID(this, window_id, &controller)) + return false; + +#if defined(OS_WIN) + // Silently ignore changes on the window for metro mode. + if (win8::IsSingleWindowMetroMode()) { + SetResult(controller->CreateWindowValue()); + return true; + } +#endif + + ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT; // No change. + std::string state_str; + if (update_props->HasKey(keys::kShowStateKey)) { + EXTENSION_FUNCTION_VALIDATE(update_props->GetString(keys::kShowStateKey, + &state_str)); + if (state_str == keys::kShowStateValueNormal) { + show_state = ui::SHOW_STATE_NORMAL; + } else if (state_str == keys::kShowStateValueMinimized) { + show_state = ui::SHOW_STATE_MINIMIZED; + } else if (state_str == keys::kShowStateValueMaximized) { + show_state = ui::SHOW_STATE_MAXIMIZED; + } else if (state_str == keys::kShowStateValueFullscreen) { + show_state = ui::SHOW_STATE_FULLSCREEN; + } else { + error_ = keys::kInvalidWindowStateError; + return false; + } + } + + if (show_state != ui::SHOW_STATE_FULLSCREEN && + show_state != ui::SHOW_STATE_DEFAULT) + controller->SetFullscreenMode(false, GetExtension()->url()); + + switch (show_state) { + case ui::SHOW_STATE_MINIMIZED: + controller->window()->Minimize(); + break; + case ui::SHOW_STATE_MAXIMIZED: + controller->window()->Maximize(); + break; + case ui::SHOW_STATE_FULLSCREEN: + if (controller->window()->IsMinimized() || + controller->window()->IsMaximized()) + controller->window()->Restore(); + controller->SetFullscreenMode(true, GetExtension()->url()); + break; + case ui::SHOW_STATE_NORMAL: + controller->window()->Restore(); + break; + default: + break; + } + + gfx::Rect bounds; + if (controller->window()->IsMinimized()) + bounds = controller->window()->GetRestoredBounds(); + else + bounds = controller->window()->GetBounds(); + bool set_bounds = false; + + // Any part of the bounds can optionally be set by the caller. + int bounds_val; + if (update_props->HasKey(keys::kLeftKey)) { + EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger( + keys::kLeftKey, + &bounds_val)); + bounds.set_x(bounds_val); + set_bounds = true; + } + + if (update_props->HasKey(keys::kTopKey)) { + EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger( + keys::kTopKey, + &bounds_val)); + bounds.set_y(bounds_val); + set_bounds = true; + } + + if (update_props->HasKey(keys::kWidthKey)) { + EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger( + keys::kWidthKey, + &bounds_val)); + bounds.set_width(bounds_val); + set_bounds = true; + } + + if (update_props->HasKey(keys::kHeightKey)) { + EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger( + keys::kHeightKey, + &bounds_val)); + bounds.set_height(bounds_val); + set_bounds = true; + } + + if (set_bounds) { + if (show_state == ui::SHOW_STATE_MINIMIZED || + show_state == ui::SHOW_STATE_MAXIMIZED || + show_state == ui::SHOW_STATE_FULLSCREEN) { + error_ = keys::kInvalidWindowStateError; + return false; + } + controller->window()->SetBounds(bounds); + } + + bool active_val = false; + if (update_props->HasKey(keys::kFocusedKey)) { + EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean( + keys::kFocusedKey, &active_val)); + if (active_val) { + if (show_state == ui::SHOW_STATE_MINIMIZED) { + error_ = keys::kInvalidWindowStateError; + return false; + } + controller->window()->Activate(); + } else { + if (show_state == ui::SHOW_STATE_MAXIMIZED || + show_state == ui::SHOW_STATE_FULLSCREEN) { + error_ = keys::kInvalidWindowStateError; + return false; + } + controller->window()->Deactivate(); + } + } + + bool draw_attention = false; + if (update_props->HasKey(keys::kDrawAttentionKey)) { + EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean( + keys::kDrawAttentionKey, &draw_attention)); + controller->window()->FlashFrame(draw_attention); + } + + SetResult(controller->CreateWindowValue()); + + return true; +} + +bool RemoveWindowFunction::RunImpl() { + int window_id = -1; + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id)); + + WindowController* controller; + if (!GetWindowFromWindowID(this, window_id, &controller)) + return false; + +#if defined(OS_WIN) + // In Windows 8 metro mode, an existing Browser instance is reused for + // hosting the extension tab. We should not be closing it as we don't own it. + if (win8::IsSingleWindowMetroMode()) + return false; +#endif + + WindowController::Reason reason; + if (!controller->CanClose(&reason)) { + if (reason == WindowController::REASON_NOT_EDITABLE) + error_ = keys::kTabStripNotEditableError; + return false; + } + controller->window()->Close(); + return true; +} + +// Tabs ------------------------------------------------------------------------ + +bool GetSelectedTabFunction::RunImpl() { + // windowId defaults to "current" window. + int window_id = extension_misc::kCurrentWindowId; + + if (HasOptionalArgument(0)) + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id)); + + Browser* browser = NULL; + if (!GetBrowserFromWindowID(this, window_id, &browser)) + return false; + + TabStripModel* tab_strip = browser->tab_strip_model(); + WebContents* contents = tab_strip->GetActiveWebContents(); + if (!contents) { + error_ = keys::kNoSelectedTabError; + return false; + } + SetResult(ExtensionTabUtil::CreateTabValue(contents, + tab_strip, + tab_strip->active_index(), + GetExtension())); + return true; +} + +bool GetAllTabsInWindowFunction::RunImpl() { + // windowId defaults to "current" window. + int window_id = extension_misc::kCurrentWindowId; + if (HasOptionalArgument(0)) + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id)); + + Browser* browser = NULL; + if (!GetBrowserFromWindowID(this, window_id, &browser)) + return false; + + SetResult(ExtensionTabUtil::CreateTabList(browser, GetExtension())); + + return true; +} + +bool QueryTabsFunction::RunImpl() { + DictionaryValue* query = NULL; + EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &query)); + + QueryArg active = ParseBoolQueryArg(query, keys::kActiveKey); + QueryArg pinned = ParseBoolQueryArg(query, keys::kPinnedKey); + QueryArg selected = ParseBoolQueryArg(query, keys::kHighlightedKey); + QueryArg current_window = ParseBoolQueryArg(query, keys::kCurrentWindowKey); + QueryArg focused_window = + ParseBoolQueryArg(query, keys::kLastFocusedWindowKey); + + QueryArg loading = NOT_SET; + if (query->HasKey(keys::kStatusKey)) { + std::string status; + EXTENSION_FUNCTION_VALIDATE(query->GetString(keys::kStatusKey, &status)); + loading = (status == keys::kStatusValueLoading) ? MATCH_TRUE : MATCH_FALSE; + } + + // It is o.k. to use URLPattern::SCHEME_ALL here because this function does + // not grant access to the content of the tabs, only to seeing their URLs and + // meta data. + URLPattern url_pattern(URLPattern::SCHEME_ALL, ""); + if (query->HasKey(keys::kUrlKey)) { + std::string value; + EXTENSION_FUNCTION_VALIDATE(query->GetString(keys::kUrlKey, &value)); + url_pattern = URLPattern(URLPattern::SCHEME_ALL, value); + } + + std::string title; + if (query->HasKey(keys::kTitleKey)) + EXTENSION_FUNCTION_VALIDATE( + query->GetString(keys::kTitleKey, &title)); + + int window_id = extension_misc::kUnknownWindowId; + if (query->HasKey(keys::kWindowIdKey)) + EXTENSION_FUNCTION_VALIDATE( + query->GetInteger(keys::kWindowIdKey, &window_id)); + + int index = -1; + if (query->HasKey(keys::kIndexKey)) + EXTENSION_FUNCTION_VALIDATE( + query->GetInteger(keys::kIndexKey, &index)); + + std::string window_type; + if (query->HasKey(keys::kWindowTypeLongKey)) + EXTENSION_FUNCTION_VALIDATE( + query->GetString(keys::kWindowTypeLongKey, &window_type)); + + ListValue* result = new ListValue(); + for (BrowserList::const_iterator browser = BrowserList::begin(); + browser != BrowserList::end(); ++browser) { + if (!profile()->IsSameProfile((*browser)->profile())) + continue; + + if (!(*browser)->window()) + continue; + + if (!include_incognito() && profile() != (*browser)->profile()) + continue; + + if (window_id >= 0 && window_id != ExtensionTabUtil::GetWindowId(*browser)) + continue; + + if (window_id == extension_misc::kCurrentWindowId && + *browser != GetCurrentBrowser()) + continue; + + if (!MatchesQueryArg(current_window, *browser == GetCurrentBrowser())) + continue; + + if (!MatchesQueryArg(focused_window, (*browser)->window()->IsActive())) + continue; + + if (!window_type.empty() && + window_type != + (*browser)->extension_window_controller()->GetWindowTypeText()) + continue; + + TabStripModel* tab_strip = (*browser)->tab_strip_model(); + for (int i = 0; i < tab_strip->count(); ++i) { + const WebContents* web_contents = tab_strip->GetWebContentsAt(i); + + if (index > -1 && i != index) + continue; + + if (!MatchesQueryArg(selected, tab_strip->IsTabSelected(i))) + continue; + + if (!MatchesQueryArg(active, i == tab_strip->active_index())) + continue; + + if (!MatchesQueryArg(pinned, tab_strip->IsTabPinned(i))) + continue; + + if (!title.empty() && !MatchPattern(web_contents->GetTitle(), + UTF8ToUTF16(title))) + continue; + + if (!url_pattern.MatchesURL(web_contents->GetURL())) + continue; + + if (!MatchesQueryArg(loading, web_contents->IsLoading())) + continue; + + result->Append(ExtensionTabUtil::CreateTabValue( + web_contents, tab_strip, i, GetExtension())); + } + } + + SetResult(result); + return true; +} + +bool CreateTabFunction::RunImpl() { + DictionaryValue* args = NULL; + EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args)); + + // windowId defaults to "current" window. + int window_id = extension_misc::kCurrentWindowId; + if (args->HasKey(keys::kWindowIdKey)) + EXTENSION_FUNCTION_VALIDATE(args->GetInteger( + keys::kWindowIdKey, &window_id)); + + Browser* browser = NULL; + if (!GetBrowserFromWindowID(this, window_id, &browser)) + return false; + + // Ensure the selected browser is tabbed. + if (!browser->is_type_tabbed() && browser->IsAttemptingToCloseBrowser()) + browser = chrome::FindTabbedBrowser(profile(), include_incognito(), + browser->host_desktop_type()); + + if (!browser || !browser->window()) + return false; + + // TODO(jstritar): Add a constant, chrome.tabs.TAB_ID_ACTIVE, that + // represents the active tab. + WebContents* opener = NULL; + if (args->HasKey(keys::kOpenerTabIdKey)) { + int opener_id = -1; + EXTENSION_FUNCTION_VALIDATE(args->GetInteger( + keys::kOpenerTabIdKey, &opener_id)); + + if (!ExtensionTabUtil::GetTabById( + opener_id, profile(), include_incognito(), + NULL, NULL, &opener, NULL)) + return false; + } + + // TODO(rafaelw): handle setting remaining tab properties: + // -title + // -favIconUrl + + std::string url_string; + GURL url; + if (args->HasKey(keys::kUrlKey)) { + EXTENSION_FUNCTION_VALIDATE(args->GetString(keys::kUrlKey, + &url_string)); + url = ExtensionTabUtil::ResolvePossiblyRelativeURL(url_string, + GetExtension()); + if (!url.is_valid()) { + error_ = ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError, + url_string); + return false; + } + } + + // Don't let extensions crash the browser or renderers. + if (ExtensionTabUtil::IsCrashURL(url)) { + error_ = keys::kNoCrashBrowserError; + return false; + } + + // Default to foreground for the new tab. The presence of 'selected' property + // will override this default. This property is deprecated ('active' should + // be used instead). + bool active = true; + if (args->HasKey(keys::kSelectedKey)) + EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kSelectedKey, &active)); + + // The 'active' property has replaced the 'selected' property. + if (args->HasKey(keys::kActiveKey)) + EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kActiveKey, &active)); + + // Default to not pinning the tab. Setting the 'pinned' property to true + // will override this default. + bool pinned = false; + if (args->HasKey(keys::kPinnedKey)) + EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kPinnedKey, &pinned)); + + // We can't load extension URLs into incognito windows unless the extension + // uses split mode. Special case to fall back to a tabbed window. + if (url.SchemeIs(extensions::kExtensionScheme) && + !GetExtension()->incognito_split_mode() && + browser->profile()->IsOffTheRecord()) { + Profile* profile = browser->profile()->GetOriginalProfile(); + chrome::HostDesktopType desktop_type = browser->host_desktop_type(); + + browser = chrome::FindTabbedBrowser(profile, false, desktop_type); + if (!browser) { + browser = new Browser(Browser::CreateParams(Browser::TYPE_TABBED, + profile, desktop_type)); + browser->window()->Show(); + } + } + + // If index is specified, honor the value, but keep it bound to + // -1 <= index <= tab_strip->count() where -1 invokes the default behavior. + int index = -1; + if (args->HasKey(keys::kIndexKey)) + EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kIndexKey, &index)); + + TabStripModel* tab_strip = browser->tab_strip_model(); + + index = std::min(std::max(index, -1), tab_strip->count()); + + int add_types = active ? TabStripModel::ADD_ACTIVE : + TabStripModel::ADD_NONE; + add_types |= TabStripModel::ADD_FORCE_INDEX; + if (pinned) + add_types |= TabStripModel::ADD_PINNED; + chrome::NavigateParams params(browser, url, content::PAGE_TRANSITION_LINK); + params.disposition = active ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB; + params.tabstrip_index = index; + params.tabstrip_add_types = add_types; + chrome::Navigate(¶ms); + + // The tab may have been created in a different window, so make sure we look + // at the right tab strip. + tab_strip = params.browser->tab_strip_model(); + int new_index = tab_strip->GetIndexOfWebContents(params.target_contents); + if (opener) + tab_strip->SetOpenerOfWebContentsAt(new_index, opener); + + if (active) + params.target_contents->GetView()->SetInitialFocus(); + + // Return data about the newly created tab. + if (has_callback()) { + SetResult(ExtensionTabUtil::CreateTabValue( + params.target_contents, + tab_strip, new_index, GetExtension())); + } + + return true; +} + +bool DuplicateTabFunction::RunImpl() { + int tab_id = -1; + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id)); + + Browser* browser = NULL; + TabStripModel* tab_strip = NULL; + int tab_index = -1; + if (!GetTabById(tab_id, profile(), include_incognito(), + &browser, &tab_strip, NULL, &tab_index, &error_)) { + return false; + } + + WebContents* new_contents = chrome::DuplicateTabAt(browser, tab_index); + if (!has_callback()) + return true; + + int new_index = tab_strip->GetIndexOfWebContents(new_contents); + + // Return data about the newly created tab. + SetResult(ExtensionTabUtil::CreateTabValue( + new_contents, + tab_strip, new_index, GetExtension())); + + return true; +} + +bool GetTabFunction::RunImpl() { + int tab_id = -1; + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id)); + + TabStripModel* tab_strip = NULL; + WebContents* contents = NULL; + int tab_index = -1; + if (!GetTabById(tab_id, profile(), include_incognito(), + NULL, &tab_strip, &contents, &tab_index, &error_)) + return false; + + SetResult(ExtensionTabUtil::CreateTabValue(contents, + tab_strip, + tab_index, + GetExtension())); + return true; +} + +bool GetCurrentTabFunction::RunImpl() { + DCHECK(dispatcher()); + + WebContents* contents = dispatcher()->delegate()->GetAssociatedWebContents(); + if (contents) + SetResult(ExtensionTabUtil::CreateTabValue(contents, GetExtension())); + + return true; +} + +bool HighlightTabsFunction::RunImpl() { + DictionaryValue* info = NULL; + EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &info)); + + // Get the window id from the params; default to current window if omitted. + int window_id = extension_misc::kCurrentWindowId; + if (info->HasKey(keys::kWindowIdKey)) + EXTENSION_FUNCTION_VALIDATE( + info->GetInteger(keys::kWindowIdKey, &window_id)); + + Browser* browser = NULL; + if (!GetBrowserFromWindowID(this, window_id, &browser)) + return false; + + TabStripModel* tabstrip = browser->tab_strip_model(); + ui::ListSelectionModel selection; + int active_index = -1; + + Value* tab_value = NULL; + EXTENSION_FUNCTION_VALIDATE(info->Get(keys::kTabsKey, &tab_value)); + + std::vector tab_indices; + EXTENSION_FUNCTION_VALIDATE(extensions::ReadOneOrMoreIntegers( + tab_value, &tab_indices)); + + // Create a new selection model as we read the list of tab indices. + for (size_t i = 0; i < tab_indices.size(); ++i) { + int index = tab_indices[i]; + + // Make sure the index is in range. + if (!tabstrip->ContainsIndex(index)) { + error_ = ErrorUtils::FormatErrorMessage( + keys::kTabIndexNotFoundError, base::IntToString(index)); + return false; + } + + // By default, we make the first tab in the list active. + if (active_index == -1) + active_index = index; + + selection.AddIndexToSelection(index); + } + + // Make sure they actually specified tabs to select. + if (selection.empty()) { + error_ = keys::kNoHighlightedTabError; + return false; + } + + selection.set_active(active_index); + browser->tab_strip_model()->SetSelectionFromModel(selection); + SetResult( + browser->extension_window_controller()->CreateWindowValueWithTabs( + GetExtension())); + return true; +} + +UpdateTabFunction::UpdateTabFunction() : web_contents_(NULL) { +} + +bool UpdateTabFunction::RunImpl() { + DictionaryValue* update_props = NULL; + EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &update_props)); + + Value* tab_value = NULL; + if (HasOptionalArgument(0)) { + EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &tab_value)); + } + + int tab_id = -1; + WebContents* contents = NULL; + if (tab_value == NULL || tab_value->IsType(Value::TYPE_NULL)) { + Browser* browser = GetCurrentBrowser(); + if (!browser) { + error_ = keys::kNoCurrentWindowError; + return false; + } + contents = browser->tab_strip_model()->GetActiveWebContents(); + if (!contents) { + error_ = keys::kNoSelectedTabError; + return false; + } + tab_id = SessionID::IdForTab(contents); + } else { + EXTENSION_FUNCTION_VALIDATE(tab_value->GetAsInteger(&tab_id)); + } + + int tab_index = -1; + TabStripModel* tab_strip = NULL; + if (!GetTabById(tab_id, profile(), include_incognito(), + NULL, &tab_strip, &contents, &tab_index, &error_)) { + return false; + } + + web_contents_ = contents; + + // TODO(rafaelw): handle setting remaining tab properties: + // -title + // -favIconUrl + + // Navigate the tab to a new location if the url is different. + bool is_async = false; + if (!UpdateURLIfPresent(update_props, tab_id, &is_async)) + return false; + + bool active = false; + // TODO(rafaelw): Setting |active| from js doesn't make much sense. + // Move tab selection management up to window. + if (update_props->HasKey(keys::kSelectedKey)) + EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean( + keys::kSelectedKey, &active)); + + // The 'active' property has replaced 'selected'. + if (update_props->HasKey(keys::kActiveKey)) + EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean( + keys::kActiveKey, &active)); + + if (active) { + if (tab_strip->active_index() != tab_index) { + tab_strip->ActivateTabAt(tab_index, false); + DCHECK_EQ(contents, tab_strip->GetActiveWebContents()); + } + web_contents_->Focus(); + } + + if (update_props->HasKey(keys::kHighlightedKey)) { + bool highlighted = false; + EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean( + keys::kHighlightedKey, &highlighted)); + if (highlighted != tab_strip->IsTabSelected(tab_index)) + tab_strip->ToggleSelectionAt(tab_index); + } + + if (update_props->HasKey(keys::kPinnedKey)) { + bool pinned = false; + EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean( + keys::kPinnedKey, &pinned)); + tab_strip->SetTabPinned(tab_index, pinned); + + // Update the tab index because it may move when being pinned. + tab_index = tab_strip->GetIndexOfWebContents(contents); + } + + if (update_props->HasKey(keys::kOpenerTabIdKey)) { + int opener_id = -1; + EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger( + keys::kOpenerTabIdKey, &opener_id)); + + WebContents* opener_contents = NULL; + if (!ExtensionTabUtil::GetTabById( + opener_id, profile(), include_incognito(), + NULL, NULL, &opener_contents, NULL)) + return false; + + tab_strip->SetOpenerOfWebContentsAt(tab_index, opener_contents); + } + + if (!is_async) { + PopulateResult(); + SendResponse(true); + } + return true; +} + +bool UpdateTabFunction::UpdateURLIfPresent(DictionaryValue* update_props, + int tab_id, + bool* is_async) { + if (!update_props->HasKey(keys::kUrlKey)) + return true; + + std::string url_string; + EXTENSION_FUNCTION_VALIDATE(update_props->GetString( + keys::kUrlKey, &url_string)); + GURL url = ExtensionTabUtil::ResolvePossiblyRelativeURL( + url_string, GetExtension()); + + if (!url.is_valid()) { + error_ = ErrorUtils::FormatErrorMessage( + keys::kInvalidUrlError, url_string); + return false; + } + + // Don't let the extension crash the browser or renderers. + if (ExtensionTabUtil::IsCrashURL(url)) { + error_ = keys::kNoCrashBrowserError; + return false; + } + + // JavaScript URLs can do the same kinds of things as cross-origin XHR, so + // we need to check host permissions before allowing them. + if (url.SchemeIs(chrome::kJavaScriptScheme)) { + if (!GetExtension()->CanExecuteScriptOnPage( + web_contents_->GetURL(), + web_contents_->GetURL(), + tab_id, + NULL, + &error_)) { + return false; + } + + extensions::TabHelper::FromWebContents(web_contents_)-> + script_executor()->ExecuteScript( + extension_id(), + ScriptExecutor::JAVASCRIPT, + url.path(), + ScriptExecutor::TOP_FRAME, + extensions::UserScript::DOCUMENT_IDLE, + ScriptExecutor::MAIN_WORLD, + base::Bind(&UpdateTabFunction::OnExecuteCodeFinished, this)); + + *is_async = true; + return true; + } + + web_contents_->GetController().LoadURL( + url, content::Referrer(), content::PAGE_TRANSITION_LINK, std::string()); + + // The URL of a tab contents never actually changes to a JavaScript URL, so + // this check only makes sense in other cases. + if (!url.SchemeIs(chrome::kJavaScriptScheme)) + DCHECK_EQ(url.spec(), web_contents_->GetURL().spec()); + + return true; +} + +void UpdateTabFunction::PopulateResult() { + if (!has_callback()) + return; + + SetResult(ExtensionTabUtil::CreateTabValue(web_contents_, GetExtension())); +} + +void UpdateTabFunction::OnExecuteCodeFinished(const std::string& error, + int32 on_page_id, + const GURL& url, + const ListValue& script_result) { + if (error.empty()) + PopulateResult(); + else + error_ = error; + SendResponse(error.empty()); +} + +bool MoveTabsFunction::RunImpl() { + Value* tab_value = NULL; + EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &tab_value)); + + std::vector tab_ids; + EXTENSION_FUNCTION_VALIDATE(extensions::ReadOneOrMoreIntegers( + tab_value, &tab_ids)); + + DictionaryValue* update_props = NULL; + int new_index; + EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &update_props)); + EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(keys::kIndexKey, + &new_index)); + + ListValue tab_values; + for (size_t i = 0; i < tab_ids.size(); ++i) { + Browser* source_browser = NULL; + TabStripModel* source_tab_strip = NULL; + WebContents* contents = NULL; + int tab_index = -1; + if (!GetTabById(tab_ids[i], profile(), include_incognito(), + &source_browser, &source_tab_strip, &contents, + &tab_index, &error_)) + return false; + + // Don't let the extension move the tab if the user is dragging tabs. + if (!source_browser->window()->IsTabStripEditable()) { + error_ = keys::kTabStripNotEditableError; + return false; + } + + // Insert the tabs one after another. + new_index += i; + + if (update_props->HasKey(keys::kWindowIdKey)) { + Browser* target_browser = NULL; + int window_id = extension_misc::kUnknownWindowId; + EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger( + keys::kWindowIdKey, &window_id)); + + if (!GetBrowserFromWindowID(this, window_id, &target_browser)) + return false; + + if (!target_browser->window()->IsTabStripEditable()) { + error_ = keys::kTabStripNotEditableError; + return false; + } + + if (!target_browser->is_type_tabbed()) { + error_ = keys::kCanOnlyMoveTabsWithinNormalWindowsError; + return false; + } + + if (target_browser->profile() != source_browser->profile()) { + error_ = keys::kCanOnlyMoveTabsWithinSameProfileError; + return false; + } + + // If windowId is different from the current window, move between windows. + if (ExtensionTabUtil::GetWindowId(target_browser) != + ExtensionTabUtil::GetWindowId(source_browser)) { + TabStripModel* target_tab_strip = target_browser->tab_strip_model(); + WebContents* web_contents = + source_tab_strip->DetachWebContentsAt(tab_index); + if (!web_contents) { + error_ = ErrorUtils::FormatErrorMessage( + keys::kTabNotFoundError, base::IntToString(tab_ids[i])); + return false; + } + + // Clamp move location to the last position. + // This is ">" because it can append to a new index position. + // -1 means set the move location to the last position. + if (new_index > target_tab_strip->count() || new_index < 0) + new_index = target_tab_strip->count(); + + target_tab_strip->InsertWebContentsAt( + new_index, web_contents, TabStripModel::ADD_NONE); + + if (has_callback()) { + tab_values.Append(ExtensionTabUtil::CreateTabValue( + web_contents, + target_tab_strip, + new_index, + GetExtension())); + } + + continue; + } + } + + // Perform a simple within-window move. + // Clamp move location to the last position. + // This is ">=" because the move must be to an existing location. + // -1 means set the move location to the last position. + if (new_index >= source_tab_strip->count() || new_index < 0) + new_index = source_tab_strip->count() - 1; + + if (new_index != tab_index) + source_tab_strip->MoveWebContentsAt(tab_index, new_index, false); + + if (has_callback()) { + tab_values.Append(ExtensionTabUtil::CreateTabValue( + contents, source_tab_strip, new_index, GetExtension())); + } + } + + if (!has_callback()) + return true; + + // Only return the results as an array if there are multiple tabs. + if (tab_ids.size() > 1) { + SetResult(tab_values.DeepCopy()); + } else if (tab_ids.size() == 1) { + Value* value = NULL; + CHECK(tab_values.Get(0, &value)); + SetResult(value->DeepCopy()); + } + return true; +} + +bool ReloadTabFunction::RunImpl() { + bool bypass_cache = false; + if (HasOptionalArgument(1)) { + DictionaryValue* reload_props = NULL; + EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &reload_props)); + + if (reload_props->HasKey(keys::kBypassCache)) { + EXTENSION_FUNCTION_VALIDATE(reload_props->GetBoolean( + keys::kBypassCache, + &bypass_cache)); + } + } + + content::WebContents* web_contents = NULL; + + // If |tab_id| is specified, look for it. Otherwise default to selected tab + // in the current window. + Value* tab_value = NULL; + if (HasOptionalArgument(0)) + EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &tab_value)); + + if (tab_value == NULL || tab_value->IsType(Value::TYPE_NULL)) { + Browser* browser = GetCurrentBrowser(); + if (!browser) { + error_ = keys::kNoCurrentWindowError; + return false; + } + + if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, NULL)) + return false; + } else { + int tab_id = -1; + EXTENSION_FUNCTION_VALIDATE(tab_value->GetAsInteger(&tab_id)); + + Browser* browser = NULL; + if (!GetTabById(tab_id, profile(), include_incognito(), + &browser, NULL, &web_contents, NULL, &error_)) + return false; + } + + if (web_contents->ShowingInterstitialPage()) { + // This does as same as Browser::ReloadInternal. + NavigationEntry* entry = web_contents->GetController().GetActiveEntry(); + OpenURLParams params(entry->GetURL(), Referrer(), CURRENT_TAB, + content::PAGE_TRANSITION_RELOAD, false); + GetCurrentBrowser()->OpenURL(params); + } else if (bypass_cache) { + web_contents->GetController().ReloadIgnoringCache(true); + } else { + web_contents->GetController().Reload(true); + } + + return true; +} + +bool RemoveTabsFunction::RunImpl() { + Value* tab_value = NULL; + EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &tab_value)); + + std::vector tab_ids; + EXTENSION_FUNCTION_VALIDATE(extensions::ReadOneOrMoreIntegers( + tab_value, &tab_ids)); + + for (size_t i = 0; i < tab_ids.size(); ++i) { + Browser* browser = NULL; + WebContents* contents = NULL; + if (!GetTabById(tab_ids[i], profile(), include_incognito(), + &browser, NULL, &contents, NULL, &error_)) + return false; + + // Don't let the extension remove a tab if the user is dragging tabs around. + if (!browser->window()->IsTabStripEditable()) { + error_ = keys::kTabStripNotEditableError; + return false; + } + + // There's a chance that the tab is being dragged, or we're in some other + // nested event loop. This code path ensures that the tab is safely closed + // under such circumstances, whereas |TabStripModel::CloseWebContentsAt()| + // does not. + contents->Close(); + } + return true; +} + +bool CaptureVisibleTabFunction::GetTabToCapture(WebContents** web_contents) { + Browser* browser = NULL; + // windowId defaults to "current" window. + int window_id = extension_misc::kCurrentWindowId; + + if (HasOptionalArgument(0)) + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id)); + + if (!GetBrowserFromWindowID(this, window_id, &browser)) + return false; + + *web_contents = chrome::GetActiveWebContents(browser); + if (*web_contents == NULL) { + error_ = keys::kInternalVisibleTabCaptureError; + return false; + } + + return true; +}; + +bool CaptureVisibleTabFunction::RunImpl() { + PrefServiceBase* service = profile()->GetPrefs(); + if (service->GetBoolean(prefs::kDisableScreenshots)) { + error_ = keys::kScreenshotsDisabled; + return false; + } + + WebContents* web_contents = NULL; + if (!GetTabToCapture(&web_contents)) + return false; + + image_format_ = FORMAT_JPEG; // Default format is JPEG. + image_quality_ = kDefaultQuality; // Default quality setting. + + if (HasOptionalArgument(1)) { + DictionaryValue* options = NULL; + EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &options)); + + if (options->HasKey(keys::kFormatKey)) { + std::string format; + EXTENSION_FUNCTION_VALIDATE( + options->GetString(keys::kFormatKey, &format)); + + if (format == keys::kFormatValueJpeg) { + image_format_ = FORMAT_JPEG; + } else if (format == keys::kFormatValuePng) { + image_format_ = FORMAT_PNG; + } else { + // Schema validation should make this unreachable. + EXTENSION_FUNCTION_VALIDATE(0); + } + } + + if (options->HasKey(keys::kQualityKey)) { + EXTENSION_FUNCTION_VALIDATE( + options->GetInteger(keys::kQualityKey, &image_quality_)); + } + } + + // captureVisibleTab() can return an image containing sensitive information + // that the browser would otherwise protect. Ensure the extension has + // permission to do this. + if (!GetExtension()->CanCaptureVisiblePage( + web_contents->GetURL(), + SessionID::IdForTab(web_contents), + &error_)) { + return false; + } + + RenderViewHost* render_view_host = web_contents->GetRenderViewHost(); + content::RenderWidgetHostView* view = render_view_host->GetView(); + if (!view) { + error_ = keys::kInternalVisibleTabCaptureError; + return false; + } + skia::PlatformBitmap* temp_bitmap = new skia::PlatformBitmap; + render_view_host->CopyFromBackingStore( + gfx::Rect(), + view->GetViewBounds().size(), + base::Bind(&CaptureVisibleTabFunction::CopyFromBackingStoreComplete, + this, + base::Owned(temp_bitmap)), + temp_bitmap); + return true; +} + +void CaptureVisibleTabFunction::CopyFromBackingStoreComplete( + skia::PlatformBitmap* bitmap, + bool succeeded) { + if (succeeded) { + VLOG(1) << "captureVisibleTab() got image from backing store."; + SendResultFromBitmap(bitmap->GetBitmap()); + return; + } + + WebContents* web_contents = NULL; + if (!GetTabToCapture(&web_contents)) { + error_ = keys::kInternalVisibleTabCaptureError; + SendResponse(false); + return; + } + + // Ask the renderer for a snapshot of the tab. + registrar_.Add(this, + chrome::NOTIFICATION_TAB_SNAPSHOT_TAKEN, + content::Source(web_contents)); + AddRef(); // Balanced in CaptureVisibleTabFunction::Observe(). + SnapshotTabHelper::FromWebContents(web_contents)->CaptureSnapshot(); +} + +// If a backing store was not available in CaptureVisibleTabFunction::RunImpl, +// than the renderer was asked for a snapshot. Listen for a notification +// that the snapshot is available. +void CaptureVisibleTabFunction::Observe( + int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + DCHECK(type == chrome::NOTIFICATION_TAB_SNAPSHOT_TAKEN); + + const SkBitmap *screen_capture = + content::Details(details).ptr(); + const bool error = screen_capture->empty(); + + if (error) { + error_ = keys::kInternalVisibleTabCaptureError; + SendResponse(false); + } else { + VLOG(1) << "captureVisibleTab() got image from renderer."; + SendResultFromBitmap(*screen_capture); + } + + Release(); // Balanced in CaptureVisibleTabFunction::RunImpl(). +} + +// Turn a bitmap of the screen into an image, set that image as the result, +// and call SendResponse(). +void CaptureVisibleTabFunction::SendResultFromBitmap( + const SkBitmap& screen_capture) { + std::vector data; + SkAutoLockPixels screen_capture_lock(screen_capture); + bool encoded = false; + std::string mime_type; + switch (image_format_) { + case FORMAT_JPEG: + encoded = gfx::JPEGCodec::Encode( + reinterpret_cast(screen_capture.getAddr32(0, 0)), + gfx::JPEGCodec::FORMAT_SkBitmap, + screen_capture.width(), + screen_capture.height(), + static_cast(screen_capture.rowBytes()), + image_quality_, + &data); + mime_type = keys::kMimeTypeJpeg; + break; + case FORMAT_PNG: + encoded = gfx::PNGCodec::EncodeBGRASkBitmap( + screen_capture, + true, // Discard transparency. + &data); + mime_type = keys::kMimeTypePng; + break; + default: + NOTREACHED() << "Invalid image format."; + } + + if (!encoded) { + error_ = keys::kInternalVisibleTabCaptureError; + SendResponse(false); + return; + } + + std::string base64_result; + base::StringPiece stream_as_string( + reinterpret_cast(vector_as_array(&data)), data.size()); + + base::Base64Encode(stream_as_string, &base64_result); + base64_result.insert(0, base::StringPrintf("data:%s;base64,", + mime_type.c_str())); + SetResult(new StringValue(base64_result)); + SendResponse(true); +} + +void CaptureVisibleTabFunction::RegisterUserPrefs( + PrefServiceSyncable* service) { + service->RegisterBooleanPref(prefs::kDisableScreenshots, false, + PrefServiceSyncable::UNSYNCABLE_PREF); +} + +bool DetectTabLanguageFunction::RunImpl() { + int tab_id = 0; + Browser* browser = NULL; + WebContents* contents = NULL; + + // If |tab_id| is specified, look for it. Otherwise default to selected tab + // in the current window. + if (HasOptionalArgument(0)) { + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id)); + if (!GetTabById(tab_id, profile(), include_incognito(), + &browser, NULL, &contents, NULL, &error_)) { + return false; + } + if (!browser || !contents) + return false; + } else { + browser = GetCurrentBrowser(); + if (!browser) + return false; + contents = browser->tab_strip_model()->GetActiveWebContents(); + if (!contents) + return false; + } + + if (contents->GetController().NeedsReload()) { + // If the tab hasn't been loaded, don't wait for the tab to load. + error_ = keys::kCannotDetermineLanguageOfUnloadedTab; + return false; + } + + AddRef(); // Balanced in GotLanguage(). + + TranslateTabHelper* translate_tab_helper = + TranslateTabHelper::FromWebContents(contents); + if (!translate_tab_helper->language_state().original_language().empty()) { + // Delay the callback invocation until after the current JS call has + // returned. + MessageLoop::current()->PostTask(FROM_HERE, base::Bind( + &DetectTabLanguageFunction::GotLanguage, this, + translate_tab_helper->language_state().original_language())); + return true; + } + // The tab contents does not know its language yet. Let's wait until it + // receives it, or until the tab is closed/navigates to some other page. + registrar_.Add(this, chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED, + content::Source(contents)); + registrar_.Add( + this, chrome::NOTIFICATION_TAB_CLOSING, + content::Source(&(contents->GetController()))); + registrar_.Add( + this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, + content::Source(&(contents->GetController()))); + return true; +} + +void DetectTabLanguageFunction::Observe( + int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + std::string language; + if (type == chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED) + language = *content::Details(details).ptr(); + + registrar_.RemoveAll(); + + // Call GotLanguage in all cases as we want to guarantee the callback is + // called for every API call the extension made. + GotLanguage(language); +} + +void DetectTabLanguageFunction::GotLanguage(const std::string& language) { + SetResult(Value::CreateStringValue(language.c_str())); + SendResponse(true); + + Release(); // Balanced in Run() +} diff --git a/chrome/browser/extensions/api/tabs/tabs.h b/chrome/browser/extensions/api/tabs/tabs.h new file mode 100644 index 0000000..5011c3f --- /dev/null +++ b/chrome/browser/extensions/api/tabs/tabs.h @@ -0,0 +1,208 @@ +// 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_API_TABS_TABS_H__ +#define CHROME_BROWSER_EXTENSIONS_API_TABS_TABS_H__ + +#include +#include + +#include "base/compiler_specific.h" +#include "chrome/browser/extensions/extension_function.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" +#include "googleurl/src/gurl.h" + +class BackingStore; +class GURL; +class PrefServiceSyncable; +class SkBitmap; + +namespace base { +class DictionaryValue; +} + +namespace content { +class WebContents; +} + +namespace skia { +class PlatformBitmap; +} + +// Windows +class GetWindowFunction : public SyncExtensionFunction { + virtual ~GetWindowFunction() {} + virtual bool RunImpl() OVERRIDE; + DECLARE_EXTENSION_FUNCTION_NAME("windows.get") +}; +class GetCurrentWindowFunction : public SyncExtensionFunction { + virtual ~GetCurrentWindowFunction() {} + virtual bool RunImpl() OVERRIDE; + DECLARE_EXTENSION_FUNCTION_NAME("windows.getCurrent") +}; +class GetLastFocusedWindowFunction : public SyncExtensionFunction { + virtual ~GetLastFocusedWindowFunction() {} + virtual bool RunImpl() OVERRIDE; + DECLARE_EXTENSION_FUNCTION_NAME("windows.getLastFocused") +}; +class GetAllWindowsFunction : public SyncExtensionFunction { + virtual ~GetAllWindowsFunction() {} + virtual bool RunImpl() OVERRIDE; + DECLARE_EXTENSION_FUNCTION_NAME("windows.getAll") +}; +class CreateWindowFunction : public SyncExtensionFunction { + virtual ~CreateWindowFunction() {} + virtual bool RunImpl() OVERRIDE; + // Returns whether the window should be created in incognito mode. + // |urls| is the list of urls to open. If we are creating an incognito window, + // the function will remove these urls which may not be opened in incognito + // mode. If window creation leads the browser into an erroneous state, + // |is_error| is set to true (also, error_ member variable is assigned + // the proper error message). + bool ShouldOpenIncognitoWindow(const base::DictionaryValue* args, + std::vector* urls, + bool* is_error); + DECLARE_EXTENSION_FUNCTION_NAME("windows.create") +}; +class UpdateWindowFunction : public SyncExtensionFunction { + virtual ~UpdateWindowFunction() {} + virtual bool RunImpl() OVERRIDE; + DECLARE_EXTENSION_FUNCTION_NAME("windows.update") +}; +class RemoveWindowFunction : public SyncExtensionFunction { + virtual ~RemoveWindowFunction() {} + virtual bool RunImpl() OVERRIDE; + DECLARE_EXTENSION_FUNCTION_NAME("windows.remove") +}; + +// Tabs +class GetTabFunction : public SyncExtensionFunction { + virtual ~GetTabFunction() {} + virtual bool RunImpl() OVERRIDE; + DECLARE_EXTENSION_FUNCTION_NAME("tabs.get") +}; +class GetCurrentTabFunction : public SyncExtensionFunction { + virtual ~GetCurrentTabFunction() {} + virtual bool RunImpl() OVERRIDE; + DECLARE_EXTENSION_FUNCTION_NAME("tabs.getCurrent") +}; +class GetSelectedTabFunction : public SyncExtensionFunction { + virtual ~GetSelectedTabFunction() {} + virtual bool RunImpl() OVERRIDE; + DECLARE_EXTENSION_FUNCTION_NAME("tabs.getSelected") +}; +class GetAllTabsInWindowFunction : public SyncExtensionFunction { + virtual ~GetAllTabsInWindowFunction() {} + virtual bool RunImpl() OVERRIDE; + DECLARE_EXTENSION_FUNCTION_NAME("tabs.getAllInWindow") +}; +class QueryTabsFunction : public SyncExtensionFunction { + virtual ~QueryTabsFunction() {} + virtual bool RunImpl() OVERRIDE; + DECLARE_EXTENSION_FUNCTION_NAME("tabs.query") +}; +class CreateTabFunction : public SyncExtensionFunction { + virtual ~CreateTabFunction() {} + virtual bool RunImpl() OVERRIDE; + DECLARE_EXTENSION_FUNCTION_NAME("tabs.create") +}; +class DuplicateTabFunction : public SyncExtensionFunction { + virtual ~DuplicateTabFunction() {} + virtual bool RunImpl() OVERRIDE; + DECLARE_EXTENSION_FUNCTION_NAME("tabs.duplicate") +}; +class HighlightTabsFunction : public SyncExtensionFunction { + virtual ~HighlightTabsFunction() {} + virtual bool RunImpl() OVERRIDE; + DECLARE_EXTENSION_FUNCTION_NAME("tabs.highlight") +}; +class UpdateTabFunction : public AsyncExtensionFunction { + public: + UpdateTabFunction(); + + protected: + virtual ~UpdateTabFunction() {} + virtual bool UpdateURLIfPresent(base::DictionaryValue* update_props, + int tab_id, + bool* is_async); + virtual void PopulateResult(); + + content::WebContents* web_contents_; + + private: + virtual bool RunImpl() OVERRIDE; + void OnExecuteCodeFinished(const std::string& error, + int32 on_page_id, + const GURL& on_url, + const ListValue& script_result); + + DECLARE_EXTENSION_FUNCTION_NAME("tabs.update") +}; +class MoveTabsFunction : public SyncExtensionFunction { + virtual ~MoveTabsFunction() {} + virtual bool RunImpl() OVERRIDE; + DECLARE_EXTENSION_FUNCTION_NAME("tabs.move") +}; +class ReloadTabFunction : public SyncExtensionFunction { + virtual ~ReloadTabFunction() {} + virtual bool RunImpl() OVERRIDE; + DECLARE_EXTENSION_FUNCTION_NAME("tabs.reload") +}; +class RemoveTabsFunction : public SyncExtensionFunction { + virtual ~RemoveTabsFunction() {} + virtual bool RunImpl() OVERRIDE; + DECLARE_EXTENSION_FUNCTION_NAME("tabs.remove") +}; +class DetectTabLanguageFunction : public AsyncExtensionFunction, + public content::NotificationObserver { + private: + virtual ~DetectTabLanguageFunction() {} + virtual bool RunImpl() OVERRIDE; + + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE; + void GotLanguage(const std::string& language); + content::NotificationRegistrar registrar_; + DECLARE_EXTENSION_FUNCTION_NAME("tabs.detectLanguage") +}; +class CaptureVisibleTabFunction : public AsyncExtensionFunction, + public content::NotificationObserver { + public: + static void RegisterUserPrefs(PrefServiceSyncable* service); + + protected: + enum ImageFormat { + FORMAT_JPEG, + FORMAT_PNG + }; + + // The default quality setting used when encoding jpegs. + static const int kDefaultQuality; + + virtual ~CaptureVisibleTabFunction() {} + virtual bool RunImpl() OVERRIDE; + virtual bool GetTabToCapture(content::WebContents** web_contents); + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE; + void SendResultFromBitmap(const SkBitmap& screen_capture); + + private: + void CopyFromBackingStoreComplete(skia::PlatformBitmap* bitmap, + bool succeeded); + + content::NotificationRegistrar registrar_; + + // The format (JPEG vs PNG) of the resulting image. Set in RunImpl(). + ImageFormat image_format_; + + // Quality setting to use when encoding jpegs. Set in RunImpl(). + int image_quality_; + + DECLARE_EXTENSION_FUNCTION_NAME("tabs.captureVisibleTab") +}; + +#endif // CHROME_BROWSER_EXTENSIONS_API_TABS_TABS_H__ diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc deleted file mode 100644 index 441f422..0000000 --- a/chrome/browser/extensions/api/tabs/tabs_api.cc +++ /dev/null @@ -1,2136 +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/api/tabs/tabs_api.h" - -#include -#include -#include - -#include "base/base64.h" -#include "base/bind.h" -#include "base/command_line.h" -#include "base/logging.h" -#include "base/memory/ref_counted_memory.h" -#include "base/message_loop.h" -#include "base/stl_util.h" -#include "base/string16.h" -#include "base/string_number_conversions.h" -#include "base/string_util.h" -#include "base/stringprintf.h" -#include "base/utf_string_conversions.h" -#include "chrome/browser/extensions/api/tabs/tabs_constants.h" -#include "chrome/browser/extensions/extension_function_dispatcher.h" -#include "chrome/browser/extensions/extension_function_util.h" -#include "chrome/browser/extensions/extension_host.h" -#include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/extensions/extension_tab_util.h" -#include "chrome/browser/extensions/file_reader.h" -#include "chrome/browser/extensions/script_executor.h" -#include "chrome/browser/extensions/tab_helper.h" -#include "chrome/browser/extensions/window_controller.h" -#include "chrome/browser/extensions/window_controller_list.h" -#include "chrome/browser/prefs/incognito_mode_prefs.h" -#include "chrome/browser/prefs/pref_service.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/translate/translate_tab_helper.h" -#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_list.h" -#include "chrome/browser/ui/browser_navigator.h" -#include "chrome/browser/ui/browser_tabstrip.h" -#include "chrome/browser/ui/browser_window.h" -#include "chrome/browser/ui/extensions/shell_window.h" -#include "chrome/browser/ui/host_desktop.h" -#include "chrome/browser/ui/panels/panel_manager.h" -#include "chrome/browser/ui/snapshot_tab_helper.h" -#include "chrome/browser/ui/tabs/tab_strip_model.h" -#include "chrome/browser/ui/window_sizer/window_sizer.h" -#include "chrome/browser/web_applications/web_app.h" -#include "chrome/common/chrome_notification_types.h" -#include "chrome/common/chrome_switches.h" -#include "chrome/common/extensions/api/tabs.h" -#include "chrome/common/extensions/api/windows.h" -#include "chrome/common/extensions/extension.h" -#include "chrome/common/extensions/extension_constants.h" -#include "chrome/common/extensions/extension_file_util.h" -#include "chrome/common/extensions/extension_l10n_util.h" -#include "chrome/common/extensions/extension_manifest_constants.h" -#include "chrome/common/extensions/extension_messages.h" -#include "chrome/common/extensions/message_bundle.h" -#include "chrome/common/extensions/user_script.h" -#include "chrome/common/pref_names.h" -#include "chrome/common/url_constants.h" -#include "content/public/browser/navigation_controller.h" -#include "content/public/browser/navigation_entry.h" -#include "content/public/browser/notification_details.h" -#include "content/public/browser/notification_source.h" -#include "content/public/browser/render_view_host.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" -#include "content/public/common/url_constants.h" -#include "extensions/common/constants.h" -#include "extensions/common/error_utils.h" -#include "skia/ext/image_operations.h" -#include "skia/ext/platform_canvas.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "ui/base/models/list_selection_model.h" -#include "ui/base/ui_base_types.h" -#include "ui/gfx/codec/jpeg_codec.h" -#include "ui/gfx/codec/png_codec.h" - -#if defined(OS_WIN) -#include "win8/util/win8_util.h" -#endif // OS_WIN - -namespace Get = extensions::api::windows::Get; -namespace GetAll = extensions::api::windows::GetAll; -namespace GetCurrent = extensions::api::windows::GetCurrent; -namespace GetLastFocused = extensions::api::windows::GetLastFocused; -namespace errors = extension_manifest_errors; -namespace keys = extensions::tabs_constants; - -using content::BrowserThread; -using content::NavigationController; -using content::NavigationEntry; -using content::OpenURLParams; -using content::Referrer; -using content::RenderViewHost; -using content::WebContents; -using extensions::ErrorUtils; -using extensions::ScriptExecutor; -using extensions::UserScript; -using extensions::WindowController; -using extensions::WindowControllerList; -using extensions::api::tabs::InjectDetails; - -const int TabsCaptureVisibleTabFunction::kDefaultQuality = 90; - -namespace { - -// |error_message| can optionally be passed in a will be set with an appropriate -// message if the window cannot be found by id. -Browser* GetBrowserInProfileWithId(Profile* profile, - const int window_id, - bool include_incognito, - std::string* error_message) { - Profile* incognito_profile = - include_incognito && profile->HasOffTheRecordProfile() ? - profile->GetOffTheRecordProfile() : NULL; - for (BrowserList::const_iterator browser = BrowserList::begin(); - browser != BrowserList::end(); ++browser) { - if (((*browser)->profile() == profile || - (*browser)->profile() == incognito_profile) && - ExtensionTabUtil::GetWindowId(*browser) == window_id && - ((*browser)->window())) - return *browser; - } - - if (error_message) - *error_message = ErrorUtils::FormatErrorMessage( - keys::kWindowNotFoundError, base::IntToString(window_id)); - - return NULL; -} - -bool GetBrowserFromWindowID( - UIThreadExtensionFunction* function, int window_id, Browser** browser) { - if (window_id == extension_misc::kCurrentWindowId) { - *browser = function->GetCurrentBrowser(); - if (!(*browser) || !(*browser)->window()) { - function->SetError(keys::kNoCurrentWindowError); - return false; - } - } else { - std::string error; - *browser = GetBrowserInProfileWithId( - function->profile(), window_id, function->include_incognito(), &error); - if (!*browser) { - function->SetError(error); - return false; - } - } - return true; -} - -bool GetWindowFromWindowID(UIThreadExtensionFunction* function, - int window_id, - WindowController** controller) { - if (window_id == extension_misc::kCurrentWindowId) { - WindowController* extension_window_controller = - function->dispatcher()->delegate()->GetExtensionWindowController(); - // If there is a window controller associated with this extension, use that. - if (extension_window_controller) { - *controller = extension_window_controller; - } else { - // Otherwise get the focused or most recently added window. - *controller = WindowControllerList::GetInstance()-> - CurrentWindowForFunction(function); - } - if (!(*controller)) { - function->SetError(keys::kNoCurrentWindowError); - return false; - } - } else { - *controller = WindowControllerList::GetInstance()-> - FindWindowForFunctionById(function, window_id); - if (!(*controller)) { - function->SetError(ErrorUtils::FormatErrorMessage( - keys::kWindowNotFoundError, base::IntToString(window_id))); - return false; - } - } - return true; -} - -// |error_message| can optionally be passed in and will be set with an -// appropriate message if the tab cannot be found by id. -bool GetTabById(int tab_id, - Profile* profile, - bool include_incognito, - Browser** browser, - TabStripModel** tab_strip, - content::WebContents** contents, - int* tab_index, - std::string* error_message) { - if (ExtensionTabUtil::GetTabById(tab_id, profile, include_incognito, - browser, tab_strip, contents, tab_index)) - return true; - - if (error_message) - *error_message = ErrorUtils::FormatErrorMessage( - keys::kTabNotFoundError, base::IntToString(tab_id)); - - return false; -} - -// A three state enum to distinguish between when a boolean query argument is -// set or not. -enum QueryArg { - NOT_SET = -1, - MATCH_FALSE, - MATCH_TRUE -}; - -bool MatchesQueryArg(QueryArg arg, bool value) { - if (arg == NOT_SET) - return true; - - return (arg == MATCH_TRUE && value) || (arg == MATCH_FALSE && !value); -} - -QueryArg ParseBoolQueryArg(base::DictionaryValue* query, const char* key) { - if (query->HasKey(key)) { - bool value = false; - CHECK(query->GetBoolean(key, &value)); - return value ? MATCH_TRUE : MATCH_FALSE; - } - return NOT_SET; -} - -Browser* CreateBrowserWindow(const Browser::CreateParams& params, - Profile* profile, - const std::string& extension_id) { - bool use_existing_browser_window = false; - -#if defined(OS_WIN) - // In windows 8 metro mode we don't allow windows to be created. - if (win8::IsSingleWindowMetroMode()) - use_existing_browser_window = true; -#endif // OS_WIN - - Browser* new_window = NULL; - if (use_existing_browser_window) - // The false parameter passed below is to ensure that we find a browser - // object matching the profile passed in, instead of the original profile - new_window = chrome::FindTabbedBrowser(profile, false, - params.host_desktop_type); - - if (!new_window) - new_window = new Browser(params); - return new_window; -} - -} // namespace - -// Windows --------------------------------------------------------------------- - -bool WindowsGetFunction::RunImpl() { - scoped_ptr params(Get::Params::Create(*args_)); - EXTENSION_FUNCTION_VALIDATE(params.get()); - - bool populate_tabs = false; - if (params->get_info.get() && params->get_info->populate.get()) - populate_tabs = *params->get_info->populate; - - WindowController* controller; - if (!GetWindowFromWindowID(this, params->window_id, &controller)) - return false; - - if (populate_tabs) - SetResult(controller->CreateWindowValueWithTabs(GetExtension())); - else - SetResult(controller->CreateWindowValue()); - return true; -} - -bool WindowsGetCurrentFunction::RunImpl() { - scoped_ptr params(GetCurrent::Params::Create(*args_)); - EXTENSION_FUNCTION_VALIDATE(params.get()); - - bool populate_tabs = false; - if (params->get_info.get() && params->get_info->populate.get()) - populate_tabs = *params->get_info->populate; - - WindowController* controller; - if (!GetWindowFromWindowID(this, - extension_misc::kCurrentWindowId, - &controller)) { - return false; - } - if (populate_tabs) - SetResult(controller->CreateWindowValueWithTabs(GetExtension())); - else - SetResult(controller->CreateWindowValue()); - return true; -} - -bool WindowsGetLastFocusedFunction::RunImpl() { - scoped_ptr params( - GetLastFocused::Params::Create(*args_)); - EXTENSION_FUNCTION_VALIDATE(params.get()); - - bool populate_tabs = false; - if (params->get_info.get() && params->get_info->populate.get()) - populate_tabs = *params->get_info->populate; - - // Note: currently this returns the last active browser. If we decide to - // include other window types (e.g. panels), we will need to add logic to - // WindowControllerList that mirrors the active behavior of BrowserList. - Browser* browser = chrome::FindAnyBrowser( - profile(), include_incognito(), chrome::GetActiveDesktop()); - if (!browser || !browser->window()) { - error_ = keys::kNoLastFocusedWindowError; - return false; - } - WindowController* controller = - browser->extension_window_controller(); - if (populate_tabs) - SetResult(controller->CreateWindowValueWithTabs(GetExtension())); - else - SetResult(controller->CreateWindowValue()); - return true; -} - -bool WindowsGetAllFunction::RunImpl() { - scoped_ptr params(GetAll::Params::Create(*args_)); - EXTENSION_FUNCTION_VALIDATE(params.get()); - - bool populate_tabs = false; - if (params->get_info.get() && params->get_info->populate.get()) - populate_tabs = *params->get_info->populate; - - ListValue* window_list = new ListValue(); - const WindowControllerList::ControllerList& windows = - WindowControllerList::GetInstance()->windows(); - for (WindowControllerList::ControllerList::const_iterator iter = - windows.begin(); - iter != windows.end(); ++iter) { - if (!this->CanOperateOnWindow(*iter)) - continue; - if (populate_tabs) - window_list->Append((*iter)->CreateWindowValueWithTabs(GetExtension())); - else - window_list->Append((*iter)->CreateWindowValue()); - } - SetResult(window_list); - return true; -} - -bool WindowsCreateFunction::ShouldOpenIncognitoWindow( - const base::DictionaryValue* args, - std::vector* urls, - bool* is_error) { - *is_error = false; - const IncognitoModePrefs::Availability incognito_availability = - IncognitoModePrefs::GetAvailability(profile_->GetPrefs()); - bool incognito = false; - if (args && args->HasKey(keys::kIncognitoKey)) { - EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kIncognitoKey, - &incognito)); - if (incognito && incognito_availability == IncognitoModePrefs::DISABLED) { - error_ = keys::kIncognitoModeIsDisabled; - *is_error = true; - return false; - } - if (!incognito && incognito_availability == IncognitoModePrefs::FORCED) { - error_ = keys::kIncognitoModeIsForced; - *is_error = true; - return false; - } - } else if (incognito_availability == IncognitoModePrefs::FORCED) { - // If incognito argument is not specified explicitly, we default to - // incognito when forced so by policy. - incognito = true; - } - - // Remove all URLs that are not allowed in an incognito session. Note that a - // ChromeOS guest session is not considered incognito in this case. - if (incognito && !profile_->IsGuestSession()) { - std::string first_url_erased; - for (size_t i = 0; i < urls->size();) { - if (chrome::IsURLAllowedInIncognito((*urls)[i], profile())) { - i++; - } else { - if (first_url_erased.empty()) - first_url_erased = (*urls)[i].spec(); - urls->erase(urls->begin() + i); - } - } - if (urls->empty() && !first_url_erased.empty()) { - error_ = ErrorUtils::FormatErrorMessage( - keys::kURLsNotAllowedInIncognitoError, first_url_erased); - *is_error = true; - return false; - } - } - return incognito; -} - -bool WindowsCreateFunction::RunImpl() { - DictionaryValue* args = NULL; - std::vector urls; - WebContents* contents = NULL; - - if (HasOptionalArgument(0)) - EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args)); - - // Look for optional url. - if (args) { - if (args->HasKey(keys::kUrlKey)) { - Value* url_value; - std::vector url_strings; - args->Get(keys::kUrlKey, &url_value); - - // First, get all the URLs the client wants to open. - if (url_value->IsType(Value::TYPE_STRING)) { - std::string url_string; - url_value->GetAsString(&url_string); - url_strings.push_back(url_string); - } else if (url_value->IsType(Value::TYPE_LIST)) { - const ListValue* url_list = static_cast(url_value); - for (size_t i = 0; i < url_list->GetSize(); ++i) { - std::string url_string; - EXTENSION_FUNCTION_VALIDATE(url_list->GetString(i, &url_string)); - url_strings.push_back(url_string); - } - } - - // Second, resolve, validate and convert them to GURLs. - for (std::vector::iterator i = url_strings.begin(); - i != url_strings.end(); ++i) { - GURL url = ExtensionTabUtil::ResolvePossiblyRelativeURL( - *i, GetExtension()); - if (!url.is_valid()) { - error_ = ErrorUtils::FormatErrorMessage( - keys::kInvalidUrlError, *i); - return false; - } - // Don't let the extension crash the browser or renderers. - if (ExtensionTabUtil::IsCrashURL(url)) { - error_ = keys::kNoCrashBrowserError; - return false; - } - urls.push_back(url); - } - } - } - - // Look for optional tab id. - if (args) { - int tab_id = -1; - if (args->HasKey(keys::kTabIdKey)) { - EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kTabIdKey, &tab_id)); - - // Find the tab and detach it from the original window. - TabStripModel* source_tab_strip = NULL; - int tab_index = -1; - if (!GetTabById(tab_id, profile(), include_incognito(), - NULL, &source_tab_strip, - NULL, &tab_index, &error_)) - return false; - contents = source_tab_strip->DetachWebContentsAt(tab_index); - if (!contents) { - error_ = ErrorUtils::FormatErrorMessage( - keys::kTabNotFoundError, base::IntToString(tab_id)); - return false; - } - } - } - - Profile* window_profile = profile(); - Browser::Type window_type = Browser::TYPE_TABBED; - - // panel_create_mode only applies if window is TYPE_PANEL. - PanelManager::CreateMode panel_create_mode = PanelManager::CREATE_AS_DOCKED; - - gfx::Rect window_bounds; - bool focused = true; - bool saw_focus_key = false; - std::string extension_id; - - // Decide whether we are opening a normal window or an incognito window. - bool is_error = true; - bool open_incognito_window = ShouldOpenIncognitoWindow(args, &urls, - &is_error); - if (is_error) { - // error_ member variable is set inside of ShouldOpenIncognitoWindow. - return false; - } - if (open_incognito_window) { - window_profile = window_profile->GetOffTheRecordProfile(); - } - - if (args) { - // Figure out window type before figuring out bounds so that default - // bounds can be set according to the window type. - std::string type_str; - if (args->HasKey(keys::kWindowTypeKey)) { - EXTENSION_FUNCTION_VALIDATE(args->GetString(keys::kWindowTypeKey, - &type_str)); - if (type_str == keys::kWindowTypeValuePopup) { - window_type = Browser::TYPE_POPUP; - extension_id = GetExtension()->id(); - } else if (type_str == keys::kWindowTypeValuePanel || - type_str == keys::kWindowTypeValueDetachedPanel) { - extension_id = GetExtension()->id(); - bool use_panels = false; -#if !defined(OS_ANDROID) - use_panels = PanelManager::ShouldUsePanels(extension_id); -#endif - if (use_panels) { - window_type = Browser::TYPE_PANEL; -#if !defined(OS_CHROMEOS) - // Non-ChromeOS has both docked and detached panel types. - if (type_str == keys::kWindowTypeValueDetachedPanel) - panel_create_mode = PanelManager::CREATE_AS_DETACHED; -#endif - } else { - window_type = Browser::TYPE_POPUP; - } - } else if (type_str != keys::kWindowTypeValueNormal) { - error_ = keys::kInvalidWindowTypeError; - return false; - } - } - - // Initialize default window bounds according to window type. - // In ChromiumOS the default popup bounds is 0x0 which indicates default - // window sizes in PanelBrowserView. In other OSs use the same default - // bounds as windows. -#if !defined(OS_CHROMEOS) - if (Browser::TYPE_TABBED == window_type || - Browser::TYPE_POPUP == window_type) { -#else - if (Browser::TYPE_TABBED == window_type) { -#endif - // Try to position the new browser relative to its originating - // browser window. The call offsets the bounds by kWindowTilePixels - // (defined in WindowSizer to be 10). - // - // NOTE(rafaelw): It's ok if GetCurrentBrowser() returns NULL here. - // GetBrowserWindowBounds will default to saved "default" values for - // the app. - ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT; - WindowSizer::GetBrowserWindowBoundsAndShowState(std::string(), - gfx::Rect(), - GetCurrentBrowser(), - &window_bounds, - &show_state); - } - - if (Browser::TYPE_PANEL == window_type && - PanelManager::CREATE_AS_DETACHED == panel_create_mode) { - window_bounds.set_origin( - PanelManager::GetInstance()->GetDefaultDetachedPanelOrigin()); - } - - // Any part of the bounds can optionally be set by the caller. - int bounds_val = -1; - if (args->HasKey(keys::kLeftKey)) { - EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kLeftKey, - &bounds_val)); - window_bounds.set_x(bounds_val); - } - - if (args->HasKey(keys::kTopKey)) { - EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kTopKey, - &bounds_val)); - window_bounds.set_y(bounds_val); - } - - if (args->HasKey(keys::kWidthKey)) { - EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kWidthKey, - &bounds_val)); - window_bounds.set_width(bounds_val); - } - - if (args->HasKey(keys::kHeightKey)) { - EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kHeightKey, - &bounds_val)); - window_bounds.set_height(bounds_val); - } - - if (args->HasKey(keys::kFocusedKey)) { - EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kFocusedKey, - &focused)); - saw_focus_key = true; - } - } - -#if !defined(OS_CHROMEOS) - if (window_type == Browser::TYPE_PANEL) { - std::string title = - web_app::GenerateApplicationNameFromExtensionId(extension_id); - // Note: Panels ignore all but the first url provided. - Panel* panel = PanelManager::GetInstance()->CreatePanel( - title, window_profile, urls[0], window_bounds, panel_create_mode); - - // Unlike other window types, Panels do not take focus by default. - if (!saw_focus_key || !focused) - panel->ShowInactive(); - else - panel->Show(); - - SetResult( - panel->extension_window_controller()->CreateWindowValueWithTabs( - GetExtension())); - return true; - } -#endif - - // Create a new BrowserWindow. - chrome::HostDesktopType host_desktop_type = chrome::GetActiveDesktop(); - Browser::CreateParams create_params(window_type, window_profile, - host_desktop_type); - if (extension_id.empty()) { - create_params.initial_bounds = window_bounds; - } else { - create_params = Browser::CreateParams::CreateForApp( - window_type, - web_app::GenerateApplicationNameFromExtensionId(extension_id), - window_bounds, - window_profile); - } - create_params.initial_show_state = ui::SHOW_STATE_NORMAL; - create_params.host_desktop_type = chrome::GetActiveDesktop(); - - Browser* new_window = CreateBrowserWindow(create_params, window_profile, - extension_id); - - for (std::vector::iterator i = urls.begin(); i != urls.end(); ++i) { - WebContents* tab = chrome::AddSelectedTabWithURL( - new_window, *i, content::PAGE_TRANSITION_LINK); - if (window_type == Browser::TYPE_PANEL) { - extensions::TabHelper::FromWebContents(tab)-> - SetExtensionAppIconById(extension_id); - } - } - if (contents) { - TabStripModel* target_tab_strip = new_window->tab_strip_model(); - target_tab_strip->InsertWebContentsAt(urls.size(), contents, - TabStripModel::ADD_NONE); - } else if (urls.empty()) { - chrome::NewTab(new_window); - } - chrome::SelectNumberedTab(new_window, 0); - - // Unlike other window types, Panels do not take focus by default. - if (!saw_focus_key && window_type == Browser::TYPE_PANEL) - focused = false; - - if (focused) - new_window->window()->Show(); - else - new_window->window()->ShowInactive(); - - if (new_window->profile()->IsOffTheRecord() && !include_incognito()) { - // Don't expose incognito windows if the extension isn't allowed. - SetResult(Value::CreateNullValue()); - } else { - SetResult( - new_window->extension_window_controller()->CreateWindowValueWithTabs( - GetExtension())); - } - - return true; -} - -bool WindowsUpdateFunction::RunImpl() { - int window_id = extension_misc::kUnknownWindowId; - EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id)); - DictionaryValue* update_props; - EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &update_props)); - - WindowController* controller; - if (!GetWindowFromWindowID(this, window_id, &controller)) - return false; - -#if defined(OS_WIN) - // Silently ignore changes on the window for metro mode. - if (win8::IsSingleWindowMetroMode()) { - SetResult(controller->CreateWindowValue()); - return true; - } -#endif - - ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT; // No change. - std::string state_str; - if (update_props->HasKey(keys::kShowStateKey)) { - EXTENSION_FUNCTION_VALIDATE(update_props->GetString(keys::kShowStateKey, - &state_str)); - if (state_str == keys::kShowStateValueNormal) { - show_state = ui::SHOW_STATE_NORMAL; - } else if (state_str == keys::kShowStateValueMinimized) { - show_state = ui::SHOW_STATE_MINIMIZED; - } else if (state_str == keys::kShowStateValueMaximized) { - show_state = ui::SHOW_STATE_MAXIMIZED; - } else if (state_str == keys::kShowStateValueFullscreen) { - show_state = ui::SHOW_STATE_FULLSCREEN; - } else { - error_ = keys::kInvalidWindowStateError; - return false; - } - } - - if (show_state != ui::SHOW_STATE_FULLSCREEN && - show_state != ui::SHOW_STATE_DEFAULT) - controller->SetFullscreenMode(false, GetExtension()->url()); - - switch (show_state) { - case ui::SHOW_STATE_MINIMIZED: - controller->window()->Minimize(); - break; - case ui::SHOW_STATE_MAXIMIZED: - controller->window()->Maximize(); - break; - case ui::SHOW_STATE_FULLSCREEN: - if (controller->window()->IsMinimized() || - controller->window()->IsMaximized()) - controller->window()->Restore(); - controller->SetFullscreenMode(true, GetExtension()->url()); - break; - case ui::SHOW_STATE_NORMAL: - controller->window()->Restore(); - break; - default: - break; - } - - gfx::Rect bounds; - if (controller->window()->IsMinimized()) - bounds = controller->window()->GetRestoredBounds(); - else - bounds = controller->window()->GetBounds(); - bool set_bounds = false; - - // Any part of the bounds can optionally be set by the caller. - int bounds_val; - if (update_props->HasKey(keys::kLeftKey)) { - EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger( - keys::kLeftKey, - &bounds_val)); - bounds.set_x(bounds_val); - set_bounds = true; - } - - if (update_props->HasKey(keys::kTopKey)) { - EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger( - keys::kTopKey, - &bounds_val)); - bounds.set_y(bounds_val); - set_bounds = true; - } - - if (update_props->HasKey(keys::kWidthKey)) { - EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger( - keys::kWidthKey, - &bounds_val)); - bounds.set_width(bounds_val); - set_bounds = true; - } - - if (update_props->HasKey(keys::kHeightKey)) { - EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger( - keys::kHeightKey, - &bounds_val)); - bounds.set_height(bounds_val); - set_bounds = true; - } - - if (set_bounds) { - if (show_state == ui::SHOW_STATE_MINIMIZED || - show_state == ui::SHOW_STATE_MAXIMIZED || - show_state == ui::SHOW_STATE_FULLSCREEN) { - error_ = keys::kInvalidWindowStateError; - return false; - } - controller->window()->SetBounds(bounds); - } - - bool active_val = false; - if (update_props->HasKey(keys::kFocusedKey)) { - EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean( - keys::kFocusedKey, &active_val)); - if (active_val) { - if (show_state == ui::SHOW_STATE_MINIMIZED) { - error_ = keys::kInvalidWindowStateError; - return false; - } - controller->window()->Activate(); - } else { - if (show_state == ui::SHOW_STATE_MAXIMIZED || - show_state == ui::SHOW_STATE_FULLSCREEN) { - error_ = keys::kInvalidWindowStateError; - return false; - } - controller->window()->Deactivate(); - } - } - - bool draw_attention = false; - if (update_props->HasKey(keys::kDrawAttentionKey)) { - EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean( - keys::kDrawAttentionKey, &draw_attention)); - controller->window()->FlashFrame(draw_attention); - } - - SetResult(controller->CreateWindowValue()); - - return true; -} - -bool WindowsRemoveFunction::RunImpl() { - int window_id = -1; - EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id)); - - WindowController* controller; - if (!GetWindowFromWindowID(this, window_id, &controller)) - return false; - -#if defined(OS_WIN) - // In Windows 8 metro mode, an existing Browser instance is reused for - // hosting the extension tab. We should not be closing it as we don't own it. - if (win8::IsSingleWindowMetroMode()) - return false; -#endif - - WindowController::Reason reason; - if (!controller->CanClose(&reason)) { - if (reason == WindowController::REASON_NOT_EDITABLE) - error_ = keys::kTabStripNotEditableError; - return false; - } - controller->window()->Close(); - return true; -} - -// Tabs ------------------------------------------------------------------------ - -bool TabsGetSelectedFunction::RunImpl() { - // windowId defaults to "current" window. - int window_id = extension_misc::kCurrentWindowId; - - if (HasOptionalArgument(0)) - EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id)); - - Browser* browser = NULL; - if (!GetBrowserFromWindowID(this, window_id, &browser)) - return false; - - TabStripModel* tab_strip = browser->tab_strip_model(); - WebContents* contents = tab_strip->GetActiveWebContents(); - if (!contents) { - error_ = keys::kNoSelectedTabError; - return false; - } - SetResult(ExtensionTabUtil::CreateTabValue(contents, - tab_strip, - tab_strip->active_index(), - GetExtension())); - return true; -} - -bool TabsGetAllInWindowFunction::RunImpl() { - // windowId defaults to "current" window. - int window_id = extension_misc::kCurrentWindowId; - if (HasOptionalArgument(0)) - EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id)); - - Browser* browser = NULL; - if (!GetBrowserFromWindowID(this, window_id, &browser)) - return false; - - SetResult(ExtensionTabUtil::CreateTabList(browser, GetExtension())); - - return true; -} - -bool TabsQueryFunction::RunImpl() { - DictionaryValue* query = NULL; - EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &query)); - - QueryArg active = ParseBoolQueryArg(query, keys::kActiveKey); - QueryArg pinned = ParseBoolQueryArg(query, keys::kPinnedKey); - QueryArg selected = ParseBoolQueryArg(query, keys::kHighlightedKey); - QueryArg current_window = ParseBoolQueryArg(query, keys::kCurrentWindowKey); - QueryArg focused_window = - ParseBoolQueryArg(query, keys::kLastFocusedWindowKey); - - QueryArg loading = NOT_SET; - if (query->HasKey(keys::kStatusKey)) { - std::string status; - EXTENSION_FUNCTION_VALIDATE(query->GetString(keys::kStatusKey, &status)); - loading = (status == keys::kStatusValueLoading) ? MATCH_TRUE : MATCH_FALSE; - } - - // It is o.k. to use URLPattern::SCHEME_ALL here because this function does - // not grant access to the content of the tabs, only to seeing their URLs and - // meta data. - URLPattern url_pattern(URLPattern::SCHEME_ALL, ""); - if (query->HasKey(keys::kUrlKey)) { - std::string value; - EXTENSION_FUNCTION_VALIDATE(query->GetString(keys::kUrlKey, &value)); - url_pattern = URLPattern(URLPattern::SCHEME_ALL, value); - } - - std::string title; - if (query->HasKey(keys::kTitleKey)) - EXTENSION_FUNCTION_VALIDATE( - query->GetString(keys::kTitleKey, &title)); - - int window_id = extension_misc::kUnknownWindowId; - if (query->HasKey(keys::kWindowIdKey)) - EXTENSION_FUNCTION_VALIDATE( - query->GetInteger(keys::kWindowIdKey, &window_id)); - - int index = -1; - if (query->HasKey(keys::kIndexKey)) - EXTENSION_FUNCTION_VALIDATE( - query->GetInteger(keys::kIndexKey, &index)); - - std::string window_type; - if (query->HasKey(keys::kWindowTypeLongKey)) - EXTENSION_FUNCTION_VALIDATE( - query->GetString(keys::kWindowTypeLongKey, &window_type)); - - ListValue* result = new ListValue(); - for (BrowserList::const_iterator browser = BrowserList::begin(); - browser != BrowserList::end(); ++browser) { - if (!profile()->IsSameProfile((*browser)->profile())) - continue; - - if (!(*browser)->window()) - continue; - - if (!include_incognito() && profile() != (*browser)->profile()) - continue; - - if (window_id >= 0 && window_id != ExtensionTabUtil::GetWindowId(*browser)) - continue; - - if (window_id == extension_misc::kCurrentWindowId && - *browser != GetCurrentBrowser()) - continue; - - if (!MatchesQueryArg(current_window, *browser == GetCurrentBrowser())) - continue; - - if (!MatchesQueryArg(focused_window, (*browser)->window()->IsActive())) - continue; - - if (!window_type.empty() && - window_type != - (*browser)->extension_window_controller()->GetWindowTypeText()) - continue; - - TabStripModel* tab_strip = (*browser)->tab_strip_model(); - for (int i = 0; i < tab_strip->count(); ++i) { - const WebContents* web_contents = tab_strip->GetWebContentsAt(i); - - if (index > -1 && i != index) - continue; - - if (!MatchesQueryArg(selected, tab_strip->IsTabSelected(i))) - continue; - - if (!MatchesQueryArg(active, i == tab_strip->active_index())) - continue; - - if (!MatchesQueryArg(pinned, tab_strip->IsTabPinned(i))) - continue; - - if (!title.empty() && !MatchPattern(web_contents->GetTitle(), - UTF8ToUTF16(title))) - continue; - - if (!url_pattern.MatchesURL(web_contents->GetURL())) - continue; - - if (!MatchesQueryArg(loading, web_contents->IsLoading())) - continue; - - result->Append(ExtensionTabUtil::CreateTabValue( - web_contents, tab_strip, i, GetExtension())); - } - } - - SetResult(result); - return true; -} - -bool TabsCreateFunction::RunImpl() { - DictionaryValue* args = NULL; - EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args)); - - // windowId defaults to "current" window. - int window_id = extension_misc::kCurrentWindowId; - if (args->HasKey(keys::kWindowIdKey)) - EXTENSION_FUNCTION_VALIDATE(args->GetInteger( - keys::kWindowIdKey, &window_id)); - - Browser* browser = NULL; - if (!GetBrowserFromWindowID(this, window_id, &browser)) - return false; - - // Ensure the selected browser is tabbed. - if (!browser->is_type_tabbed() && browser->IsAttemptingToCloseBrowser()) - browser = chrome::FindTabbedBrowser(profile(), include_incognito(), - browser->host_desktop_type()); - - if (!browser || !browser->window()) - return false; - - // TODO(jstritar): Add a constant, chrome.tabs.TAB_ID_ACTIVE, that - // represents the active tab. - WebContents* opener = NULL; - if (args->HasKey(keys::kOpenerTabIdKey)) { - int opener_id = -1; - EXTENSION_FUNCTION_VALIDATE(args->GetInteger( - keys::kOpenerTabIdKey, &opener_id)); - - if (!ExtensionTabUtil::GetTabById( - opener_id, profile(), include_incognito(), - NULL, NULL, &opener, NULL)) - return false; - } - - // TODO(rafaelw): handle setting remaining tab properties: - // -title - // -favIconUrl - - std::string url_string; - GURL url; - if (args->HasKey(keys::kUrlKey)) { - EXTENSION_FUNCTION_VALIDATE(args->GetString(keys::kUrlKey, - &url_string)); - url = ExtensionTabUtil::ResolvePossiblyRelativeURL(url_string, - GetExtension()); - if (!url.is_valid()) { - error_ = ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError, - url_string); - return false; - } - } - - // Don't let extensions crash the browser or renderers. - if (ExtensionTabUtil::IsCrashURL(url)) { - error_ = keys::kNoCrashBrowserError; - return false; - } - - // Default to foreground for the new tab. The presence of 'selected' property - // will override this default. This property is deprecated ('active' should - // be used instead). - bool active = true; - if (args->HasKey(keys::kSelectedKey)) - EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kSelectedKey, &active)); - - // The 'active' property has replaced the 'selected' property. - if (args->HasKey(keys::kActiveKey)) - EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kActiveKey, &active)); - - // Default to not pinning the tab. Setting the 'pinned' property to true - // will override this default. - bool pinned = false; - if (args->HasKey(keys::kPinnedKey)) - EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kPinnedKey, &pinned)); - - // We can't load extension URLs into incognito windows unless the extension - // uses split mode. Special case to fall back to a tabbed window. - if (url.SchemeIs(extensions::kExtensionScheme) && - !GetExtension()->incognito_split_mode() && - browser->profile()->IsOffTheRecord()) { - Profile* profile = browser->profile()->GetOriginalProfile(); - chrome::HostDesktopType desktop_type = browser->host_desktop_type(); - - browser = chrome::FindTabbedBrowser(profile, false, desktop_type); - if (!browser) { - browser = new Browser(Browser::CreateParams(Browser::TYPE_TABBED, - profile, desktop_type)); - browser->window()->Show(); - } - } - - // If index is specified, honor the value, but keep it bound to - // -1 <= index <= tab_strip->count() where -1 invokes the default behavior. - int index = -1; - if (args->HasKey(keys::kIndexKey)) - EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kIndexKey, &index)); - - TabStripModel* tab_strip = browser->tab_strip_model(); - - index = std::min(std::max(index, -1), tab_strip->count()); - - int add_types = active ? TabStripModel::ADD_ACTIVE : - TabStripModel::ADD_NONE; - add_types |= TabStripModel::ADD_FORCE_INDEX; - if (pinned) - add_types |= TabStripModel::ADD_PINNED; - chrome::NavigateParams params(browser, url, content::PAGE_TRANSITION_LINK); - params.disposition = active ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB; - params.tabstrip_index = index; - params.tabstrip_add_types = add_types; - chrome::Navigate(¶ms); - - // The tab may have been created in a different window, so make sure we look - // at the right tab strip. - tab_strip = params.browser->tab_strip_model(); - int new_index = tab_strip->GetIndexOfWebContents(params.target_contents); - if (opener) - tab_strip->SetOpenerOfWebContentsAt(new_index, opener); - - if (active) - params.target_contents->GetView()->SetInitialFocus(); - - // Return data about the newly created tab. - if (has_callback()) { - SetResult(ExtensionTabUtil::CreateTabValue( - params.target_contents, - tab_strip, new_index, GetExtension())); - } - - return true; -} - -bool TabsDuplicateFunction::RunImpl() { - int tab_id = -1; - EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id)); - - Browser* browser = NULL; - TabStripModel* tab_strip = NULL; - int tab_index = -1; - if (!GetTabById(tab_id, profile(), include_incognito(), - &browser, &tab_strip, NULL, &tab_index, &error_)) { - return false; - } - - WebContents* new_contents = chrome::DuplicateTabAt(browser, tab_index); - if (!has_callback()) - return true; - - int new_index = tab_strip->GetIndexOfWebContents(new_contents); - - // Return data about the newly created tab. - SetResult(ExtensionTabUtil::CreateTabValue( - new_contents, - tab_strip, new_index, GetExtension())); - - return true; -} - -bool TabsGetFunction::RunImpl() { - int tab_id = -1; - EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id)); - - TabStripModel* tab_strip = NULL; - WebContents* contents = NULL; - int tab_index = -1; - if (!GetTabById(tab_id, profile(), include_incognito(), - NULL, &tab_strip, &contents, &tab_index, &error_)) - return false; - - SetResult(ExtensionTabUtil::CreateTabValue(contents, - tab_strip, - tab_index, - GetExtension())); - return true; -} - -bool TabsGetCurrentFunction::RunImpl() { - DCHECK(dispatcher()); - - WebContents* contents = dispatcher()->delegate()->GetAssociatedWebContents(); - if (contents) - SetResult(ExtensionTabUtil::CreateTabValue(contents, GetExtension())); - - return true; -} - -bool TabsHighlightFunction::RunImpl() { - DictionaryValue* info = NULL; - EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &info)); - - // Get the window id from the params; default to current window if omitted. - int window_id = extension_misc::kCurrentWindowId; - if (info->HasKey(keys::kWindowIdKey)) - EXTENSION_FUNCTION_VALIDATE( - info->GetInteger(keys::kWindowIdKey, &window_id)); - - Browser* browser = NULL; - if (!GetBrowserFromWindowID(this, window_id, &browser)) - return false; - - TabStripModel* tabstrip = browser->tab_strip_model(); - ui::ListSelectionModel selection; - int active_index = -1; - - Value* tab_value = NULL; - EXTENSION_FUNCTION_VALIDATE(info->Get(keys::kTabsKey, &tab_value)); - - std::vector tab_indices; - EXTENSION_FUNCTION_VALIDATE(extensions::ReadOneOrMoreIntegers( - tab_value, &tab_indices)); - - // Create a new selection model as we read the list of tab indices. - for (size_t i = 0; i < tab_indices.size(); ++i) { - int index = tab_indices[i]; - - // Make sure the index is in range. - if (!tabstrip->ContainsIndex(index)) { - error_ = ErrorUtils::FormatErrorMessage( - keys::kTabIndexNotFoundError, base::IntToString(index)); - return false; - } - - // By default, we make the first tab in the list active. - if (active_index == -1) - active_index = index; - - selection.AddIndexToSelection(index); - } - - // Make sure they actually specified tabs to select. - if (selection.empty()) { - error_ = keys::kNoHighlightedTabError; - return false; - } - - selection.set_active(active_index); - browser->tab_strip_model()->SetSelectionFromModel(selection); - SetResult( - browser->extension_window_controller()->CreateWindowValueWithTabs( - GetExtension())); - return true; -} - -TabsUpdateFunction::TabsUpdateFunction() : web_contents_(NULL) { -} - -bool TabsUpdateFunction::RunImpl() { - DictionaryValue* update_props = NULL; - EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &update_props)); - - Value* tab_value = NULL; - if (HasOptionalArgument(0)) { - EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &tab_value)); - } - - int tab_id = -1; - WebContents* contents = NULL; - if (tab_value == NULL || tab_value->IsType(Value::TYPE_NULL)) { - Browser* browser = GetCurrentBrowser(); - if (!browser) { - error_ = keys::kNoCurrentWindowError; - return false; - } - contents = browser->tab_strip_model()->GetActiveWebContents(); - if (!contents) { - error_ = keys::kNoSelectedTabError; - return false; - } - tab_id = SessionID::IdForTab(contents); - } else { - EXTENSION_FUNCTION_VALIDATE(tab_value->GetAsInteger(&tab_id)); - } - - int tab_index = -1; - TabStripModel* tab_strip = NULL; - if (!GetTabById(tab_id, profile(), include_incognito(), - NULL, &tab_strip, &contents, &tab_index, &error_)) { - return false; - } - - web_contents_ = contents; - - // TODO(rafaelw): handle setting remaining tab properties: - // -title - // -favIconUrl - - // Navigate the tab to a new location if the url is different. - bool is_async = false; - if (!UpdateURLIfPresent(update_props, tab_id, &is_async)) - return false; - - bool active = false; - // TODO(rafaelw): Setting |active| from js doesn't make much sense. - // Move tab selection management up to window. - if (update_props->HasKey(keys::kSelectedKey)) - EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean( - keys::kSelectedKey, &active)); - - // The 'active' property has replaced 'selected'. - if (update_props->HasKey(keys::kActiveKey)) - EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean( - keys::kActiveKey, &active)); - - if (active) { - if (tab_strip->active_index() != tab_index) { - tab_strip->ActivateTabAt(tab_index, false); - DCHECK_EQ(contents, tab_strip->GetActiveWebContents()); - } - web_contents_->Focus(); - } - - if (update_props->HasKey(keys::kHighlightedKey)) { - bool highlighted = false; - EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean( - keys::kHighlightedKey, &highlighted)); - if (highlighted != tab_strip->IsTabSelected(tab_index)) - tab_strip->ToggleSelectionAt(tab_index); - } - - if (update_props->HasKey(keys::kPinnedKey)) { - bool pinned = false; - EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean( - keys::kPinnedKey, &pinned)); - tab_strip->SetTabPinned(tab_index, pinned); - - // Update the tab index because it may move when being pinned. - tab_index = tab_strip->GetIndexOfWebContents(contents); - } - - if (update_props->HasKey(keys::kOpenerTabIdKey)) { - int opener_id = -1; - EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger( - keys::kOpenerTabIdKey, &opener_id)); - - WebContents* opener_contents = NULL; - if (!ExtensionTabUtil::GetTabById( - opener_id, profile(), include_incognito(), - NULL, NULL, &opener_contents, NULL)) - return false; - - tab_strip->SetOpenerOfWebContentsAt(tab_index, opener_contents); - } - - if (!is_async) { - PopulateResult(); - SendResponse(true); - } - return true; -} - -bool TabsUpdateFunction::UpdateURLIfPresent(DictionaryValue* update_props, - int tab_id, - bool* is_async) { - if (!update_props->HasKey(keys::kUrlKey)) - return true; - - std::string url_string; - EXTENSION_FUNCTION_VALIDATE(update_props->GetString( - keys::kUrlKey, &url_string)); - GURL url = ExtensionTabUtil::ResolvePossiblyRelativeURL( - url_string, GetExtension()); - - if (!url.is_valid()) { - error_ = ErrorUtils::FormatErrorMessage( - keys::kInvalidUrlError, url_string); - return false; - } - - // Don't let the extension crash the browser or renderers. - if (ExtensionTabUtil::IsCrashURL(url)) { - error_ = keys::kNoCrashBrowserError; - return false; - } - - // JavaScript URLs can do the same kinds of things as cross-origin XHR, so - // we need to check host permissions before allowing them. - if (url.SchemeIs(chrome::kJavaScriptScheme)) { - if (!GetExtension()->CanExecuteScriptOnPage( - web_contents_->GetURL(), - web_contents_->GetURL(), - tab_id, - NULL, - &error_)) { - return false; - } - - extensions::TabHelper::FromWebContents(web_contents_)-> - script_executor()->ExecuteScript( - extension_id(), - ScriptExecutor::JAVASCRIPT, - url.path(), - ScriptExecutor::TOP_FRAME, - extensions::UserScript::DOCUMENT_IDLE, - ScriptExecutor::MAIN_WORLD, - base::Bind(&TabsUpdateFunction::OnExecuteCodeFinished, this)); - - *is_async = true; - return true; - } - - web_contents_->GetController().LoadURL( - url, content::Referrer(), content::PAGE_TRANSITION_LINK, std::string()); - - // The URL of a tab contents never actually changes to a JavaScript URL, so - // this check only makes sense in other cases. - if (!url.SchemeIs(chrome::kJavaScriptScheme)) - DCHECK_EQ(url.spec(), web_contents_->GetURL().spec()); - - return true; -} - -void TabsUpdateFunction::PopulateResult() { - if (!has_callback()) - return; - - SetResult(ExtensionTabUtil::CreateTabValue(web_contents_, GetExtension())); -} - -void TabsUpdateFunction::OnExecuteCodeFinished(const std::string& error, - int32 on_page_id, - const GURL& url, - const ListValue& script_result) { - if (error.empty()) - PopulateResult(); - else - error_ = error; - SendResponse(error.empty()); -} - -bool TabsMoveFunction::RunImpl() { - Value* tab_value = NULL; - EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &tab_value)); - - std::vector tab_ids; - EXTENSION_FUNCTION_VALIDATE(extensions::ReadOneOrMoreIntegers( - tab_value, &tab_ids)); - - DictionaryValue* update_props = NULL; - int new_index; - EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &update_props)); - EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(keys::kIndexKey, - &new_index)); - - ListValue tab_values; - for (size_t i = 0; i < tab_ids.size(); ++i) { - Browser* source_browser = NULL; - TabStripModel* source_tab_strip = NULL; - WebContents* contents = NULL; - int tab_index = -1; - if (!GetTabById(tab_ids[i], profile(), include_incognito(), - &source_browser, &source_tab_strip, &contents, - &tab_index, &error_)) - return false; - - // Don't let the extension move the tab if the user is dragging tabs. - if (!source_browser->window()->IsTabStripEditable()) { - error_ = keys::kTabStripNotEditableError; - return false; - } - - // Insert the tabs one after another. - new_index += i; - - if (update_props->HasKey(keys::kWindowIdKey)) { - Browser* target_browser = NULL; - int window_id = extension_misc::kUnknownWindowId; - EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger( - keys::kWindowIdKey, &window_id)); - - if (!GetBrowserFromWindowID(this, window_id, &target_browser)) - return false; - - if (!target_browser->window()->IsTabStripEditable()) { - error_ = keys::kTabStripNotEditableError; - return false; - } - - if (!target_browser->is_type_tabbed()) { - error_ = keys::kCanOnlyMoveTabsWithinNormalWindowsError; - return false; - } - - if (target_browser->profile() != source_browser->profile()) { - error_ = keys::kCanOnlyMoveTabsWithinSameProfileError; - return false; - } - - // If windowId is different from the current window, move between windows. - if (ExtensionTabUtil::GetWindowId(target_browser) != - ExtensionTabUtil::GetWindowId(source_browser)) { - TabStripModel* target_tab_strip = target_browser->tab_strip_model(); - WebContents* web_contents = - source_tab_strip->DetachWebContentsAt(tab_index); - if (!web_contents) { - error_ = ErrorUtils::FormatErrorMessage( - keys::kTabNotFoundError, base::IntToString(tab_ids[i])); - return false; - } - - // Clamp move location to the last position. - // This is ">" because it can append to a new index position. - // -1 means set the move location to the last position. - if (new_index > target_tab_strip->count() || new_index < 0) - new_index = target_tab_strip->count(); - - target_tab_strip->InsertWebContentsAt( - new_index, web_contents, TabStripModel::ADD_NONE); - - if (has_callback()) { - tab_values.Append(ExtensionTabUtil::CreateTabValue( - web_contents, - target_tab_strip, - new_index, - GetExtension())); - } - - continue; - } - } - - // Perform a simple within-window move. - // Clamp move location to the last position. - // This is ">=" because the move must be to an existing location. - // -1 means set the move location to the last position. - if (new_index >= source_tab_strip->count() || new_index < 0) - new_index = source_tab_strip->count() - 1; - - if (new_index != tab_index) - source_tab_strip->MoveWebContentsAt(tab_index, new_index, false); - - if (has_callback()) { - tab_values.Append(ExtensionTabUtil::CreateTabValue( - contents, source_tab_strip, new_index, GetExtension())); - } - } - - if (!has_callback()) - return true; - - // Only return the results as an array if there are multiple tabs. - if (tab_ids.size() > 1) { - SetResult(tab_values.DeepCopy()); - } else if (tab_ids.size() == 1) { - Value* value = NULL; - CHECK(tab_values.Get(0, &value)); - SetResult(value->DeepCopy()); - } - return true; -} - -bool TabsReloadFunction::RunImpl() { - bool bypass_cache = false; - if (HasOptionalArgument(1)) { - DictionaryValue* reload_props = NULL; - EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &reload_props)); - - if (reload_props->HasKey(keys::kBypassCache)) { - EXTENSION_FUNCTION_VALIDATE(reload_props->GetBoolean( - keys::kBypassCache, - &bypass_cache)); - } - } - - content::WebContents* web_contents = NULL; - - // If |tab_id| is specified, look for it. Otherwise default to selected tab - // in the current window. - Value* tab_value = NULL; - if (HasOptionalArgument(0)) - EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &tab_value)); - - if (tab_value == NULL || tab_value->IsType(Value::TYPE_NULL)) { - Browser* browser = GetCurrentBrowser(); - if (!browser) { - error_ = keys::kNoCurrentWindowError; - return false; - } - - if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, NULL)) - return false; - } else { - int tab_id = -1; - EXTENSION_FUNCTION_VALIDATE(tab_value->GetAsInteger(&tab_id)); - - Browser* browser = NULL; - if (!GetTabById(tab_id, profile(), include_incognito(), - &browser, NULL, &web_contents, NULL, &error_)) - return false; - } - - if (web_contents->ShowingInterstitialPage()) { - // This does as same as Browser::ReloadInternal. - NavigationEntry* entry = web_contents->GetController().GetActiveEntry(); - OpenURLParams params(entry->GetURL(), Referrer(), CURRENT_TAB, - content::PAGE_TRANSITION_RELOAD, false); - GetCurrentBrowser()->OpenURL(params); - } else if (bypass_cache) { - web_contents->GetController().ReloadIgnoringCache(true); - } else { - web_contents->GetController().Reload(true); - } - - return true; -} - -bool TabsRemoveFunction::RunImpl() { - Value* tab_value = NULL; - EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &tab_value)); - - std::vector tab_ids; - EXTENSION_FUNCTION_VALIDATE(extensions::ReadOneOrMoreIntegers( - tab_value, &tab_ids)); - - for (size_t i = 0; i < tab_ids.size(); ++i) { - Browser* browser = NULL; - WebContents* contents = NULL; - if (!GetTabById(tab_ids[i], profile(), include_incognito(), - &browser, NULL, &contents, NULL, &error_)) - return false; - - // Don't let the extension remove a tab if the user is dragging tabs around. - if (!browser->window()->IsTabStripEditable()) { - error_ = keys::kTabStripNotEditableError; - return false; - } - - // There's a chance that the tab is being dragged, or we're in some other - // nested event loop. This code path ensures that the tab is safely closed - // under such circumstances, whereas |TabStripModel::CloseWebContentsAt()| - // does not. - contents->Close(); - } - return true; -} - -bool TabsCaptureVisibleTabFunction::GetTabToCapture( - WebContents** web_contents) { - Browser* browser = NULL; - // windowId defaults to "current" window. - int window_id = extension_misc::kCurrentWindowId; - - if (HasOptionalArgument(0)) - EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id)); - - if (!GetBrowserFromWindowID(this, window_id, &browser)) - return false; - - *web_contents = chrome::GetActiveWebContents(browser); - if (*web_contents == NULL) { - error_ = keys::kInternalVisibleTabCaptureError; - return false; - } - - return true; -}; - -bool TabsCaptureVisibleTabFunction::RunImpl() { - PrefServiceBase* service = profile()->GetPrefs(); - if (service->GetBoolean(prefs::kDisableScreenshots)) { - error_ = keys::kScreenshotsDisabled; - return false; - } - - WebContents* web_contents = NULL; - if (!GetTabToCapture(&web_contents)) - return false; - - image_format_ = FORMAT_JPEG; // Default format is JPEG. - image_quality_ = kDefaultQuality; // Default quality setting. - - if (HasOptionalArgument(1)) { - DictionaryValue* options = NULL; - EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &options)); - - if (options->HasKey(keys::kFormatKey)) { - std::string format; - EXTENSION_FUNCTION_VALIDATE( - options->GetString(keys::kFormatKey, &format)); - - if (format == keys::kFormatValueJpeg) { - image_format_ = FORMAT_JPEG; - } else if (format == keys::kFormatValuePng) { - image_format_ = FORMAT_PNG; - } else { - // Schema validation should make this unreachable. - EXTENSION_FUNCTION_VALIDATE(0); - } - } - - if (options->HasKey(keys::kQualityKey)) { - EXTENSION_FUNCTION_VALIDATE( - options->GetInteger(keys::kQualityKey, &image_quality_)); - } - } - - // captureVisibleTab() can return an image containing sensitive information - // that the browser would otherwise protect. Ensure the extension has - // permission to do this. - if (!GetExtension()->CanCaptureVisiblePage( - web_contents->GetURL(), - SessionID::IdForTab(web_contents), - &error_)) { - return false; - } - - RenderViewHost* render_view_host = web_contents->GetRenderViewHost(); - content::RenderWidgetHostView* view = render_view_host->GetView(); - if (!view) { - error_ = keys::kInternalVisibleTabCaptureError; - return false; - } - skia::PlatformBitmap* temp_bitmap = new skia::PlatformBitmap; - render_view_host->CopyFromBackingStore( - gfx::Rect(), - view->GetViewBounds().size(), - base::Bind(&TabsCaptureVisibleTabFunction::CopyFromBackingStoreComplete, - this, - base::Owned(temp_bitmap)), - temp_bitmap); - return true; -} - -void TabsCaptureVisibleTabFunction::CopyFromBackingStoreComplete( - skia::PlatformBitmap* bitmap, - bool succeeded) { - if (succeeded) { - VLOG(1) << "captureVisibleTab() got image from backing store."; - SendResultFromBitmap(bitmap->GetBitmap()); - return; - } - - WebContents* web_contents = NULL; - if (!GetTabToCapture(&web_contents)) { - error_ = keys::kInternalVisibleTabCaptureError; - SendResponse(false); - return; - } - - // Ask the renderer for a snapshot of the tab. - registrar_.Add(this, - chrome::NOTIFICATION_TAB_SNAPSHOT_TAKEN, - content::Source(web_contents)); - AddRef(); // Balanced in TabsCaptureVisibleTabFunction::Observe(). - SnapshotTabHelper::FromWebContents(web_contents)->CaptureSnapshot(); -} - -// If a backing store was not available in -// TabsCaptureVisibleTabFunction::RunImpl, than the renderer was asked for a -// snapshot. Listen for a notification that the snapshot is available. -void TabsCaptureVisibleTabFunction::Observe( - int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) { - DCHECK(type == chrome::NOTIFICATION_TAB_SNAPSHOT_TAKEN); - - const SkBitmap *screen_capture = - content::Details(details).ptr(); - const bool error = screen_capture->empty(); - - if (error) { - error_ = keys::kInternalVisibleTabCaptureError; - SendResponse(false); - } else { - VLOG(1) << "captureVisibleTab() got image from renderer."; - SendResultFromBitmap(*screen_capture); - } - - Release(); // Balanced in TabsCaptureVisibleTabFunction::RunImpl(). -} - -// Turn a bitmap of the screen into an image, set that image as the result, -// and call SendResponse(). -void TabsCaptureVisibleTabFunction::SendResultFromBitmap( - const SkBitmap& screen_capture) { - std::vector data; - SkAutoLockPixels screen_capture_lock(screen_capture); - bool encoded = false; - std::string mime_type; - switch (image_format_) { - case FORMAT_JPEG: - encoded = gfx::JPEGCodec::Encode( - reinterpret_cast(screen_capture.getAddr32(0, 0)), - gfx::JPEGCodec::FORMAT_SkBitmap, - screen_capture.width(), - screen_capture.height(), - static_cast(screen_capture.rowBytes()), - image_quality_, - &data); - mime_type = keys::kMimeTypeJpeg; - break; - case FORMAT_PNG: - encoded = gfx::PNGCodec::EncodeBGRASkBitmap( - screen_capture, - true, // Discard transparency. - &data); - mime_type = keys::kMimeTypePng; - break; - default: - NOTREACHED() << "Invalid image format."; - } - - if (!encoded) { - error_ = keys::kInternalVisibleTabCaptureError; - SendResponse(false); - return; - } - - std::string base64_result; - base::StringPiece stream_as_string( - reinterpret_cast(vector_as_array(&data)), data.size()); - - base::Base64Encode(stream_as_string, &base64_result); - base64_result.insert(0, base::StringPrintf("data:%s;base64,", - mime_type.c_str())); - SetResult(new StringValue(base64_result)); - SendResponse(true); -} - -void TabsCaptureVisibleTabFunction::RegisterUserPrefs( - PrefServiceSyncable* service) { - service->RegisterBooleanPref(prefs::kDisableScreenshots, false, - PrefServiceSyncable::UNSYNCABLE_PREF); -} - -bool TabsDetectLanguageFunction::RunImpl() { - int tab_id = 0; - Browser* browser = NULL; - WebContents* contents = NULL; - - // If |tab_id| is specified, look for it. Otherwise default to selected tab - // in the current window. - if (HasOptionalArgument(0)) { - EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id)); - if (!GetTabById(tab_id, profile(), include_incognito(), - &browser, NULL, &contents, NULL, &error_)) { - return false; - } - if (!browser || !contents) - return false; - } else { - browser = GetCurrentBrowser(); - if (!browser) - return false; - contents = browser->tab_strip_model()->GetActiveWebContents(); - if (!contents) - return false; - } - - if (contents->GetController().NeedsReload()) { - // If the tab hasn't been loaded, don't wait for the tab to load. - error_ = keys::kCannotDetermineLanguageOfUnloadedTab; - return false; - } - - AddRef(); // Balanced in GotLanguage(). - - TranslateTabHelper* translate_tab_helper = - TranslateTabHelper::FromWebContents(contents); - if (!translate_tab_helper->language_state().original_language().empty()) { - // Delay the callback invocation until after the current JS call has - // returned. - MessageLoop::current()->PostTask(FROM_HERE, base::Bind( - &TabsDetectLanguageFunction::GotLanguage, this, - translate_tab_helper->language_state().original_language())); - return true; - } - // The tab contents does not know its language yet. Let's wait until it - // receives it, or until the tab is closed/navigates to some other page. - registrar_.Add(this, chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED, - content::Source(contents)); - registrar_.Add( - this, chrome::NOTIFICATION_TAB_CLOSING, - content::Source(&(contents->GetController()))); - registrar_.Add( - this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, - content::Source(&(contents->GetController()))); - return true; -} - -void TabsDetectLanguageFunction::Observe( - int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) { - std::string language; - if (type == chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED) - language = *content::Details(details).ptr(); - - registrar_.RemoveAll(); - - // Call GotLanguage in all cases as we want to guarantee the callback is - // called for every API call the extension made. - GotLanguage(language); -} - -void TabsDetectLanguageFunction::GotLanguage(const std::string& language) { - SetResult(Value::CreateStringValue(language.c_str())); - SendResponse(true); - - Release(); // Balanced in Run() -} - -ExecuteCodeInTabFunction::ExecuteCodeInTabFunction() - : execute_tab_id_(-1) { -} - -ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {} - -bool ExecuteCodeInTabFunction::HasPermission() { - if (Init() && - extension_->HasAPIPermissionForTab(execute_tab_id_, - extensions::APIPermission::kTab)) { - return true; - } - return ExtensionFunction::HasPermission(); -} - -bool ExecuteCodeInTabFunction::RunImpl() { - EXTENSION_FUNCTION_VALIDATE(Init()); - - if (!details_->code.get() && !details_->file.get()) { - error_ = keys::kNoCodeOrFileToExecuteError; - return false; - } - if (details_->code.get() && details_->file.get()) { - error_ = keys::kMoreThanOneValuesError; - return false; - } - - content::WebContents* contents = NULL; - - // If |tab_id| is specified, look for the tab. Otherwise default to selected - // tab in the current window. - CHECK_GE(execute_tab_id_, 0); - if (!ExtensionTabUtil::GetTabById(execute_tab_id_, profile(), - include_incognito(), - NULL, NULL, &contents, NULL)) { - return false; - } - - // NOTE: This can give the wrong answer due to race conditions, but it is OK, - // we check again in the renderer. - CHECK(contents); - if (!GetExtension()->CanExecuteScriptOnPage(contents->GetURL(), - contents->GetURL(), - execute_tab_id_, - NULL, - &error_)) { - return false; - } - - if (details_->code.get()) - return Execute(*details_->code); - - CHECK(details_->file.get()); - resource_ = GetExtension()->GetResource(*details_->file); - - if (resource_.extension_root().empty() || resource_.relative_path().empty()) { - error_ = keys::kNoCodeOrFileToExecuteError; - return false; - } - - scoped_refptr file_reader(new FileReader( - resource_, base::Bind(&ExecuteCodeInTabFunction::DidLoadFile, this))); - file_reader->Start(); - - return true; -} - -void ExecuteCodeInTabFunction::OnExecuteCodeFinished(const std::string& error, - int32 on_page_id, - const GURL& on_url, - const ListValue& result) { - if (!error.empty()) - SetError(error); - - SendResponse(error.empty()); -} - -void TabsExecuteScriptFunction::OnExecuteCodeFinished(const std::string& error, - int32 on_page_id, - const GURL& on_url, - const ListValue& result) { - if (error.empty()) - SetResult(result.DeepCopy()); - ExecuteCodeInTabFunction::OnExecuteCodeFinished(error, on_page_id, on_url, - result); -} - -bool ExecuteCodeInTabFunction::Init() { - if (details_.get()) - return true; - - // |tab_id| is optional so it's ok if it's not there. - int tab_id = -1; - args_->GetInteger(0, &tab_id); - - // |details| are not optional. - DictionaryValue* details_value = NULL; - if (!args_->GetDictionary(1, &details_value)) - return false; - scoped_ptr details(new InjectDetails()); - if (!InjectDetails::Populate(*details_value, details.get())) - return false; - - // If the tab ID is -1 then it needs to be converted to the currently active - // tab's ID. - if (tab_id == -1) { - Browser* browser = GetCurrentBrowser(); - if (!browser) - return false; - content::WebContents* web_contents = NULL; - if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, &tab_id)) - return false; - } - - execute_tab_id_ = tab_id; - details_ = details.Pass(); - return true; -} - -void ExecuteCodeInTabFunction::DidLoadFile(bool success, - const std::string& data) { - std::string function_name = name(); - const extensions::Extension* extension = GetExtension(); - - // Check if the file is CSS and needs localization. - if (success && - function_name == TabsInsertCSSFunction::function_name() && - extension != NULL && - data.find( - extensions::MessageBundle::kMessageBegin) != std::string::npos) { - BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, - base::Bind(&ExecuteCodeInTabFunction::LocalizeCSS, this, - data, - extension->id(), - extension->path(), - extension->default_locale())); - } else { - DidLoadAndLocalizeFile(success, data); - } -} - -void ExecuteCodeInTabFunction::LocalizeCSS( - const std::string& data, - const std::string& extension_id, - const FilePath& extension_path, - const std::string& extension_default_locale) { - scoped_ptr localization_messages( - extension_file_util::LoadMessageBundleSubstitutionMap( - extension_path, extension_id, extension_default_locale)); - - // We need to do message replacement on the data, so it has to be mutable. - std::string css_data = data; - std::string error; - extensions::MessageBundle::ReplaceMessagesWithExternalDictionary( - *localization_messages, &css_data, &error); - - // Call back DidLoadAndLocalizeFile on the UI thread. The success parameter - // is always true, because if loading had failed, we wouldn't have had - // anything to localize. - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&ExecuteCodeInTabFunction::DidLoadAndLocalizeFile, this, - true, css_data)); -} - -void ExecuteCodeInTabFunction::DidLoadAndLocalizeFile(bool success, - const std::string& data) { - if (success) { - if (!Execute(data)) - SendResponse(false); - } else { -#if defined(OS_POSIX) - // TODO(viettrungluu): bug: there's no particular reason the path should be - // UTF-8, in which case this may fail. - error_ = ErrorUtils::FormatErrorMessage(keys::kLoadFileError, - resource_.relative_path().value()); -#elif defined(OS_WIN) - error_ = ErrorUtils::FormatErrorMessage(keys::kLoadFileError, - WideToUTF8(resource_.relative_path().value())); -#endif // OS_WIN - SendResponse(false); - } -} - -bool ExecuteCodeInTabFunction::Execute(const std::string& code_string) { - content::WebContents* contents = NULL; - Browser* browser = NULL; - - bool success = ExtensionTabUtil::GetTabById( - execute_tab_id_, profile(), include_incognito(), &browser, NULL, - &contents, NULL) && contents && browser; - - if (!success) - return false; - - const extensions::Extension* extension = GetExtension(); - if (!extension) - return false; - - ScriptExecutor::ScriptType script_type = ScriptExecutor::JAVASCRIPT; - std::string function_name = name(); - if (function_name == TabsInsertCSSFunction::function_name()) { - script_type = ScriptExecutor::CSS; - } else if (function_name != TabsExecuteScriptFunction::function_name()) { - NOTREACHED(); - } - - ScriptExecutor::FrameScope frame_scope = - details_->all_frames.get() && *details_->all_frames ? - ScriptExecutor::ALL_FRAMES : - ScriptExecutor::TOP_FRAME; - - UserScript::RunLocation run_at = UserScript::UNDEFINED; - switch (details_->run_at) { - case InjectDetails::RUN_AT_NONE: - case InjectDetails::RUN_AT_DOCUMENT_IDLE: - run_at = UserScript::DOCUMENT_IDLE; - break; - case InjectDetails::RUN_AT_DOCUMENT_START: - run_at = UserScript::DOCUMENT_START; - break; - case InjectDetails::RUN_AT_DOCUMENT_END: - run_at = UserScript::DOCUMENT_END; - break; - } - CHECK_NE(UserScript::UNDEFINED, run_at); - - extensions::TabHelper::FromWebContents(contents)-> - script_executor()->ExecuteScript( - extension->id(), - script_type, - code_string, - frame_scope, - run_at, - ScriptExecutor::ISOLATED_WORLD, - base::Bind(&ExecuteCodeInTabFunction::OnExecuteCodeFinished, this)); - return true; -} diff --git a/chrome/browser/extensions/api/tabs/tabs_api.h b/chrome/browser/extensions/api/tabs/tabs_api.h deleted file mode 100644 index db362cc..0000000 --- a/chrome/browser/extensions/api/tabs/tabs_api.h +++ /dev/null @@ -1,290 +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_API_TABS_TABS_API_H_ -#define CHROME_BROWSER_EXTENSIONS_API_TABS_TABS_API_H_ - -#include -#include - -#include "base/compiler_specific.h" -#include "chrome/browser/extensions/extension_function.h" -#include "chrome/common/extensions/extension_resource.h" -#include "chrome/common/extensions/user_script.h" -#include "content/public/browser/notification_observer.h" -#include "content/public/browser/notification_registrar.h" -#include "googleurl/src/gurl.h" - -class BackingStore; -class GURL; -class PrefServiceSyncable; -class SkBitmap; - -namespace base { -class DictionaryValue; -} - -namespace content { -class WebContents; -} - -namespace extensions { -namespace api { -namespace tabs { -struct InjectDetails; -} // namespace tabs -} // namespace api -} // namespace extensions - -namespace skia { -class PlatformBitmap; -} - -// Windows -class WindowsGetFunction : public SyncExtensionFunction { - virtual ~WindowsGetFunction() {} - virtual bool RunImpl() OVERRIDE; - DECLARE_EXTENSION_FUNCTION_NAME("windows.get") -}; -class WindowsGetCurrentFunction : public SyncExtensionFunction { - virtual ~WindowsGetCurrentFunction() {} - virtual bool RunImpl() OVERRIDE; - DECLARE_EXTENSION_FUNCTION_NAME("windows.getCurrent") -}; -class WindowsGetLastFocusedFunction : public SyncExtensionFunction { - virtual ~WindowsGetLastFocusedFunction() {} - virtual bool RunImpl() OVERRIDE; - DECLARE_EXTENSION_FUNCTION_NAME("windows.getLastFocused") -}; -class WindowsGetAllFunction : public SyncExtensionFunction { - virtual ~WindowsGetAllFunction() {} - virtual bool RunImpl() OVERRIDE; - DECLARE_EXTENSION_FUNCTION_NAME("windows.getAll") -}; -class WindowsCreateFunction : public SyncExtensionFunction { - virtual ~WindowsCreateFunction() {} - virtual bool RunImpl() OVERRIDE; - // Returns whether the window should be created in incognito mode. - // |urls| is the list of urls to open. If we are creating an incognito window, - // the function will remove these urls which may not be opened in incognito - // mode. If window creation leads the browser into an erroneous state, - // |is_error| is set to true (also, error_ member variable is assigned - // the proper error message). - bool ShouldOpenIncognitoWindow(const base::DictionaryValue* args, - std::vector* urls, - bool* is_error); - DECLARE_EXTENSION_FUNCTION_NAME("windows.create") -}; -class WindowsUpdateFunction : public SyncExtensionFunction { - virtual ~WindowsUpdateFunction() {} - virtual bool RunImpl() OVERRIDE; - DECLARE_EXTENSION_FUNCTION_NAME("windows.update") -}; -class WindowsRemoveFunction : public SyncExtensionFunction { - virtual ~WindowsRemoveFunction() {} - virtual bool RunImpl() OVERRIDE; - DECLARE_EXTENSION_FUNCTION_NAME("windows.remove") -}; - -// Tabs -class TabsGetFunction : public SyncExtensionFunction { - virtual ~TabsGetFunction() {} - virtual bool RunImpl() OVERRIDE; - DECLARE_EXTENSION_FUNCTION_NAME("tabs.get") -}; -class TabsGetCurrentFunction : public SyncExtensionFunction { - virtual ~TabsGetCurrentFunction() {} - virtual bool RunImpl() OVERRIDE; - DECLARE_EXTENSION_FUNCTION_NAME("tabs.getCurrent") -}; -class TabsGetSelectedFunction : public SyncExtensionFunction { - virtual ~TabsGetSelectedFunction() {} - virtual bool RunImpl() OVERRIDE; - DECLARE_EXTENSION_FUNCTION_NAME("tabs.getSelected") -}; -class TabsGetAllInWindowFunction : public SyncExtensionFunction { - virtual ~TabsGetAllInWindowFunction() {} - virtual bool RunImpl() OVERRIDE; - DECLARE_EXTENSION_FUNCTION_NAME("tabs.getAllInWindow") -}; -class TabsQueryFunction : public SyncExtensionFunction { - virtual ~TabsQueryFunction() {} - virtual bool RunImpl() OVERRIDE; - DECLARE_EXTENSION_FUNCTION_NAME("tabs.query") -}; -class TabsCreateFunction : public SyncExtensionFunction { - virtual ~TabsCreateFunction() {} - virtual bool RunImpl() OVERRIDE; - DECLARE_EXTENSION_FUNCTION_NAME("tabs.create") -}; -class TabsDuplicateFunction : public SyncExtensionFunction { - virtual ~TabsDuplicateFunction() {} - virtual bool RunImpl() OVERRIDE; - DECLARE_EXTENSION_FUNCTION_NAME("tabs.duplicate") -}; -class TabsHighlightFunction : public SyncExtensionFunction { - virtual ~TabsHighlightFunction() {} - virtual bool RunImpl() OVERRIDE; - DECLARE_EXTENSION_FUNCTION_NAME("tabs.highlight") -}; -class TabsUpdateFunction : public AsyncExtensionFunction { - public: - TabsUpdateFunction(); - - protected: - virtual ~TabsUpdateFunction() {} - virtual bool UpdateURLIfPresent(base::DictionaryValue* update_props, - int tab_id, - bool* is_async); - virtual void PopulateResult(); - - content::WebContents* web_contents_; - - private: - virtual bool RunImpl() OVERRIDE; - void OnExecuteCodeFinished(const std::string& error, - int32 on_page_id, - const GURL& on_url, - const ListValue& script_result); - - DECLARE_EXTENSION_FUNCTION_NAME("tabs.update") -}; -class TabsMoveFunction : public SyncExtensionFunction { - virtual ~TabsMoveFunction() {} - virtual bool RunImpl() OVERRIDE; - DECLARE_EXTENSION_FUNCTION_NAME("tabs.move") -}; -class TabsReloadFunction : public SyncExtensionFunction { - virtual ~TabsReloadFunction() {} - virtual bool RunImpl() OVERRIDE; - DECLARE_EXTENSION_FUNCTION_NAME("tabs.reload") -}; -class TabsRemoveFunction : public SyncExtensionFunction { - virtual ~TabsRemoveFunction() {} - virtual bool RunImpl() OVERRIDE; - DECLARE_EXTENSION_FUNCTION_NAME("tabs.remove") -}; -class TabsDetectLanguageFunction : public AsyncExtensionFunction, - public content::NotificationObserver { - private: - virtual ~TabsDetectLanguageFunction() {} - virtual bool RunImpl() OVERRIDE; - - virtual void Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) OVERRIDE; - void GotLanguage(const std::string& language); - content::NotificationRegistrar registrar_; - DECLARE_EXTENSION_FUNCTION_NAME("tabs.detectLanguage") -}; -class TabsCaptureVisibleTabFunction : public AsyncExtensionFunction, - public content::NotificationObserver { - public: - static void RegisterUserPrefs(PrefServiceSyncable* service); - - protected: - enum ImageFormat { - FORMAT_JPEG, - FORMAT_PNG - }; - - // The default quality setting used when encoding jpegs. - static const int kDefaultQuality; - - virtual ~TabsCaptureVisibleTabFunction() {} - virtual bool RunImpl() OVERRIDE; - virtual bool GetTabToCapture(content::WebContents** web_contents); - virtual void Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) OVERRIDE; - void SendResultFromBitmap(const SkBitmap& screen_capture); - - private: - void CopyFromBackingStoreComplete(skia::PlatformBitmap* bitmap, - bool succeeded); - - content::NotificationRegistrar registrar_; - - // The format (JPEG vs PNG) of the resulting image. Set in RunImpl(). - ImageFormat image_format_; - - // Quality setting to use when encoding jpegs. Set in RunImpl(). - int image_quality_; - - DECLARE_EXTENSION_FUNCTION_NAME("tabs.captureVisibleTab") -}; - -// Implement API call tabs.executeScript and tabs.insertCSS. -class ExecuteCodeInTabFunction : public AsyncExtensionFunction { - public: - ExecuteCodeInTabFunction(); - - protected: - virtual ~ExecuteCodeInTabFunction(); - - // ExtensionFunction: - virtual bool HasPermission() OVERRIDE; - virtual bool RunImpl() OVERRIDE; - - // Message handler. - virtual void OnExecuteCodeFinished(const std::string& error, - int32 on_page_id, - const GURL& on_url, - const ListValue& script_result); - - private: - // Initialize the |execute_tab_id_| and |details_| if they haven't already - // been. Returns whether initialization was successful. - bool Init(); - - // Called when contents from the file whose path is specified in JSON - // arguments has been loaded. - void DidLoadFile(bool success, const std::string& data); - - // Runs on FILE thread. Loads message bundles for the extension and - // localizes the CSS data. Calls back DidLoadAndLocalizeFile on the UI thread. - void LocalizeCSS( - const std::string& data, - const std::string& extension_id, - const FilePath& extension_path, - const std::string& extension_default_locale); - - // Called when contents from the loaded file have been localized. - void DidLoadAndLocalizeFile(bool success, const std::string& data); - - // Run in UI thread. Code string contains the code to be executed. Returns - // true on success. If true is returned, this does an AddRef. - bool Execute(const std::string& code_string); - - // Id of tab which executes code. - int execute_tab_id_; - - // The injection details. - scoped_ptr details_; - - // Contains extension resource built from path of file which is - // specified in JSON arguments. - ExtensionResource resource_; -}; - -class TabsExecuteScriptFunction : public ExecuteCodeInTabFunction { - private: - virtual ~TabsExecuteScriptFunction() {} - - virtual void OnExecuteCodeFinished(const std::string& error, - int32 on_page_id, - const GURL& on_url, - const ListValue& script_result) OVERRIDE; - - DECLARE_EXTENSION_FUNCTION_NAME("tabs.executeScript") -}; - -class TabsInsertCSSFunction : public ExecuteCodeInTabFunction { - private: - virtual ~TabsInsertCSSFunction() {} - - DECLARE_EXTENSION_FUNCTION_NAME("tabs.insertCSS") -}; - -#endif // CHROME_BROWSER_EXTENSIONS_API_TABS_TABS_API_H_ diff --git a/chrome/browser/extensions/api/tabs/tabs_interactive_test.cc b/chrome/browser/extensions/api/tabs/tabs_interactive_test.cc index c18a161..c5a0dd5 100644 --- a/chrome/browser/extensions/api/tabs/tabs_interactive_test.cc +++ b/chrome/browser/extensions/api/tabs/tabs_interactive_test.cc @@ -2,16 +2,17 @@ // 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/tabs/tabs.h" + #include "base/values.h" -#include "chrome/browser/extensions/api/tabs/tabs_api.h" #include "chrome/browser/extensions/api/tabs/tabs_constants.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/extensions/extension_function_test_utils.h" #include "chrome/browser/extensions/extension_tab_util.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_window.h" -#include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/interactive_test_utils.h" +#include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" namespace keys = extensions::tabs_constants; @@ -31,8 +32,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, MAYBE_GetLastFocusedWindow) { Browser* new_browser = CreateBrowser(browser()->profile()); int focused_window_id = ExtensionTabUtil::GetWindowId(new_browser); - scoped_refptr function = - new WindowsGetLastFocusedFunction(); + scoped_refptr function = + new GetLastFocusedWindowFunction(); scoped_refptr extension(utils::CreateEmptyExtension()); function->set_extension(extension.get()); scoped_ptr result(utils::ToDictionary( @@ -46,7 +47,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, MAYBE_GetLastFocusedWindow) { ListValue* tabs = NULL; EXPECT_FALSE(result.get()->GetList(keys::kTabsKey, &tabs)); - function = new WindowsGetLastFocusedFunction(); + function = new GetLastFocusedWindowFunction(); function->set_extension(extension.get()); result.reset(utils::ToDictionary( utils::RunFunctionAndReturnSingleResult(function.get(), @@ -82,7 +83,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, DISABLED_QueryLastFocusedWindowTabs) { int focused_window_id = ExtensionTabUtil::GetWindowId(focused_window); // Get tabs in the 'last focused' window called from non-focused browser. - scoped_refptr function = new TabsQueryFunction(); + scoped_refptr function = new QueryTabsFunction(); scoped_ptr result(utils::ToList( utils::RunFunctionAndReturnSingleResult(function.get(), "[{\"lastFocusedWindow\":true}]", @@ -99,7 +100,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, DISABLED_QueryLastFocusedWindowTabs) { } // Get tabs NOT in the 'last focused' window called from the focused browser. - function = new TabsQueryFunction(); + function = new QueryTabsFunction(); result.reset(utils::ToList( utils::RunFunctionAndReturnSingleResult(function.get(), "[{\"lastFocusedWindow\":false}]", diff --git a/chrome/browser/extensions/api/tabs/tabs_test.cc b/chrome/browser/extensions/api/tabs/tabs_test.cc index ec96b6a..30f192c 100644 --- a/chrome/browser/extensions/api/tabs/tabs_test.cc +++ b/chrome/browser/extensions/api/tabs/tabs_test.cc @@ -2,15 +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/extensions/api/tabs/tabs.h" #include #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" -#include "base/string_util.h" #include "base/stringprintf.h" +#include "base/string_util.h" #include "base/values.h" -#include "chrome/browser/extensions/api/tabs/tabs_api.h" #include "chrome/browser/extensions/api/tabs/tabs_constants.h" #include "chrome/browser/extensions/extension_function_test_utils.h" #include "chrome/browser/extensions/extension_tab_util.h" @@ -38,7 +38,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetWindow) { int window_id = ExtensionTabUtil::GetWindowId(browser()); // Invalid window ID error. - scoped_refptr function = new WindowsGetFunction(); + scoped_refptr function = new GetWindowFunction(); scoped_refptr extension(utils::CreateEmptyExtension()); function->set_extension(extension.get()); EXPECT_TRUE(MatchPattern( @@ -55,7 +55,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetWindow) { else bounds = browser()->window()->GetBounds(); - function = new WindowsGetFunction(); + function = new GetWindowFunction(); function->set_extension(extension.get()); scoped_ptr result(utils::ToDictionary( utils::RunFunctionAndReturnSingleResult( @@ -71,7 +71,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetWindow) { EXPECT_EQ(bounds.height(), utils::GetInteger(result.get(), "height")); // With "populate" enabled. - function = new WindowsGetFunction(); + function = new GetWindowFunction(); function->set_extension(extension.get()); result.reset(utils::ToDictionary( utils::RunFunctionAndReturnSingleResult( @@ -96,7 +96,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetWindow) { // Popup. Browser* popup_browser = new Browser( Browser::CreateParams(Browser::TYPE_POPUP, browser()->profile())); - function = new WindowsGetFunction(); + function = new GetWindowFunction(); function->set_extension(extension.get()); result.reset(utils::ToDictionary( utils::RunFunctionAndReturnSingleResult( @@ -109,7 +109,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetWindow) { // Panel. Browser* panel_browser = new Browser( Browser::CreateParams(Browser::TYPE_PANEL, browser()->profile())); - function = new WindowsGetFunction(); + function = new GetWindowFunction(); function->set_extension(extension.get()); result.reset(utils::ToDictionary( utils::RunFunctionAndReturnSingleResult( @@ -124,7 +124,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetWindow) { int incognito_window_id = ExtensionTabUtil::GetWindowId(incognito_browser); // Without "include_incognito". - function = new WindowsGetFunction(); + function = new GetWindowFunction(); function->set_extension(extension.get()); EXPECT_TRUE(MatchPattern( utils::RunFunctionAndReturnError( @@ -134,7 +134,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetWindow) { keys::kWindowNotFoundError)); // With "include_incognito". - function = new WindowsGetFunction(); + function = new GetWindowFunction(); function->set_extension(extension.get()); result.reset(utils::ToDictionary( utils::RunFunctionAndReturnSingleResult( @@ -151,8 +151,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetCurrentWindow) { int new_id = ExtensionTabUtil::GetWindowId(new_browser); // Get the current window using new_browser. - scoped_refptr function = - new WindowsGetCurrentFunction(); + scoped_refptr function = + new GetCurrentWindowFunction(); scoped_refptr extension(utils::CreateEmptyExtension()); function->set_extension(extension.get()); scoped_ptr result(utils::ToDictionary( @@ -167,7 +167,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetCurrentWindow) { EXPECT_FALSE(result.get()->GetList(keys::kTabsKey, &tabs)); // Get the current window using the old window and make the tabs populated. - function = new WindowsGetCurrentFunction(); + function = new GetCurrentWindowFunction(); function->set_extension(extension.get()); result.reset(utils::ToDictionary( utils::RunFunctionAndReturnSingleResult(function.get(), @@ -192,7 +192,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetAllWindows) { window_ids.insert(ExtensionTabUtil::GetWindowId(new_browser)); } - scoped_refptr function = new WindowsGetAllFunction(); + scoped_refptr function = new GetAllWindowsFunction(); scoped_refptr extension(utils::CreateEmptyExtension()); function->set_extension(extension.get()); scoped_ptr result(utils::ToList( @@ -215,7 +215,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetAllWindows) { EXPECT_EQ(window_ids, result_ids); result_ids.clear(); - function = new WindowsGetAllFunction(); + function = new GetAllWindowsFunction(); function->set_extension(extension.get()); result.reset(utils::ToList( utils::RunFunctionAndReturnSingleResult(function.get(), @@ -240,8 +240,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetAllWindows) { IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, UpdateNoPermissions) { // The test empty extension has no permissions, therefore it should not get // tab data in the function result. - scoped_refptr update_tab_function( - new TabsUpdateFunction()); + scoped_refptr update_tab_function(new UpdateTabFunction()); scoped_refptr empty_extension( utils::CreateEmptyExtension()); update_tab_function->set_extension(empty_extension.get()); @@ -266,7 +265,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, IncognitoModePrefs::SetAvailability(browser()->profile()->GetPrefs(), IncognitoModePrefs::FORCED); // Run without an explicit "incognito" param. - scoped_refptr function(new WindowsCreateFunction()); + scoped_refptr function(new CreateWindowFunction()); scoped_refptr extension(utils::CreateEmptyExtension()); function->set_extension(extension.get()); scoped_ptr result(utils::ToDictionary( @@ -285,7 +284,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, // Now try creating a window from incognito window. Browser* incognito_browser = CreateIncognitoBrowser(); // Run without an explicit "incognito" param. - function = new WindowsCreateFunction(); + function = new CreateWindowFunction(); function->set_extension(extension.get()); result.reset(utils::ToDictionary( utils::RunFunctionAndReturnSingleResult( @@ -307,7 +306,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, IncognitoModePrefs::SetAvailability(browser()->profile()->GetPrefs(), IncognitoModePrefs::FORCED); // Run without an explicit "incognito" param. - scoped_refptr function = new WindowsCreateFunction(); + scoped_refptr function = new CreateWindowFunction(); scoped_refptr extension(utils::CreateEmptyExtension()); function->set_extension(extension.get()); scoped_ptr result(utils::ToDictionary( @@ -325,7 +324,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, // Now try creating a window from incognito window. Browser* incognito_browser = CreateIncognitoBrowser(); // Run without an explicit "incognito" param. - function = new WindowsCreateFunction(); + function = new CreateWindowFunction(); function->set_extension(extension.get()); result.reset(utils::ToDictionary( utils::RunFunctionAndReturnSingleResult(function.get(), @@ -348,7 +347,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, IncognitoModePrefs::FORCED); // Run with an explicit "incognito" param. - scoped_refptr function = new WindowsCreateFunction(); + scoped_refptr function = new CreateWindowFunction(); scoped_refptr extension(utils::CreateEmptyExtension()); function->set_extension(extension.get()); EXPECT_TRUE(MatchPattern( @@ -360,7 +359,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, // Now try opening a normal window from incognito window. Browser* incognito_browser = CreateIncognitoBrowser(); // Run with an explicit "incognito" param. - function = new WindowsCreateFunction(); + function = new CreateWindowFunction(); function->set_extension(extension.get()); EXPECT_TRUE(MatchPattern( utils::RunFunctionAndReturnError(function.get(), @@ -379,7 +378,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, IncognitoModePrefs::SetAvailability(browser()->profile()->GetPrefs(), IncognitoModePrefs::DISABLED); // Run in normal window. - scoped_refptr function = new WindowsCreateFunction(); + scoped_refptr function = new CreateWindowFunction(); scoped_refptr extension(utils::CreateEmptyExtension()); function->set_extension(extension.get()); EXPECT_TRUE(MatchPattern( @@ -389,7 +388,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, keys::kIncognitoModeIsDisabled)); // Run in incognito window. - function = new WindowsCreateFunction(); + function = new CreateWindowFunction(); function->set_extension(extension.get()); EXPECT_TRUE(MatchPattern( utils::RunFunctionAndReturnError(function.get(), @@ -408,7 +407,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, QueryCurrentWindowTabs) { int window_id = ExtensionTabUtil::GetWindowId(browser()); // Get tabs in the 'current' window called from non-focused browser. - scoped_refptr function = new TabsQueryFunction(); + scoped_refptr function = new QueryTabsFunction(); function->set_extension(utils::CreateEmptyExtension().get()); scoped_ptr result(utils::ToList( utils::RunFunctionAndReturnSingleResult(function.get(), @@ -425,7 +424,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, QueryCurrentWindowTabs) { } // Get tabs NOT in the 'current' window called from non-focused browser. - function = new TabsQueryFunction(); + function = new QueryTabsFunction(); function->set_extension(utils::CreateEmptyExtension().get()); result.reset(utils::ToList( utils::RunFunctionAndReturnSingleResult(function.get(), @@ -451,8 +450,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, DontCreateTabInClosingPopupWindow) { int window_id = ExtensionTabUtil::GetWindowId(popup_browser); chrome::CloseWindow(popup_browser); - scoped_refptr create_tab_function( - new TabsCreateFunction()); + scoped_refptr create_tab_function(new CreateTabFunction()); create_tab_function->set_extension(utils::CreateEmptyExtension().get()); // Without a callback the function will not generate a result. create_tab_function->set_has_callback(true); @@ -474,7 +472,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, InvalidUpdateWindowState) { static const char kArgsMinimizedWithFocus[] = "[%u, {\"state\": \"minimized\", \"focused\": true}]"; - scoped_refptr function = new WindowsUpdateFunction(); + scoped_refptr function = new UpdateWindowFunction(); scoped_refptr extension(utils::CreateEmptyExtension()); function->set_extension(extension.get()); EXPECT_TRUE(MatchPattern( @@ -486,7 +484,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, InvalidUpdateWindowState) { static const char kArgsMaximizedWithoutFocus[] = "[%u, {\"state\": \"maximized\", \"focused\": false}]"; - function = new WindowsUpdateFunction(); + function = new UpdateWindowFunction(); function->set_extension(extension.get()); EXPECT_TRUE(MatchPattern( utils::RunFunctionAndReturnError( @@ -497,7 +495,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, InvalidUpdateWindowState) { static const char kArgsMinimizedWithBounds[] = "[%u, {\"state\": \"minimized\", \"width\": 500}]"; - function = new WindowsUpdateFunction(); + function = new UpdateWindowFunction(); function->set_extension(extension.get()); EXPECT_TRUE(MatchPattern( utils::RunFunctionAndReturnError( @@ -508,7 +506,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, InvalidUpdateWindowState) { static const char kArgsMaximizedWithBounds[] = "[%u, {\"state\": \"maximized\", \"width\": 500}]"; - function = new WindowsUpdateFunction(); + function = new UpdateWindowFunction(); function->set_extension(extension.get()); EXPECT_TRUE(MatchPattern( utils::RunFunctionAndReturnError( @@ -531,8 +529,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, DuplicateTab) { TabStripModel* tab_strip; ExtensionTabUtil::GetTabStripModel(web_contents, &tab_strip, &tab_index); - scoped_refptr duplicate_tab_function( - new TabsDuplicateFunction()); + scoped_refptr duplicate_tab_function( + new DuplicateTabFunction()); scoped_ptr test_extension_value( utils::ParseDictionary( "{\"name\": \"Test\", \"version\": \"1.0\", \"permissions\": [\"tabs\"]}" @@ -575,8 +573,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, DuplicateTabNoPermission) { TabStripModel* tab_strip; ExtensionTabUtil::GetTabStripModel(web_contents, &tab_strip, &tab_index); - scoped_refptr duplicate_tab_function( - new TabsDuplicateFunction()); + scoped_refptr duplicate_tab_function( + new DuplicateTabFunction()); scoped_refptr empty_extension( utils::CreateEmptyExtension()); duplicate_tab_function->set_extension(empty_extension.get()); 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 713fff5..841a7fc 100644 --- a/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc +++ b/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc @@ -19,12 +19,12 @@ #include "chrome/browser/view_type_utils.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/extensions/api/web_navigation.h" +#include "content/public/browser/resource_request_details.h" #include "content/public/browser/navigation_details.h" #include "content/public/browser/notification_service.h" #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/resource_request_details.h" #include "content/public/browser/web_contents.h" #include "content/public/common/url_constants.h" #include "net/base/net_errors.h" @@ -664,7 +664,7 @@ bool WebNavigationTabObserver::IsReferenceFragmentNavigation( url.ReplaceComponents(replacements); } -bool WebNavigationGetFrameFunction::RunImpl() { +bool GetFrameFunction::RunImpl() { scoped_ptr params(GetFrame::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); int tab_id = params->details.tab_id; @@ -720,7 +720,7 @@ bool WebNavigationGetFrameFunction::RunImpl() { return true; } -bool WebNavigationGetAllFramesFunction::RunImpl() { +bool GetAllFramesFunction::RunImpl() { scoped_ptr params(GetAllFrames::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); int tab_id = params->details.tab_id; 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 8270307..58e4308 100644 --- a/chrome/browser/extensions/api/web_navigation/web_navigation_api.h +++ b/chrome/browser/extensions/api/web_navigation/web_navigation_api.h @@ -201,15 +201,15 @@ class WebNavigationEventRouter : public TabStripModelObserver, }; // API function that returns the state of a given frame. -class WebNavigationGetFrameFunction : public SyncExtensionFunction { - virtual ~WebNavigationGetFrameFunction() {} +class GetFrameFunction : public SyncExtensionFunction { + virtual ~GetFrameFunction() {} virtual bool RunImpl() OVERRIDE; DECLARE_EXTENSION_FUNCTION_NAME("webNavigation.getFrame") }; // API function that returns the states of all frames in a given tab. -class WebNavigationGetAllFramesFunction : public SyncExtensionFunction { - virtual ~WebNavigationGetAllFramesFunction() {} +class GetAllFramesFunction : public SyncExtensionFunction { + virtual ~GetAllFramesFunction() {} virtual bool RunImpl() OVERRIDE; DECLARE_EXTENSION_FUNCTION_NAME("webNavigation.getAllFrames") }; diff --git a/chrome/browser/extensions/api/web_request/web_request_api.cc b/chrome/browser/extensions/api/web_request/web_request_api.cc index b53ef97..f907d43 100644 --- a/chrome/browser/extensions/api/web_request/web_request_api.cc +++ b/chrome/browser/extensions/api/web_request/web_request_api.cc @@ -1706,7 +1706,7 @@ void ExtensionWebRequestEventRouter::ClearSignaled(uint64 request_id, iter->second &= ~event_type; } -// Special QuotaLimitHeuristic for WebRequestHandlerBehaviorChangedFunction. +// Special QuotaLimitHeuristic for WebRequestHandlerBehaviorChanged. // // Each call of webRequest.handlerBehaviorChanged() clears the in-memory cache // of WebKit at the time of the next page load (top level navigation event). @@ -1955,7 +1955,7 @@ bool WebRequestEventHandled::RunImpl() { return true; } -void WebRequestHandlerBehaviorChangedFunction::GetQuotaLimitHeuristics( +void WebRequestHandlerBehaviorChanged::GetQuotaLimitHeuristics( QuotaLimitHeuristics* heuristics) const { QuotaLimitHeuristic::Config config = { // See web_request.json for current value. @@ -1969,7 +1969,7 @@ void WebRequestHandlerBehaviorChangedFunction::GetQuotaLimitHeuristics( heuristics->push_back(heuristic); } -void WebRequestHandlerBehaviorChangedFunction::OnQuotaExceeded( +void WebRequestHandlerBehaviorChanged::OnQuotaExceeded( const std::string& violation_error) { // Post warning message. ExtensionWarningSet warnings; @@ -1985,7 +1985,7 @@ void WebRequestHandlerBehaviorChangedFunction::OnQuotaExceeded( Run(); } -bool WebRequestHandlerBehaviorChangedFunction::RunImpl() { +bool WebRequestHandlerBehaviorChanged::RunImpl() { helpers::ClearCacheOnNavigation(); return true; } diff --git a/chrome/browser/extensions/api/web_request/web_request_api.h b/chrome/browser/extensions/api/web_request/web_request_api.h index c7e567a..d0fb338 100644 --- a/chrome/browser/extensions/api/web_request/web_request_api.h +++ b/chrome/browser/extensions/api/web_request/web_request_api.h @@ -433,13 +433,12 @@ class WebRequestEventHandled : public SyncIOThreadExtensionFunction { virtual bool RunImpl() OVERRIDE; }; -class WebRequestHandlerBehaviorChangedFunction - : public SyncIOThreadExtensionFunction { +class WebRequestHandlerBehaviorChanged : public SyncIOThreadExtensionFunction { public: DECLARE_EXTENSION_FUNCTION_NAME("webRequest.handlerBehaviorChanged"); protected: - virtual ~WebRequestHandlerBehaviorChangedFunction() {} + virtual ~WebRequestHandlerBehaviorChanged() {} // ExtensionFunction: virtual void GetQuotaLimitHeuristics( diff --git a/chrome/browser/extensions/browser_event_router.h b/chrome/browser/extensions/browser_event_router.h index 0f40c36..0ef7a0c 100644 --- a/chrome/browser/extensions/browser_event_router.h +++ b/chrome/browser/extensions/browser_event_router.h @@ -10,7 +10,7 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" -#include "chrome/browser/extensions/api/tabs/tabs_api.h" +#include "chrome/browser/extensions/api/tabs/tabs.h" #include "chrome/browser/extensions/event_router.h" #include "chrome/browser/extensions/extension_toolbar_model.h" #include "chrome/browser/ui/browser_list_observer.h" diff --git a/chrome/browser/extensions/content_script_apitest.cc b/chrome/browser/extensions/content_script_apitest.cc index d1a71f3..b675403 100644 --- a/chrome/browser/extensions/content_script_apitest.cc +++ b/chrome/browser/extensions/content_script_apitest.cc @@ -163,8 +163,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContentScriptExtensionAPIs) { } IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContentScriptPermissionsApi) { - PermissionsRequestFunction::SetIgnoreUserGestureForTests(true); - PermissionsRequestFunction::SetAutoConfirmForTests(true); + RequestPermissionsFunction::SetIgnoreUserGestureForTests(true); + RequestPermissionsFunction::SetAutoConfirmForTests(true); host_resolver()->AddRule("*.com", "127.0.0.1"); ASSERT_TRUE(StartTestServer()); ASSERT_TRUE(RunExtensionTest("content_scripts/permissions")) << message_; diff --git a/chrome/browser/extensions/data_deleter.cc b/chrome/browser/extensions/data_deleter.cc index a5c645a..a164df0 100644 --- a/chrome/browser/extensions/data_deleter.cc +++ b/chrome/browser/extensions/data_deleter.cc @@ -4,8 +4,8 @@ #include "chrome/browser/extensions/data_deleter.h" -#include "chrome/browser/extensions/api/storage/settings_frontend.h" #include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/settings/settings_frontend.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/extensions/extension.h" #include "content/public/browser/browser_context.h" diff --git a/chrome/browser/extensions/extension_function_registry.cc b/chrome/browser/extensions/extension_function_registry.cc index 7657dc9..5b63968 100644 --- a/chrome/browser/extensions/extension_function_registry.cc +++ b/chrome/browser/extensions/extension_function_registry.cc @@ -7,23 +7,44 @@ #include "chrome/browser/accessibility/accessibility_extension_api.h" #include "chrome/browser/extensions/api/app/app_api.h" #include "chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.h" +#include "chrome/browser/extensions/api/bookmarks/bookmark_api.h" #include "chrome/browser/extensions/api/browsing_data/browsing_data_api.h" +#include "chrome/browser/extensions/api/cloud_print_private/cloud_print_private_api.h" #include "chrome/browser/extensions/api/commands/commands.h" +#include "chrome/browser/extensions/api/content_settings/content_settings_api.h" +#include "chrome/browser/extensions/api/context_menu/context_menu_api.h" +#include "chrome/browser/extensions/api/cookies/cookies_api.h" +#include "chrome/browser/extensions/api/debugger/debugger_api.h" +#include "chrome/browser/extensions/api/declarative/declarative_api.h" #include "chrome/browser/extensions/api/extension_action/extension_browser_actions_api.h" #include "chrome/browser/extensions/api/extension_action/extension_page_actions_api.h" #include "chrome/browser/extensions/api/extension_action/extension_script_badge_api.h" +#include "chrome/browser/extensions/api/font_settings/font_settings_api.h" +#include "chrome/browser/extensions/api/history/history_api.h" +#include "chrome/browser/extensions/api/identity/identity_api.h" +#include "chrome/browser/extensions/api/i18n/i18n_api.h" #include "chrome/browser/extensions/api/idle/idle_api.h" #include "chrome/browser/extensions/api/managed_mode/managed_mode_api.h" +#include "chrome/browser/extensions/api/management/management_api.h" #include "chrome/browser/extensions/api/metrics/metrics.h" #include "chrome/browser/extensions/api/module/module.h" #include "chrome/browser/extensions/api/omnibox/omnibox_api.h" +#include "chrome/browser/extensions/api/page_capture/page_capture_api.h" +#include "chrome/browser/extensions/api/permissions/permissions_api.h" #include "chrome/browser/extensions/api/preference/preference_api.h" +#include "chrome/browser/extensions/api/record/record_api.h" #include "chrome/browser/extensions/api/runtime/runtime_api.h" +#include "chrome/browser/extensions/api/serial/serial_api.h" +#include "chrome/browser/extensions/api/socket/socket_api.h" +#include "chrome/browser/extensions/api/tabs/execute_code_in_tab_function.h" +#include "chrome/browser/extensions/api/tabs/tabs.h" #include "chrome/browser/extensions/api/test/test_api.h" #include "chrome/browser/extensions/api/top_sites/top_sites_api.h" +#include "chrome/browser/extensions/api/web_navigation/web_navigation_api.h" #include "chrome/browser/extensions/api/web_request/web_request_api.h" #include "chrome/browser/extensions/api/web_socket_proxy_private/web_socket_proxy_private_api.h" #include "chrome/browser/extensions/api/webstore_private/webstore_private_api.h" +#include "chrome/browser/extensions/settings/settings_api.h" #include "chrome/browser/extensions/system/system_api.h" #include "chrome/browser/infobars/infobar_extension_api.h" #include "chrome/browser/rlz/rlz_extension_api.h" @@ -32,17 +53,18 @@ #if defined(TOOLKIT_VIEWS) #include "chrome/browser/extensions/api/input/input.h" -#endif // defined(TOOLKIT_VIEWS) +#endif #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/extensions/echo_private_api.h" +#include "chrome/browser/chromeos/extensions/file_browser_handler_api.h" #include "chrome/browser/chromeos/extensions/info_private_api.h" #include "chrome/browser/chromeos/extensions/input_method_api.h" #include "chrome/browser/chromeos/extensions/media_player_api.h" #include "chrome/browser/chromeos/extensions/power/power_api.h" #include "chrome/browser/chromeos/extensions/wallpaper_private_api.h" #include "chrome/browser/extensions/api/terminal/terminal_private_api.h" -#endif // defined(OS_CHROMEOS) +#endif // static ExtensionFunctionRegistry* ExtensionFunctionRegistry::GetInstance() { @@ -61,6 +83,33 @@ void ExtensionFunctionRegistry::ResetFunctions() { // Register all functions here. + // Windows + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + + // Tabs + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + // Page Actions. RegisterFunction(); RegisterFunction(); @@ -105,6 +154,21 @@ void ExtensionFunctionRegistry::ResetFunctions() { RegisterFunction(); RegisterFunction(); + // Bookmarks. + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + // Infobars. RegisterFunction(); @@ -121,10 +185,22 @@ void ExtensionFunctionRegistry::ResetFunctions() { RegisterFunction(); RegisterFunction(); + // History + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + // Idle RegisterFunction(); RegisterFunction(); + // I18N. + RegisterFunction(); + // Metrics. RegisterFunction(); RegisterFunction(); @@ -144,6 +220,13 @@ void ExtensionFunctionRegistry::ResetFunctions() { RegisterFunction(); #endif + // Cookies. + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + // Test. RegisterFunction(); RegisterFunction(); @@ -153,6 +236,10 @@ void ExtensionFunctionRegistry::ResetFunctions() { RegisterFunction(); RegisterFunction(); + // Record. + RegisterFunction(); + RegisterFunction(); + // Accessibility. RegisterFunction(); RegisterFunction(); @@ -161,6 +248,12 @@ void ExtensionFunctionRegistry::ResetFunctions() { // Commands. RegisterFunction(); + // Context Menus. + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + // Omnibox. RegisterFunction(); RegisterFunction(); @@ -189,6 +282,15 @@ void ExtensionFunctionRegistry::ResetFunctions() { RegisterFunction(); RegisterFunction(); + // Management. + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + // Extension module. RegisterFunction(); RegisterFunction(); @@ -203,9 +305,14 @@ void ExtensionFunctionRegistry::ResetFunctions() { RegisterFunction(); RegisterFunction(); + // WebNavigation. + RegisterFunction(); + RegisterFunction(); + // WebRequest. RegisterFunction(); RegisterFunction(); + RegisterFunction(); // Preferences. RegisterFunction(); @@ -217,6 +324,9 @@ void ExtensionFunctionRegistry::ResetFunctions() { // Device Customization. RegisterFunction(); + // FileBrowserHandlerInternal. + RegisterFunction(); + // Mediaplayer RegisterFunction(); RegisterFunction(); @@ -252,17 +362,83 @@ void ExtensionFunctionRegistry::ResetFunctions() { extensions::WebSocketProxyPrivateGetPassportForTCPFunction>(); RegisterFunction(); + // Debugger + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + + // Settings + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + + // Content settings. + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + + // Font settings. + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + + // CloudPrint settings. + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + // Experimental App API. RegisterFunction(); RegisterFunction(); + // Permissions + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + + // PageCapture + RegisterFunction(); + // TopSites RegisterFunction(); + // Serial + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + + // Sockets + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + // System RegisterFunction(); RegisterFunction(); + // Net + RegisterFunction(); + RegisterFunction(); + RegisterFunction(); + // Runtime RegisterFunction(); RegisterFunction(); diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc index 726959e..4917d5f 100644 --- a/chrome/browser/extensions/extension_service.cc +++ b/chrome/browser/extensions/extension_service.cc @@ -36,7 +36,6 @@ #include "chrome/browser/extensions/api/profile_keyed_api_factory.h" #include "chrome/browser/extensions/api/push_messaging/push_messaging_api.h" #include "chrome/browser/extensions/api/runtime/runtime_api.h" -#include "chrome/browser/extensions/api/storage/settings_frontend.h" #include "chrome/browser/extensions/app_notification_manager.h" #include "chrome/browser/extensions/app_sync_data.h" #include "chrome/browser/extensions/browser_event_router.h" @@ -63,6 +62,7 @@ #include "chrome/browser/extensions/pending_extension_manager.h" #include "chrome/browser/extensions/permissions_updater.h" #include "chrome/browser/extensions/platform_app_launcher.h" +#include "chrome/browser/extensions/settings/settings_frontend.h" #include "chrome/browser/extensions/shell_window_registry.h" #include "chrome/browser/extensions/unpacked_installer.h" #include "chrome/browser/extensions/updater/extension_updater.h" diff --git a/chrome/browser/extensions/platform_app_browsertest.cc b/chrome/browser/extensions/platform_app_browsertest.cc index 718d2bd..d2715bd 100644 --- a/chrome/browser/extensions/platform_app_browsertest.cc +++ b/chrome/browser/extensions/platform_app_browsertest.cc @@ -10,8 +10,8 @@ #include "chrome/browser/automation/automation_util.h" #include "chrome/browser/devtools/devtools_window.h" #include "chrome/browser/extensions/api/permissions/permissions_api.h" -#include "chrome/browser/extensions/app_restore_service.h" #include "chrome/browser/extensions/app_restore_service_factory.h" +#include "chrome/browser/extensions/app_restore_service.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/extensions/extension_prefs.h" #include "chrome/browser/extensions/extension_service.h" @@ -761,7 +761,7 @@ IN_PROC_BROWSER_TEST_F(PlatformAppDevToolsBrowserTest, ReOpenedWithURL) { #endif IN_PROC_BROWSER_TEST_F(PlatformAppBrowserTest, MAYBE_ConstrainedWindowRequest) { - PermissionsRequestFunction::SetIgnoreUserGestureForTests(true); + RequestPermissionsFunction::SetIgnoreUserGestureForTests(true); const Extension* extension = LoadAndLaunchPlatformApp("optional_permission_request"); ASSERT_TRUE(extension) << "Failed to load extension."; diff --git a/chrome/browser/extensions/platform_app_browsertest_util.cc b/chrome/browser/extensions/platform_app_browsertest_util.cc index f1b78a4..4611c0a 100644 --- a/chrome/browser/extensions/platform_app_browsertest_util.cc +++ b/chrome/browser/extensions/platform_app_browsertest_util.cc @@ -6,7 +6,7 @@ #include "base/command_line.h" #include "base/stringprintf.h" -#include "chrome/browser/extensions/api/tabs/tabs_api.h" +#include "chrome/browser/extensions/api/tabs/tabs.h" #include "chrome/browser/extensions/extension_function_test_utils.h" #include "chrome/browser/extensions/shell_window_registry.h" #include "chrome/browser/ui/browser.h" @@ -93,7 +93,7 @@ ShellWindow* PlatformAppBrowserTest::GetFirstShellWindow() { size_t PlatformAppBrowserTest::RunGetWindowsFunctionForExtension( const Extension* extension) { - scoped_refptr function = new WindowsGetAllFunction(); + scoped_refptr function = new GetAllWindowsFunction(); function->set_extension(extension); scoped_ptr result(utils::ToList( utils::RunFunctionAndReturnSingleResult(function.get(), @@ -105,7 +105,7 @@ size_t PlatformAppBrowserTest::RunGetWindowsFunctionForExtension( bool PlatformAppBrowserTest::RunGetWindowFunctionForExtension( int window_id, const Extension* extension) { - scoped_refptr function = new WindowsGetFunction(); + scoped_refptr function = new GetWindowFunction(); function->set_extension(extension); utils::RunFunction( function.get(), diff --git a/chrome/browser/extensions/settings/leveldb_settings_storage_factory.cc b/chrome/browser/extensions/settings/leveldb_settings_storage_factory.cc new file mode 100644 index 0000000..d057529 --- /dev/null +++ b/chrome/browser/extensions/settings/leveldb_settings_storage_factory.cc @@ -0,0 +1,18 @@ +// 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/settings/leveldb_settings_storage_factory.h" + +#include "base/logging.h" +#include "chrome/browser/value_store/leveldb_value_store.h" + +namespace extensions { + +ValueStore* LeveldbSettingsStorageFactory::Create( + const FilePath& base_path, + const std::string& extension_id) { + return new LeveldbValueStore(base_path.AppendASCII(extension_id)); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/settings/leveldb_settings_storage_factory.h b/chrome/browser/extensions/settings/leveldb_settings_storage_factory.h new file mode 100644 index 0000000..8a88f0d --- /dev/null +++ b/chrome/browser/extensions/settings/leveldb_settings_storage_factory.h @@ -0,0 +1,25 @@ +// 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_SETTINGS_LEVELDB_SETTINGS_STORAGE_FACTORY_H_ +#define CHROME_BROWSER_EXTENSIONS_SETTINGS_LEVELDB_SETTINGS_STORAGE_FACTORY_H_ + +#include "chrome/browser/extensions/settings/settings_storage_factory.h" + +namespace extensions { + +// Factory for creating LeveldbValueStore instances. +class LeveldbSettingsStorageFactory : public SettingsStorageFactory { + public: + virtual ValueStore* Create(const FilePath& base_path, + const std::string& extension_id) OVERRIDE; + + private: + // SettingsStorageFactory is refcounted. + virtual ~LeveldbSettingsStorageFactory() {} +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_SETTINGS_LEVELDB_SETTINGS_STORAGE_FACTORY_H_ diff --git a/chrome/browser/extensions/settings/managed_value_store_cache.cc b/chrome/browser/extensions/settings/managed_value_store_cache.cc new file mode 100644 index 0000000..62eb094 --- /dev/null +++ b/chrome/browser/extensions/settings/managed_value_store_cache.cc @@ -0,0 +1,250 @@ +// 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/settings/managed_value_store_cache.h" + +#include + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/callback.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/message_loop_proxy.h" +#include "chrome/browser/extensions/event_names.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/settings/policy_value_store.h" +#include "chrome/browser/extensions/settings/settings_storage_factory.h" +#include "chrome/browser/value_store/value_store_change.h" +#include "chrome/common/extensions/extension.h" +#include "content/public/browser/browser_thread.h" + +using content::BrowserThread; + +namespace extensions { + +ManagedValueStoreCache::ManagedValueStoreCache( + policy::PolicyService* policy_service, + EventRouter* event_router, + const scoped_refptr& factory, + const scoped_refptr& observers, + const FilePath& profile_path) + : ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), + weak_this_on_ui_(weak_factory_.GetWeakPtr()), + policy_service_(policy_service), + event_router_(event_router), + storage_factory_(factory), + observers_(observers), + base_path_(profile_path.AppendASCII( + ExtensionService::kManagedSettingsDirectoryName)) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + // |event_router| can be NULL on unit_tests. + if (event_router_) + event_router_->RegisterObserver(this, event_names::kOnSettingsChanged); + policy_service_->AddObserver(policy::POLICY_DOMAIN_EXTENSIONS, this); +} + +ManagedValueStoreCache::~ManagedValueStoreCache() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + DCHECK(!event_router_); + // Delete the PolicyValueStores on FILE. + store_map_.clear(); +} + +void ManagedValueStoreCache::ShutdownOnUI() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + policy_service_->RemoveObserver(policy::POLICY_DOMAIN_EXTENSIONS, this); + policy_service_ = NULL; + if (event_router_) + event_router_->UnregisterObserver(this); + event_router_ = NULL; + weak_factory_.InvalidateWeakPtrs(); +} + +void ManagedValueStoreCache::RunWithValueStoreForExtension( + const StorageCallback& callback, + scoped_refptr extension) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + PolicyValueStore* store = GetStoreFor(extension->id()); + if (store) { + callback.Run(store); + } else { + // First time that an extension calls storage.managed.get(). Create the + // store and load it with the current policy, and don't send event + // notifications. + CreateStoreFor( + extension->id(), + false, + base::Bind(&ManagedValueStoreCache::RunWithValueStoreForExtension, + base::Unretained(this), + callback, + extension)); + } +} + +void ManagedValueStoreCache::DeleteStorageSoon( + const std::string& extension_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + PolicyValueStore* store = GetStoreFor(extension_id); + if (!store) { + // It's possible that the store exists, but hasn't been loaded yet + // (because the extension is unloaded, for example). Open the database to + // clear it if it exists. + // TODO(joaodasilva): move this check to a ValueStore method. + if (file_util::DirectoryExists(base_path_.AppendASCII(extension_id))) { + CreateStoreFor( + extension_id, + false, + base::Bind(&ManagedValueStoreCache::DeleteStorageSoon, + base::Unretained(this), + extension_id)); + } + } else { + store->DeleteStorage(); + store_map_.erase(extension_id); + } +} + +void ManagedValueStoreCache::OnPolicyUpdated(policy::PolicyDomain domain, + const std::string& component_id, + const policy::PolicyMap& previous, + const policy::PolicyMap& current) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + base::Bind(&ManagedValueStoreCache::UpdatePolicyOnFILE, + base::Unretained(this), + std::string(component_id), + base::Passed(current.DeepCopy()))); +} + +void ManagedValueStoreCache::UpdatePolicyOnFILE( + const std::string& extension_id, + scoped_ptr current_policy) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + PolicyValueStore* store = GetStoreFor(extension_id); + if (!store) { + // The extension hasn't executed any storage.managed.* calls, and isn't + // listening for onChanged() either. Ignore this notification in that case. + return; + } + // Update the policy on the backing store, and fire notifications if it + // changed. + store->SetCurrentPolicy(*current_policy, true); +} + +void ManagedValueStoreCache::OnListenerAdded( + const EventListenerInfo& details) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_EQ(std::string(event_names::kOnSettingsChanged), details.event_name); + // This is invoked on several occasions: + // + // 1. when an extension first registers to observe storage.onChanged; in this + // case the backend doesn't have any previous data persisted, and it won't + // trigger a notification. + // + // 2. when the browser starts up and all existing extensions re-register for + // the onChanged event. In this case, if the current policy differs from + // the persisted version then a notification will be sent. + // + // 3. a policy update just occurred and sent a notification, and an extension + // with EventPages that is observing onChanged just woke up and registed + // again. In this case the policy update already persisted the current + // policy version, and |store| already exists. + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + base::Bind(&ManagedValueStoreCache::CreateForExtensionOnFILE, + base::Unretained(this), + details.extension_id)); +} + +void ManagedValueStoreCache::CreateForExtensionOnFILE( + const std::string& extension_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + PolicyValueStore* store = GetStoreFor(extension_id); + if (!store) + CreateStoreFor(extension_id, true, base::Closure()); +} + +PolicyValueStore* ManagedValueStoreCache::GetStoreFor( + const std::string& extension_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + PolicyValueStoreMap::iterator it = store_map_.find(extension_id); + if (it == store_map_.end()) + return NULL; + return it->second.get(); +} + +void ManagedValueStoreCache::CreateStoreFor( + const std::string& extension_id, + bool notify_if_changed, + const base::Closure& continuation) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + DCHECK(!GetStoreFor(extension_id)); + // Creating or loading an existing database requires an immediate update + // with the current policy for the corresponding extension, which must be + // retrieved on UI. + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&ManagedValueStoreCache::GetInitialPolicy, + weak_this_on_ui_, + extension_id, + notify_if_changed, + continuation)); +} + +void ManagedValueStoreCache::GetInitialPolicy( + const std::string& extension_id, + bool notify_if_changed, + const base::Closure& continuation) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + const policy::PolicyMap& policy = policy_service_->GetPolicies( + policy::POLICY_DOMAIN_EXTENSIONS, extension_id); + // Now post back to FILE to create the database. + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + base::Bind(&ManagedValueStoreCache::CreateStoreWithInitialPolicy, + base::Unretained(this), + extension_id, + notify_if_changed, + base::Passed(policy.DeepCopy()), + continuation)); +} + +void ManagedValueStoreCache::CreateStoreWithInitialPolicy( + const std::string& extension_id, + bool notify_if_changed, + scoped_ptr initial_policy, + const base::Closure& continuation) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + // If a 2nd call to CreateStoreFor() is issued before the 1st gets to execute + // its UI task, then the 2nd will enter this function but the store has + // already been created. Check for that. + PolicyValueStore* store = GetStoreFor(extension_id); + + if (!store) { + // Create it now. + + // If the database doesn't exist yet then this is the initial install, + // and no notifications should be issued in that case. + // TODO(joaodasilva): move this check to a ValueStore method. + if (!file_util::DirectoryExists(base_path_.AppendASCII(extension_id))) + notify_if_changed = false; + + store = new PolicyValueStore( + extension_id, + observers_, + make_scoped_ptr(storage_factory_->Create(base_path_, extension_id))); + store_map_[extension_id] = make_linked_ptr(store); + } + + // Send the latest policy to the store. + store->SetCurrentPolicy(*initial_policy, notify_if_changed); + + // And finally resume from where this process started. + if (!continuation.is_null()) + continuation.Run(); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/settings/managed_value_store_cache.h b/chrome/browser/extensions/settings/managed_value_store_cache.h new file mode 100644 index 0000000..9a1ea7b --- /dev/null +++ b/chrome/browser/extensions/settings/managed_value_store_cache.h @@ -0,0 +1,154 @@ +// 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_SETTINGS_MANAGED_VALUE_STORE_CACHE_H_ +#define CHROME_BROWSER_EXTENSIONS_SETTINGS_MANAGED_VALUE_STORE_CACHE_H_ + +#include +#include + +#include "base/basictypes.h" +#include "base/callback_forward.h" +#include "base/compiler_specific.h" +#include "base/file_path.h" +#include "base/memory/linked_ptr.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "chrome/browser/extensions/event_router.h" +#include "chrome/browser/extensions/settings/settings_observer.h" +#include "chrome/browser/extensions/settings/value_store_cache.h" +#include "chrome/browser/policy/policy_service.h" + +namespace policy { +class PolicyMap; +} + +namespace extensions { + +class PolicyValueStore; +class SettingsStorageFactory; + +// A ValueStoreCache that manages a PolicyValueStore for each extension that +// uses the storage.managed namespace. This class observes policy changes and +// which extensions listen for storage.onChanged(), and sends the appropriate +// updates to the corresponding PolicyValueStore on the FILE thread. +class ManagedValueStoreCache : public ValueStoreCache, + public policy::PolicyService::Observer, + public EventRouter::Observer { + public: + // |policy_service| is used to retrieve policy for extensions, and to observe + // policy updates. + // ||event_router| is used to observe which extensions listen for onChanged. + // |factory| is used to create databases for the PolicyValueStores. + // |observers| is the list of SettingsObservers to notify when a ValueStore + // changes. + // |profile_path| is the path for the profile. The databases are created in + // a directory under this path. + ManagedValueStoreCache(policy::PolicyService* policy_service, + EventRouter* event_router, + const scoped_refptr& factory, + const scoped_refptr& observers, + const FilePath& profile_path); + virtual ~ManagedValueStoreCache(); + + private: + // Maps an extension ID to its PolicyValueStoreMap. + typedef std::map > + PolicyValueStoreMap; + + // ValueStoreCache implementation: + virtual void ShutdownOnUI() OVERRIDE; + virtual void RunWithValueStoreForExtension( + const StorageCallback& callback, + scoped_refptr extension) OVERRIDE; + virtual void DeleteStorageSoon(const std::string& extension_id) OVERRIDE; + + // PolicyService::Observer implementation: + virtual void OnPolicyUpdated(policy::PolicyDomain domain, + const std::string& component_id, + const policy::PolicyMap& previous, + const policy::PolicyMap& current) OVERRIDE; + + // Posted by OnPolicyUpdated() to update a PolicyValueStore on the FILE + // thread. + void UpdatePolicyOnFILE(const std::string& extension_id, + scoped_ptr current_policy); + + // EventRouter::Observer implementation: + virtual void OnListenerAdded(const EventListenerInfo& details) OVERRIDE; + + // Posted by OnListenerAdded() to load or create a PolicyValueStore for the + // given |extension_id|. + void CreateForExtensionOnFILE(const std::string& extension_id); + + // Returns an existing PolicyValueStore for |extension_id|, or NULL. + PolicyValueStore* GetStoreFor(const std::string& extension_id); + + // Creates a new PolicyValueStore for |extension_id|. This may open an + // existing database, or create a new one. This also sends the current policy + // for |extension_id| to the database. When |notify_if_changed| is true, + // a notification is sent with the changes between the current policy and the + // previously stored policy, if there are any. + // + // Since this is used on FILE but must retrieve the current policy, this + // method first posts GetInitialPolicy() to UI and then resumes in + // CreateStoreWithInitialPolicy(). If |continuation| is not null then it + // will be invoked after the store is created. + // + // CreateStoreFor() can be safely invoked from any method on the FILE thread. + // It posts to UI used |weak_this_on_ui_|, so that the task is dropped if + // ShutdownOnUI() has been invoked. Otherwise, GetInitialPolicy() executes + // on UI and can safely post CreateStoreWithInitialPolicy to FILE. + // CreateStoreWithInitialPolicy then guarantees that a store for + // |extension_id| exists or is created, and then executes the |continuation|; + // so when the |continuation| executes, a store for |extension_id| is + // guaranteed to exist. + void CreateStoreFor(const std::string& extension_id, + bool notify_if_changed, + const base::Closure& continuation); + + // Helper for CreateStoreFor, invoked on UI. + void GetInitialPolicy(const std::string& extension_id, + bool notify_if_changed, + const base::Closure& continuation); + + // Helper for CreateStoreFor, invoked on FILE. + void CreateStoreWithInitialPolicy(const std::string& extension_id, + bool notify_if_changed, + scoped_ptr policy, + const base::Closure& continuation); + + // Used to create a WeakPtr valid on the UI thread, so that FILE tasks can + // post back to UI. + base::WeakPtrFactory weak_factory_; + + // A WeakPtr to |this| that is valid on UI. This is used by tasks on the FILE + // thread to post back to UI. + base::WeakPtr weak_this_on_ui_; + + // The PolicyService that is observed for policy updates. Lives on UI. + policy::PolicyService* policy_service_; + + // The EventRouter is created before the SettingsFrontend (which owns the + // instance of this class), and the SettingsFrontend is also destroyed before + // the EventRouter is. |event_router_| is thus valid for the lifetime of this + // object, until ShutdownOnUI() is invoked. Lives on UI. + EventRouter* event_router_; + + // These live on the FILE thread. + scoped_refptr storage_factory_; + scoped_refptr observers_; + FilePath base_path_; + + // All the PolicyValueStores live on the FILE thread, and |store_map_| can be + // accessed only on the FILE thread as well. + PolicyValueStoreMap store_map_; + + DISALLOW_COPY_AND_ASSIGN(ManagedValueStoreCache); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_SETTINGS_MANAGED_VALUE_STORE_CACHE_H_ diff --git a/chrome/browser/extensions/settings/policy_value_store.cc b/chrome/browser/extensions/settings/policy_value_store.cc new file mode 100644 index 0000000..88e2de8 --- /dev/null +++ b/chrome/browser/extensions/settings/policy_value_store.cc @@ -0,0 +1,163 @@ +// 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/settings/policy_value_store.h" + +#include "base/logging.h" +#include "base/values.h" +#include "chrome/browser/extensions/settings/settings_namespace.h" +#include "chrome/browser/policy/policy_map.h" +#include "chrome/browser/policy/policy_types.h" +#include "chrome/browser/value_store/value_store_change.h" +#include "content/public/browser/browser_thread.h" + +using content::BrowserThread; + +namespace extensions { + +namespace { + +const char kReadOnlyStoreErrorMessage[] = "This is a read-only store."; + +ValueStore::WriteResult WriteResultError() { + return ValueStore::MakeWriteResult(kReadOnlyStoreErrorMessage); +} + +} // namespace + +PolicyValueStore::PolicyValueStore( + const std::string& extension_id, + const scoped_refptr& observers, + scoped_ptr delegate) + : extension_id_(extension_id), + observers_(observers), + delegate_(delegate.Pass()) {} + +PolicyValueStore::~PolicyValueStore() {} + +void PolicyValueStore::SetCurrentPolicy(const policy::PolicyMap& policy, + bool notify_if_changed) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + // Convert |policy| to a dictionary value. Only include mandatory policies + // for now. + base::DictionaryValue current_policy; + for (policy::PolicyMap::const_iterator it = policy.begin(); + it != policy.end(); ++it) { + if (it->second.level == policy::POLICY_LEVEL_MANDATORY) { + current_policy.SetWithoutPathExpansion( + it->first, it->second.value->DeepCopy()); + } + } + + // Get the previous policies stored in the database. + // TODO(joaodasilva): it'd be better to have a less expensive way of + // determining which keys are currently stored, or of determining which keys + // must be removed. + base::DictionaryValue previous_policy; + ValueStore::ReadResult read_result = delegate_->Get(); + if (read_result->HasError()) { + LOG(WARNING) << "Failed to read managed settings for extension " + << extension_id_ << ": " << read_result->error(); + // Leave |previous_policy| empty, so that events are generated for every + // policy in |current_policy|. + } else { + read_result->settings()->Swap(&previous_policy); + } + + // Now get two lists of changes: changes after setting the current policies, + // and changes after removing old policies that aren't in |current_policy| + // anymore. + std::vector removed_keys; + for (base::DictionaryValue::Iterator it(previous_policy); + it.HasNext(); it.Advance()) { + if (!current_policy.HasKey(it.key())) + removed_keys.push_back(it.key()); + } + + ValueStoreChangeList changes; + + WriteResult result = delegate_->Remove(removed_keys); + if (!result->HasError()) { + changes.insert( + changes.end(), result->changes().begin(), result->changes().end()); + } + + // IGNORE_QUOTA because these settings aren't writable by the extension, and + // are configured by the domain administrator. + ValueStore::WriteOptions options = ValueStore::IGNORE_QUOTA; + result = delegate_->Set(options, current_policy); + if (!result->HasError()) { + changes.insert( + changes.end(), result->changes().begin(), result->changes().end()); + } + + if (!changes.empty() && notify_if_changed) { + observers_->Notify( + &SettingsObserver::OnSettingsChanged, + extension_id_, + settings_namespace::MANAGED, + ValueStoreChange::ToJson(changes)); + } +} + +void PolicyValueStore::DeleteStorage() { + // This is called from our owner, indicating that storage for this extension + // should be removed. + delegate_->Clear(); +} + +size_t PolicyValueStore::GetBytesInUse(const std::string& key) { + // LeveldbValueStore doesn't implement this; and the underlying database + // isn't acccessible to the extension in any case; from the extension's + // perspective this is a read-only store. + return 0; +} + +size_t PolicyValueStore::GetBytesInUse(const std::vector& keys) { + // See note above. + return 0; +} + +size_t PolicyValueStore::GetBytesInUse() { + // See note above. + return 0; +} + +ValueStore::ReadResult PolicyValueStore::Get(const std::string& key) { + return delegate_->Get(key); +} + +ValueStore::ReadResult PolicyValueStore::Get( + const std::vector& keys) { + return delegate_->Get(keys); +} + +ValueStore::ReadResult PolicyValueStore::Get() { + return delegate_->Get(); +} + +ValueStore::WriteResult PolicyValueStore::Set( + WriteOptions options, const std::string& key, const base::Value& value) { + return WriteResultError(); +} + +ValueStore::WriteResult PolicyValueStore::Set( + WriteOptions options, const base::DictionaryValue& settings) { + return WriteResultError(); +} + +ValueStore::WriteResult PolicyValueStore::Remove(const std::string& key) { + return WriteResultError(); +} + +ValueStore::WriteResult PolicyValueStore::Remove( + const std::vector& keys) { + return WriteResultError(); +} + +ValueStore::WriteResult PolicyValueStore::Clear() { + return WriteResultError(); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/settings/policy_value_store.h b/chrome/browser/extensions/settings/policy_value_store.h new file mode 100644 index 0000000..adf5129 --- /dev/null +++ b/chrome/browser/extensions/settings/policy_value_store.h @@ -0,0 +1,75 @@ +// 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_SETTINGS_POLICY_VALUE_STORE_H_ +#define CHROME_BROWSER_EXTENSIONS_SETTINGS_POLICY_VALUE_STORE_H_ + +#include +#include + +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/extensions/settings/settings_observer.h" +#include "chrome/browser/value_store/value_store.h" + +namespace policy { +class PolicyMap; +} + +namespace extensions { + +// A ValueStore that is backed by another, persistent ValueStore, and stores +// the policies for a specific extension there. This ValueStore is used to +// run the function of the storage.managed namespace; it's read-only for the +// extension. The ManagedValueStoreCache sends updated policy to this store +// and manages its lifetime. +class PolicyValueStore : public ValueStore { + public: + PolicyValueStore(const std::string& extension_id, + const scoped_refptr& observers, + scoped_ptr delegate); + virtual ~PolicyValueStore(); + + // Stores |policy| in the persistent database represented by the |delegate_|. + // If |notify_if_changed| and |policy| differs from the previously persisted + // version, then a notification is sent to the |observers_| with a list of the + // changes detected. + void SetCurrentPolicy(const policy::PolicyMap& policy, + bool notify_if_changed); + + // Clears all the stored data and deletes the database. + void DeleteStorage(); + + // ValueStore implementation: + virtual size_t GetBytesInUse(const std::string& key) OVERRIDE; + virtual size_t GetBytesInUse(const std::vector& keys) OVERRIDE; + virtual size_t GetBytesInUse() OVERRIDE; + virtual ReadResult Get(const std::string& key) OVERRIDE; + virtual ReadResult Get(const std::vector& keys) OVERRIDE; + virtual ReadResult Get() OVERRIDE; + virtual WriteResult Set( + WriteOptions options, + const std::string& key, + const base::Value& value) OVERRIDE; + virtual WriteResult Set( + WriteOptions options, const base::DictionaryValue& values) OVERRIDE; + virtual WriteResult Remove(const std::string& key) OVERRIDE; + virtual WriteResult Remove(const std::vector& keys) OVERRIDE; + virtual WriteResult Clear() OVERRIDE; + + // For unit tests. + ValueStore* delegate() { return delegate_.get(); } + + private: + std::string extension_id_; + scoped_refptr observers_; + scoped_ptr delegate_; + + DISALLOW_COPY_AND_ASSIGN(PolicyValueStore); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_SETTINGS_POLICY_VALUE_STORE_H_ diff --git a/chrome/browser/extensions/settings/policy_value_store_unittest.cc b/chrome/browser/extensions/settings/policy_value_store_unittest.cc new file mode 100644 index 0000000..0d7fa45 --- /dev/null +++ b/chrome/browser/extensions/settings/policy_value_store_unittest.cc @@ -0,0 +1,217 @@ +// 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/settings/policy_value_store.h" + +#include "base/file_path.h" +#include "base/files/scoped_temp_dir.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "chrome/browser/extensions/settings/settings_observer.h" +#include "chrome/browser/policy/policy_map.h" +#include "chrome/browser/value_store/leveldb_value_store.h" +#include "chrome/browser/value_store/value_store_unittest.h" +#include "content/public/test/test_browser_thread.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::Mock; + +namespace extensions { + +namespace { + +const char kTestExtensionId[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + +class MockSettingsObserver : public SettingsObserver { + public: + MOCK_METHOD3(OnSettingsChanged, void( + const std::string& extension_id, + settings_namespace::Namespace settings_namespace, + const std::string& changes_json)); +}; + +// Extends PolicyValueStore by overriding the mutating methods, so that the +// Get() base implementation can be tested with the ValueStoreTest parameterized +// tests. +class MutablePolicyValueStore : public PolicyValueStore { + public: + explicit MutablePolicyValueStore(const FilePath& path) + : PolicyValueStore(kTestExtensionId, + make_scoped_refptr(new SettingsObserverList()), + scoped_ptr(new LeveldbValueStore(path))) {} + virtual ~MutablePolicyValueStore() {} + + virtual WriteResult Set( + WriteOptions options, + const std::string& key, + const base::Value& value) OVERRIDE { + return delegate()->Set(options, key, value); + } + + virtual WriteResult Set( + WriteOptions options, const base::DictionaryValue& values) OVERRIDE { + return delegate()->Set(options, values); + } + + virtual WriteResult Remove(const std::string& key) OVERRIDE { + return delegate()->Remove(key); + } + + virtual WriteResult Remove(const std::vector& keys) OVERRIDE { + return delegate()->Remove(keys); + } + + virtual WriteResult Clear() OVERRIDE { + return delegate()->Clear(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(MutablePolicyValueStore); +}; + +ValueStore* Param(const FilePath& file_path) { + return new MutablePolicyValueStore(file_path); +} + +} // namespace + +INSTANTIATE_TEST_CASE_P( + PolicyValueStoreTest, + ValueStoreTest, + testing::Values(&Param)); + +class PolicyValueStoreTest : public testing::Test { + public: + PolicyValueStoreTest() + : file_thread_(content::BrowserThread::FILE, &loop_) {} + virtual ~PolicyValueStoreTest() {} + + virtual void SetUp() OVERRIDE { + ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir()); + observers_ = new SettingsObserverList(); + observers_->AddObserver(&observer_); + store_.reset(new PolicyValueStore( + kTestExtensionId, + observers_, + scoped_ptr( + new LeveldbValueStore(scoped_temp_dir_.path())))); + } + + virtual void TearDown() OVERRIDE { + observers_->RemoveObserver(&observer_); + store_.reset(); + } + + protected: + base::ScopedTempDir scoped_temp_dir_; + MessageLoop loop_; + content::TestBrowserThread file_thread_; + scoped_ptr store_; + MockSettingsObserver observer_; + scoped_refptr observers_; +}; + +TEST_F(PolicyValueStoreTest, DontProvideRecommendedPolicies) { + policy::PolicyMap policies; + base::FundamentalValue expected(123); + policies.Set("must", policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, expected.DeepCopy()); + policies.Set("may", policy::POLICY_LEVEL_RECOMMENDED, + policy::POLICY_SCOPE_USER, base::Value::CreateIntegerValue(456)); + store_->SetCurrentPolicy(policies, false); + ValueStore::ReadResult result = store_->Get(); + ASSERT_FALSE(result->HasError()); + EXPECT_EQ(1u, result->settings()->size()); + base::Value* value = NULL; + EXPECT_FALSE(result->settings()->Get("may", &value)); + EXPECT_TRUE(result->settings()->Get("must", &value)); + EXPECT_TRUE(base::Value::Equals(&expected, value)); +} + +TEST_F(PolicyValueStoreTest, ReadOnly) { + ValueStore::WriteOptions options = ValueStore::DEFAULTS; + + base::StringValue string_value("value"); + EXPECT_TRUE(store_->Set(options, "key", string_value)->HasError()); + + base::DictionaryValue dict; + dict.SetString("key", "value"); + EXPECT_TRUE(store_->Set(options, dict)->HasError()); + + EXPECT_TRUE(store_->Remove("key")->HasError()); + std::vector keys; + keys.push_back("key"); + EXPECT_TRUE(store_->Remove(keys)->HasError()); + EXPECT_TRUE(store_->Clear()->HasError()); +} + +TEST_F(PolicyValueStoreTest, NotifyOnChanges) { + policy::PolicyMap policies; + policies.Set("aaa", policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER, + base::Value::CreateStringValue("111")); + EXPECT_CALL(observer_, OnSettingsChanged(_, _, _)).Times(0); + // No notification when setting the initial policy. + store_->SetCurrentPolicy(policies, false); + loop_.RunUntilIdle(); + Mock::VerifyAndClearExpectations(&observer_); + + // And no notifications on changes when not asked for. + policies.Set("aaa", policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER, + base::Value::CreateStringValue("222")); + policies.Set("bbb", policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER, + base::Value::CreateStringValue("223")); + EXPECT_CALL(observer_, OnSettingsChanged(_, _, _)).Times(0); + store_->SetCurrentPolicy(policies, false); + loop_.RunUntilIdle(); + Mock::VerifyAndClearExpectations(&observer_); + + // Notify when new policies are added. + ValueStoreChangeList changes; + base::StringValue value("333"); + changes.push_back(ValueStoreChange("ccc", NULL, value.DeepCopy())); + policies.Set("ccc", policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER, + value.DeepCopy()); + EXPECT_CALL(observer_, OnSettingsChanged(kTestExtensionId, + settings_namespace::MANAGED, + ValueStoreChange::ToJson(changes))); + store_->SetCurrentPolicy(policies, true); + loop_.RunUntilIdle(); + Mock::VerifyAndClearExpectations(&observer_); + + // Notify when policies change. + changes.clear(); + base::StringValue new_value("444"); + changes.push_back( + ValueStoreChange("ccc", value.DeepCopy(), new_value.DeepCopy())); + policies.Set("ccc", policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER, + new_value.DeepCopy()); + EXPECT_CALL(observer_, OnSettingsChanged(kTestExtensionId, + settings_namespace::MANAGED, + ValueStoreChange::ToJson(changes))); + store_->SetCurrentPolicy(policies, true); + loop_.RunUntilIdle(); + Mock::VerifyAndClearExpectations(&observer_); + + // Notify when policies are removed. + changes.clear(); + changes.push_back(ValueStoreChange("ccc", new_value.DeepCopy(), NULL)); + policies.Erase("ccc"); + EXPECT_CALL(observer_, OnSettingsChanged(kTestExtensionId, + settings_namespace::MANAGED, + ValueStoreChange::ToJson(changes))); + store_->SetCurrentPolicy(policies, true); + loop_.RunUntilIdle(); + Mock::VerifyAndClearExpectations(&observer_); + + // Don't notify when there aren't changes. + EXPECT_CALL(observer_, OnSettingsChanged(_, _, _)).Times(0); + store_->SetCurrentPolicy(policies, true); + loop_.RunUntilIdle(); + Mock::VerifyAndClearExpectations(&observer_); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/settings/setting_sync_data.cc b/chrome/browser/extensions/settings/setting_sync_data.cc new file mode 100644 index 0000000..4baf04c --- /dev/null +++ b/chrome/browser/extensions/settings/setting_sync_data.cc @@ -0,0 +1,102 @@ +// 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/settings/setting_sync_data.h" + +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "sync/api/sync_data.h" +#include "sync/protocol/app_setting_specifics.pb.h" +#include "sync/protocol/extension_setting_specifics.pb.h" +#include "sync/protocol/sync.pb.h" + +namespace extensions { + +SettingSyncData::SettingSyncData( + const syncer::SyncChange& sync_change) { + Init(sync_change.change_type(), sync_change.sync_data()); +} + +SettingSyncData::SettingSyncData( + const syncer::SyncData& sync_data) { + Init(syncer::SyncChange::ACTION_INVALID, sync_data); +} + +void SettingSyncData::Init( + syncer::SyncChange::SyncChangeType change_type, + const syncer::SyncData& sync_data) { + DCHECK(!internal_.get()); + sync_pb::EntitySpecifics specifics = sync_data.GetSpecifics(); + // The data must only be either extension or app specfics. + DCHECK_NE(specifics.has_extension_setting(), + specifics.has_app_setting()); + if (specifics.has_extension_setting()) { + InitFromExtensionSettingSpecifics( + change_type, + specifics.extension_setting()); + } else if (specifics.has_app_setting()) { + InitFromExtensionSettingSpecifics( + change_type, + specifics.app_setting().extension_setting()); + } +} + +void SettingSyncData::InitFromExtensionSettingSpecifics( + syncer::SyncChange::SyncChangeType change_type, + const sync_pb::ExtensionSettingSpecifics& specifics) { + DCHECK(!internal_.get()); + scoped_ptr value( + base::JSONReader::Read(specifics.value())); + if (!value.get()) { + LOG(WARNING) << "Specifics for " << specifics.extension_id() << "/" << + specifics.key() << " had bad JSON for value: " << specifics.value(); + value.reset(new DictionaryValue()); + } + internal_ = new Internal( + change_type, + specifics.extension_id(), + specifics.key(), + value.Pass()); +} + +SettingSyncData::SettingSyncData( + syncer::SyncChange::SyncChangeType change_type, + const std::string& extension_id, + const std::string& key, + scoped_ptr value) + : internal_(new Internal(change_type, extension_id, key, value.Pass())) {} + +SettingSyncData::~SettingSyncData() {} + +syncer::SyncChange::SyncChangeType SettingSyncData::change_type() const { + return internal_->change_type_; +} + +const std::string& SettingSyncData::extension_id() const { + return internal_->extension_id_; +} + +const std::string& SettingSyncData::key() const { + return internal_->key_; +} + +const Value& SettingSyncData::value() const { + return *internal_->value_; +} + +SettingSyncData::Internal::Internal( + syncer::SyncChange::SyncChangeType change_type, + const std::string& extension_id, + const std::string& key, + scoped_ptr value) + : change_type_(change_type), + extension_id_(extension_id), + key_(key), + value_(value.Pass()) { + DCHECK(value_.get()); +} + +SettingSyncData::Internal::~Internal() {} + +} // namespace extensions diff --git a/chrome/browser/extensions/settings/setting_sync_data.h b/chrome/browser/extensions/settings/setting_sync_data.h new file mode 100644 index 0000000..fc9d1d4 --- /dev/null +++ b/chrome/browser/extensions/settings/setting_sync_data.h @@ -0,0 +1,91 @@ +// 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_SETTINGS_SETTING_SYNC_DATA_H_ +#define CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTING_SYNC_DATA_H_ + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/values.h" +#include "sync/api/sync_change.h" + +namespace syncer { +class SyncData; +} + +namespace sync_pb { +class ExtensionSettingSpecifics; +} + +namespace extensions { + +// Container for data interpreted from sync data/changes for an extension or +// app setting. Safe and efficient to copy. +class SettingSyncData { + public: + // Creates from a sync change. + explicit SettingSyncData(const syncer::SyncChange& sync_change); + + // Creates from sync data. |change_type| will be ACTION_INVALID. + explicit SettingSyncData(const syncer::SyncData& sync_data); + + // Creates explicitly. + SettingSyncData( + syncer::SyncChange::SyncChangeType change_type, + const std::string& extension_id, + const std::string& key, + scoped_ptr value); + + ~SettingSyncData(); + + // Returns the type of the sync change; may be ACTION_INVALID. + syncer::SyncChange::SyncChangeType change_type() const; + + // Returns the extension id the setting is for. + const std::string& extension_id() const; + + // Returns the settings key. + const std::string& key() const; + + // Returns the value of the setting. + const Value& value() const; + + private: + // Ref-counted container for the data. + // TODO(kalman): Use browser_sync::Immutable. + class Internal : public base::RefCountedThreadSafe { + public: + Internal( + syncer::SyncChange::SyncChangeType change_type, + const std::string& extension_id, + const std::string& key, + scoped_ptr value); + + syncer::SyncChange::SyncChangeType change_type_; + std::string extension_id_; + std::string key_; + scoped_ptr value_; + + private: + friend class base::RefCountedThreadSafe; + ~Internal(); + }; + + // Initializes internal_ from sync data for an extension or app setting. + void Init(syncer::SyncChange::SyncChangeType change_type, + const syncer::SyncData& sync_data); + + // Initializes internal_ from extension specifics. + void InitFromExtensionSettingSpecifics( + syncer::SyncChange::SyncChangeType change_type, + const sync_pb::ExtensionSettingSpecifics& specifics); + + scoped_refptr internal_; +}; + +typedef std::vector SettingSyncDataList; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTING_SYNC_DATA_H_ diff --git a/chrome/browser/extensions/settings/settings_api.cc b/chrome/browser/extensions/settings/settings_api.cc new file mode 100644 index 0000000..35f4eaf --- /dev/null +++ b/chrome/browser/extensions/settings/settings_api.cc @@ -0,0 +1,286 @@ +// 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/settings/settings_api.h" + +#include +#include + +#include "base/bind.h" +#include "base/stringprintf.h" +#include "base/values.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extensions_quota_service.h" +#include "chrome/browser/extensions/settings/settings_frontend.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/extensions/api/storage.h" +#include "content/public/browser/browser_thread.h" + +namespace extensions { + +using content::BrowserThread; + +namespace { +const char kUnsupportedArgumentType[] = "Unsupported argument type"; +const char kInvalidNamespaceErrorMessage[] = + "\"%s\" is not available in this instance of Chrome"; +const char kManagedNamespaceDisabledErrorMessage[] = + "\"managed\" is disabled. Use \"--%s\" to enable it."; +const char kStorageErrorMessage[] = "Storage error"; +} // namespace + +// SettingsFunction + +SettingsFunction::SettingsFunction() + : settings_namespace_(settings_namespace::INVALID) {} + +SettingsFunction::~SettingsFunction() {} + +bool SettingsFunction::ShouldSkipQuotaLimiting() const { + // Only apply quota if this is for sync storage. + std::string settings_namespace_string; + if (!args_->GetString(0, &settings_namespace_string)) { + // This is an error but it will be caught in RunImpl(), there is no + // mechanism to signify an error from this function. + return false; + } + return settings_namespace_string != "sync"; +} + +bool SettingsFunction::RunImpl() { + std::string settings_namespace_string; + EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &settings_namespace_string)); + args_->Remove(0, NULL); + settings_namespace_ = + settings_namespace::FromString(settings_namespace_string); + EXTENSION_FUNCTION_VALIDATE( + settings_namespace_ != settings_namespace::INVALID); + + SettingsFrontend* frontend = + profile()->GetExtensionService()->settings_frontend(); + if (!frontend->IsStorageEnabled(settings_namespace_)) { + error_ = base::StringPrintf(kInvalidNamespaceErrorMessage, + settings_namespace_string.c_str()); + return false; + } + + observers_ = frontend->GetObservers(); + frontend->RunWithStorage( + extension_id(), + settings_namespace_, + base::Bind(&SettingsFunction::AsyncRunWithStorage, this)); + return true; +} + +void SettingsFunction::AsyncRunWithStorage(ValueStore* storage) { + bool success = RunWithStorage(storage); + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&SettingsFunction::SendResponse, this, success)); +} + +bool SettingsFunction::UseReadResult(ValueStore::ReadResult result) { + if (result->HasError()) { + error_ = result->error(); + return false; + } + + SetResult(result->settings().release()); + return true; +} + +bool SettingsFunction::UseWriteResult(ValueStore::WriteResult result) { + if (result->HasError()) { + error_ = result->error(); + return false; + } + + if (!result->changes().empty()) { + observers_->Notify( + &SettingsObserver::OnSettingsChanged, + extension_id(), + settings_namespace_, + ValueStoreChange::ToJson(result->changes())); + } + + return true; +} + +// Concrete settings functions + +namespace { + +// Adds all StringValues from a ListValue to a vector of strings. +void AddAllStringValues(const ListValue& from, std::vector* to) { + DCHECK(to->empty()); + std::string as_string; + for (ListValue::const_iterator it = from.begin(); it != from.end(); ++it) { + if ((*it)->GetAsString(&as_string)) { + to->push_back(as_string); + } + } +} + +// Gets the keys of a DictionaryValue. +std::vector GetKeys(const DictionaryValue& dict) { + std::vector keys; + for (DictionaryValue::key_iterator it = dict.begin_keys(); + it != dict.end_keys(); ++it) { + keys.push_back(*it); + } + return keys; +} + +// Creates quota heuristics for settings modification. +void GetModificationQuotaLimitHeuristics(QuotaLimitHeuristics* heuristics) { + QuotaLimitHeuristic::Config longLimitConfig = { + // See storage.json for current value. + api::storage::sync::MAX_WRITE_OPERATIONS_PER_HOUR, + base::TimeDelta::FromHours(1) + }; + heuristics->push_back( + new ExtensionsQuotaService::TimedLimit( + longLimitConfig, + new QuotaLimitHeuristic::SingletonBucketMapper(), + "MAX_WRITE_OPERATIONS_PER_HOUR")); + + // A max of 10 operations per minute, sustained over 10 minutes. + QuotaLimitHeuristic::Config shortLimitConfig = { + // See storage.json for current value. + api::storage::sync::MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE, + base::TimeDelta::FromMinutes(1) + }; + heuristics->push_back( + new ExtensionsQuotaService::SustainedLimit( + base::TimeDelta::FromMinutes(10), + shortLimitConfig, + new QuotaLimitHeuristic::SingletonBucketMapper(), + "MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE")); +}; + +} // namespace + +bool GetSettingsFunction::RunWithStorage(ValueStore* storage) { + Value* input = NULL; + EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &input)); + + switch (input->GetType()) { + case Value::TYPE_NULL: + return UseReadResult(storage->Get()); + + case Value::TYPE_STRING: { + std::string as_string; + input->GetAsString(&as_string); + return UseReadResult(storage->Get(as_string)); + } + + case Value::TYPE_LIST: { + std::vector as_string_list; + AddAllStringValues(*static_cast(input), &as_string_list); + return UseReadResult(storage->Get(as_string_list)); + } + + case Value::TYPE_DICTIONARY: { + DictionaryValue* as_dict = static_cast(input); + ValueStore::ReadResult result = storage->Get(GetKeys(*as_dict)); + if (result->HasError()) { + return UseReadResult(result.Pass()); + } + + DictionaryValue* with_default_values = as_dict->DeepCopy(); + with_default_values->MergeDictionary(result->settings().get()); + return UseReadResult( + ValueStore::MakeReadResult(with_default_values)); + } + + default: + return UseReadResult( + ValueStore::MakeReadResult(kUnsupportedArgumentType)); + } +} + +bool GetBytesInUseSettingsFunction::RunWithStorage(ValueStore* storage) { + Value* input = NULL; + EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &input)); + + size_t bytes_in_use = 0; + + switch (input->GetType()) { + case Value::TYPE_NULL: + bytes_in_use = storage->GetBytesInUse(); + break; + + case Value::TYPE_STRING: { + std::string as_string; + input->GetAsString(&as_string); + bytes_in_use = storage->GetBytesInUse(as_string); + break; + } + + case Value::TYPE_LIST: { + std::vector as_string_list; + AddAllStringValues(*static_cast(input), &as_string_list); + bytes_in_use = storage->GetBytesInUse(as_string_list); + break; + } + + default: + error_ = kUnsupportedArgumentType; + return false; + } + + SetResult(Value::CreateIntegerValue(bytes_in_use)); + return true; +} + +bool SetSettingsFunction::RunWithStorage(ValueStore* storage) { + DictionaryValue* input = NULL; + EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &input)); + return UseWriteResult(storage->Set(ValueStore::DEFAULTS, *input)); +} + +void SetSettingsFunction::GetQuotaLimitHeuristics( + QuotaLimitHeuristics* heuristics) const { + GetModificationQuotaLimitHeuristics(heuristics); +} + +bool RemoveSettingsFunction::RunWithStorage(ValueStore* storage) { + Value* input = NULL; + EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &input)); + + switch (input->GetType()) { + case Value::TYPE_STRING: { + std::string as_string; + input->GetAsString(&as_string); + return UseWriteResult(storage->Remove(as_string)); + } + + case Value::TYPE_LIST: { + std::vector as_string_list; + AddAllStringValues(*static_cast(input), &as_string_list); + return UseWriteResult(storage->Remove(as_string_list)); + } + + default: + return UseWriteResult( + ValueStore::MakeWriteResult(kUnsupportedArgumentType)); + }; +} + +void RemoveSettingsFunction::GetQuotaLimitHeuristics( + QuotaLimitHeuristics* heuristics) const { + GetModificationQuotaLimitHeuristics(heuristics); +} + +bool ClearSettingsFunction::RunWithStorage(ValueStore* storage) { + return UseWriteResult(storage->Clear()); +} + +void ClearSettingsFunction::GetQuotaLimitHeuristics( + QuotaLimitHeuristics* heuristics) const { + GetModificationQuotaLimitHeuristics(heuristics); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/settings/settings_api.h b/chrome/browser/extensions/settings/settings_api.h new file mode 100644 index 0000000..38a18d5 --- /dev/null +++ b/chrome/browser/extensions/settings/settings_api.h @@ -0,0 +1,129 @@ +// 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_SETTINGS_SETTINGS_API_H_ +#define CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTINGS_API_H_ + +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "chrome/browser/extensions/extension_function.h" +#include "chrome/browser/extensions/settings/settings_namespace.h" +#include "chrome/browser/extensions/settings/settings_observer.h" +#include "chrome/browser/value_store/value_store.h" + +namespace extensions { + +// Superclass of all settings functions. +// +// NOTE: these all have "*SettingsFunction" names left over from when the API +// was called the "Settings API" (now "Storage API"). +// TODO(kalman): Rename these functions, and all files under +// chrome/browser/extensions/settings. +class SettingsFunction : public AsyncExtensionFunction { + protected: + SettingsFunction(); + virtual ~SettingsFunction(); + + // ExtensionFunction: + virtual bool ShouldSkipQuotaLimiting() const OVERRIDE; + virtual bool RunImpl() OVERRIDE; + + // Extension settings function implementations should do their work here. + // The SettingsFrontend makes sure this is posted to the appropriate thread. + // Implementations should fill in args themselves, though (like RunImpl) + // may return false to imply failure. + virtual bool RunWithStorage(ValueStore* storage) = 0; + + // Sets error_ or result_ depending on the value of a storage ReadResult, and + // returns whether the result implies success (i.e. !error). + bool UseReadResult(ValueStore::ReadResult result); + + // Sets error_ depending on the value of a storage WriteResult, sends a + // change notification if needed, and returns whether the result implies + // success (i.e. !error). + bool UseWriteResult(ValueStore::WriteResult result); + + private: + // Called via PostTask from RunImpl. Calls RunWithStorage and then + // SendResponse with its success value. + void AsyncRunWithStorage(ValueStore* storage); + + // The settings namespace the call was for. For example, SYNC if the API + // call was chrome.settings.experimental.sync..., LOCAL if .local, etc. + settings_namespace::Namespace settings_namespace_; + + // Observers, cached so that it's only grabbed from the UI thread. + scoped_refptr observers_; +}; + +class GetSettingsFunction : public SettingsFunction { + public: + DECLARE_EXTENSION_FUNCTION_NAME("storage.get"); + + protected: + virtual ~GetSettingsFunction() {} + + // SettingsFunction: + virtual bool RunWithStorage(ValueStore* storage) OVERRIDE; +}; + +class SetSettingsFunction : public SettingsFunction { + public: + DECLARE_EXTENSION_FUNCTION_NAME("storage.set"); + + protected: + virtual ~SetSettingsFunction() {} + + // SettingsFunction: + virtual bool RunWithStorage(ValueStore* storage) OVERRIDE; + + // ExtensionFunction: + virtual void GetQuotaLimitHeuristics( + QuotaLimitHeuristics* heuristics) const OVERRIDE; +}; + +class RemoveSettingsFunction : public SettingsFunction { + public: + DECLARE_EXTENSION_FUNCTION_NAME("storage.remove"); + + protected: + virtual ~RemoveSettingsFunction() {} + + // SettingsFunction: + virtual bool RunWithStorage(ValueStore* storage) OVERRIDE; + + // ExtensionFunction: + virtual void GetQuotaLimitHeuristics( + QuotaLimitHeuristics* heuristics) const OVERRIDE; +}; + +class ClearSettingsFunction : public SettingsFunction { + public: + DECLARE_EXTENSION_FUNCTION_NAME("storage.clear"); + + protected: + virtual ~ClearSettingsFunction() {} + + // SettingsFunction: + virtual bool RunWithStorage(ValueStore* storage) OVERRIDE; + + // ExtensionFunction: + virtual void GetQuotaLimitHeuristics( + QuotaLimitHeuristics* heuristics) const OVERRIDE; +}; + +class GetBytesInUseSettingsFunction : public SettingsFunction { + public: + DECLARE_EXTENSION_FUNCTION_NAME("storage.getBytesInUse"); + + protected: + virtual ~GetBytesInUseSettingsFunction() {} + + // SettingsFunction: + virtual bool RunWithStorage(ValueStore* storage) OVERRIDE; +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTINGS_API_H_ diff --git a/chrome/browser/extensions/settings/settings_apitest.cc b/chrome/browser/extensions/settings/settings_apitest.cc new file mode 100644 index 0000000..acfa22d --- /dev/null +++ b/chrome/browser/extensions/settings/settings_apitest.cc @@ -0,0 +1,518 @@ +// 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 "base/bind.h" +#include "base/json/json_writer.h" +#include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_test_message_listener.h" +#include "chrome/browser/extensions/settings/settings_frontend.h" +#include "chrome/browser/extensions/settings/settings_namespace.h" +#include "chrome/browser/extensions/settings/settings_sync_util.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/common/extensions/value_builder.h" +#include "chrome/test/base/ui_test_utils.h" +#include "sync/api/sync_change.h" +#include "sync/api/sync_change_processor.h" +#include "sync/api/sync_error_factory.h" +#include "sync/api/sync_error_factory_mock.h" +#include "testing/gmock/include/gmock/gmock.h" + +#if defined(ENABLE_CONFIGURATION_POLICY) +#include "chrome/browser/policy/browser_policy_connector.h" +#include "chrome/browser/policy/mock_configuration_policy_provider.h" +#include "chrome/browser/policy/policy_bundle.h" +#include "chrome/browser/policy/policy_map.h" +#endif + +namespace extensions { + +using settings_namespace::FromString; +using settings_namespace::LOCAL; +using settings_namespace::MANAGED; +using settings_namespace::Namespace; +using settings_namespace::SYNC; +using settings_namespace::ToString; +using testing::Return; + +namespace { + +// TODO(kalman): test both EXTENSION_SETTINGS and APP_SETTINGS. +const syncer::ModelType kModelType = syncer::EXTENSION_SETTINGS; + +// The managed_storage extension has a key defined in its manifest, so that +// its extension ID is well-known and the policy system can push policies for +// the extension. +const char kManagedStorageExtensionId[] = "kjmkgkdkpedkejedfhmfcenooemhbpbo"; + +class NoopSyncChangeProcessor : public syncer::SyncChangeProcessor { + public: + virtual syncer::SyncError ProcessSyncChanges( + const tracked_objects::Location& from_here, + const syncer::SyncChangeList& change_list) OVERRIDE { + return syncer::SyncError(); + } + + virtual ~NoopSyncChangeProcessor() {}; +}; + +class SyncChangeProcessorDelegate : public syncer::SyncChangeProcessor { + public: + explicit SyncChangeProcessorDelegate(syncer::SyncChangeProcessor* recipient) + : recipient_(recipient) { + DCHECK(recipient_); + } + virtual ~SyncChangeProcessorDelegate() {} + + // syncer::SyncChangeProcessor implementation. + virtual syncer::SyncError ProcessSyncChanges( + const tracked_objects::Location& from_here, + const syncer::SyncChangeList& change_list) OVERRIDE { + return recipient_->ProcessSyncChanges(from_here, change_list); + } + + private: + // The recipient of all sync changes. + syncer::SyncChangeProcessor* recipient_; + + DISALLOW_COPY_AND_ASSIGN(SyncChangeProcessorDelegate); +}; + +} // namespace + +class ExtensionSettingsApiTest : public ExtensionApiTest { + protected: + virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { + ExtensionApiTest::SetUpInProcessBrowserTestFixture(); + +#if defined(ENABLE_CONFIGURATION_POLICY) + EXPECT_CALL(policy_provider_, IsInitializationComplete()) + .WillRepeatedly(Return(true)); + policy::BrowserPolicyConnector::SetPolicyProviderForTesting( + &policy_provider_); +#endif + } + + void ReplyWhenSatisfied( + Namespace settings_namespace, + const std::string& normal_action, + const std::string& incognito_action) { + MaybeLoadAndReplyWhenSatisfied( + settings_namespace, normal_action, incognito_action, NULL, false); + } + + const Extension* LoadAndReplyWhenSatisfied( + Namespace settings_namespace, + const std::string& normal_action, + const std::string& incognito_action, + const std::string& extension_dir) { + return MaybeLoadAndReplyWhenSatisfied( + settings_namespace, + normal_action, + incognito_action, + &extension_dir, + false); + } + + void FinalReplyWhenSatisfied( + Namespace settings_namespace, + const std::string& normal_action, + const std::string& incognito_action) { + MaybeLoadAndReplyWhenSatisfied( + settings_namespace, normal_action, incognito_action, NULL, true); + } + + void InitSync(syncer::SyncChangeProcessor* sync_processor) { + MessageLoop::current()->RunUntilIdle(); + InitSyncWithSyncableService( + sync_processor, + browser()->profile()->GetExtensionService()->settings_frontend()-> + GetBackendForSync(kModelType)); + } + + void SendChanges(const syncer::SyncChangeList& change_list) { + MessageLoop::current()->RunUntilIdle(); + SendChangesToSyncableService( + change_list, + browser()->profile()->GetExtensionService()->settings_frontend()-> + GetBackendForSync(kModelType)); + } + +#if defined(ENABLE_CONFIGURATION_POLICY) + void SetPolicies(const base::DictionaryValue& policies) { + scoped_ptr bundle(new policy::PolicyBundle()); + policy::PolicyMap& policy_map = bundle->Get( + policy::POLICY_DOMAIN_EXTENSIONS, kManagedStorageExtensionId); + policy_map.LoadFrom( + &policies, policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER); + policy_provider_.UpdatePolicy(bundle.Pass()); + } +#endif + + private: + const Extension* MaybeLoadAndReplyWhenSatisfied( + Namespace settings_namespace, + const std::string& normal_action, + const std::string& incognito_action, + // May be NULL to imply not loading the extension. + const std::string* extension_dir, + bool is_final_action) { + ExtensionTestMessageListener listener("waiting", true); + ExtensionTestMessageListener listener_incognito("waiting_incognito", true); + + // Only load the extension after the listeners have been set up, to avoid + // initialisation race conditions. + const Extension* extension = NULL; + if (extension_dir) { + extension = LoadExtensionIncognito( + test_data_dir_.AppendASCII("settings").AppendASCII(*extension_dir)); + EXPECT_TRUE(extension); + } + + EXPECT_TRUE(listener.WaitUntilSatisfied()); + EXPECT_TRUE(listener_incognito.WaitUntilSatisfied()); + + listener.Reply( + CreateMessage(settings_namespace, normal_action, is_final_action)); + listener_incognito.Reply( + CreateMessage(settings_namespace, incognito_action, is_final_action)); + return extension; + } + + std::string CreateMessage( + Namespace settings_namespace, + const std::string& action, + bool is_final_action) { + scoped_ptr message(new DictionaryValue()); + message->SetString("namespace", ToString(settings_namespace)); + message->SetString("action", action); + message->SetBoolean("isFinalAction", is_final_action); + std::string message_json; + base::JSONWriter::Write(message.get(), &message_json); + return message_json; + } + + void InitSyncWithSyncableService( + syncer::SyncChangeProcessor* sync_processor, + syncer::SyncableService* settings_service) { + EXPECT_FALSE(settings_service->MergeDataAndStartSyncing( + kModelType, + syncer::SyncDataList(), + scoped_ptr( + new SyncChangeProcessorDelegate(sync_processor)), + scoped_ptr( + new syncer::SyncErrorFactoryMock())).error().IsSet()); + } + + void SendChangesToSyncableService( + const syncer::SyncChangeList& change_list, + syncer::SyncableService* settings_service) { + EXPECT_FALSE( + settings_service->ProcessSyncChanges(FROM_HERE, change_list).IsSet()); + } + + protected: +#if defined(ENABLE_CONFIGURATION_POLICY) + policy::MockConfigurationPolicyProvider policy_provider_; +#endif +}; + +IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, SimpleTest) { + ASSERT_TRUE(RunExtensionTest("settings/simple_test")) << message_; +} + +// Structure of this test taken from IncognitoSplitMode. +// Note that only split-mode incognito is tested, because spanning mode +// incognito looks the same as normal mode when the only API activity comes +// from background pages. +IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, SplitModeIncognito) { + // We need 2 ResultCatchers because we'll be running the same test in both + // regular and incognito mode. + ResultCatcher catcher, catcher_incognito; + catcher.RestrictToProfile(browser()->profile()); + catcher_incognito.RestrictToProfile( + browser()->profile()->GetOffTheRecordProfile()); + + LoadAndReplyWhenSatisfied(SYNC, + "assertEmpty", "assertEmpty", "split_incognito"); + ReplyWhenSatisfied(SYNC, "noop", "setFoo"); + ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo"); + ReplyWhenSatisfied(SYNC, "clear", "noop"); + ReplyWhenSatisfied(SYNC, "assertEmpty", "assertEmpty"); + ReplyWhenSatisfied(SYNC, "setFoo", "noop"); + ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo"); + ReplyWhenSatisfied(SYNC, "noop", "removeFoo"); + FinalReplyWhenSatisfied(SYNC, "assertEmpty", "assertEmpty"); + + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); + EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message(); +} + +IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, + OnChangedNotificationsBetweenBackgroundPages) { + // We need 2 ResultCatchers because we'll be running the same test in both + // regular and incognito mode. + ResultCatcher catcher, catcher_incognito; + catcher.RestrictToProfile(browser()->profile()); + catcher_incognito.RestrictToProfile( + browser()->profile()->GetOffTheRecordProfile()); + + LoadAndReplyWhenSatisfied(SYNC, + "assertNoNotifications", "assertNoNotifications", "split_incognito"); + ReplyWhenSatisfied(SYNC, "noop", "setFoo"); + ReplyWhenSatisfied(SYNC, + "assertAddFooNotification", "assertAddFooNotification"); + ReplyWhenSatisfied(SYNC, "clearNotifications", "clearNotifications"); + ReplyWhenSatisfied(SYNC, "removeFoo", "noop"); + FinalReplyWhenSatisfied(SYNC, + "assertDeleteFooNotification", "assertDeleteFooNotification"); + + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); + EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message(); +} + +IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, + SyncAndLocalAreasAreSeparate) { + // We need 2 ResultCatchers because we'll be running the same test in both + // regular and incognito mode. + ResultCatcher catcher, catcher_incognito; + catcher.RestrictToProfile(browser()->profile()); + catcher_incognito.RestrictToProfile( + browser()->profile()->GetOffTheRecordProfile()); + + LoadAndReplyWhenSatisfied(SYNC, + "assertNoNotifications", "assertNoNotifications", "split_incognito"); + + ReplyWhenSatisfied(SYNC, "noop", "setFoo"); + ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo"); + ReplyWhenSatisfied(SYNC, + "assertAddFooNotification", "assertAddFooNotification"); + ReplyWhenSatisfied(LOCAL, "assertEmpty", "assertEmpty"); + ReplyWhenSatisfied(LOCAL, "assertNoNotifications", "assertNoNotifications"); + + ReplyWhenSatisfied(SYNC, "clearNotifications", "clearNotifications"); + + ReplyWhenSatisfied(LOCAL, "setFoo", "noop"); + ReplyWhenSatisfied(LOCAL, "assertFoo", "assertFoo"); + ReplyWhenSatisfied(LOCAL, + "assertAddFooNotification", "assertAddFooNotification"); + ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo"); + ReplyWhenSatisfied(SYNC, "assertNoNotifications", "assertNoNotifications"); + + ReplyWhenSatisfied(LOCAL, "clearNotifications", "clearNotifications"); + + ReplyWhenSatisfied(LOCAL, "noop", "removeFoo"); + ReplyWhenSatisfied(LOCAL, "assertEmpty", "assertEmpty"); + ReplyWhenSatisfied(LOCAL, + "assertDeleteFooNotification", "assertDeleteFooNotification"); + ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo"); + ReplyWhenSatisfied(SYNC, "assertNoNotifications", "assertNoNotifications"); + + ReplyWhenSatisfied(LOCAL, "clearNotifications", "clearNotifications"); + + ReplyWhenSatisfied(SYNC, "removeFoo", "noop"); + ReplyWhenSatisfied(SYNC, "assertEmpty", "assertEmpty"); + ReplyWhenSatisfied(SYNC, + "assertDeleteFooNotification", "assertDeleteFooNotification"); + ReplyWhenSatisfied(LOCAL, "assertNoNotifications", "assertNoNotifications"); + FinalReplyWhenSatisfied(LOCAL, "assertEmpty", "assertEmpty"); + + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); + EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message(); +} + +// Disabled, see crbug.com/101110 +IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, + DISABLED_OnChangedNotificationsFromSync) { + // We need 2 ResultCatchers because we'll be running the same test in both + // regular and incognito mode. + ResultCatcher catcher, catcher_incognito; + catcher.RestrictToProfile(browser()->profile()); + catcher_incognito.RestrictToProfile( + browser()->profile()->GetOffTheRecordProfile()); + + const Extension* extension = + LoadAndReplyWhenSatisfied(SYNC, + "assertNoNotifications", "assertNoNotifications", "split_incognito"); + const std::string& extension_id = extension->id(); + + NoopSyncChangeProcessor sync_processor; + InitSync(&sync_processor); + + // Set "foo" to "bar" via sync. + syncer::SyncChangeList sync_changes; + StringValue bar("bar"); + sync_changes.push_back(settings_sync_util::CreateAdd( + extension_id, "foo", bar, kModelType)); + SendChanges(sync_changes); + + ReplyWhenSatisfied(SYNC, + "assertAddFooNotification", "assertAddFooNotification"); + ReplyWhenSatisfied(SYNC, "clearNotifications", "clearNotifications"); + + // Remove "foo" via sync. + sync_changes.clear(); + sync_changes.push_back(settings_sync_util::CreateDelete( + extension_id, "foo", kModelType)); + SendChanges(sync_changes); + + FinalReplyWhenSatisfied(SYNC, + "assertDeleteFooNotification", "assertDeleteFooNotification"); + + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); + EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message(); +} + +// Disabled, see crbug.com/101110 +// +// TODO: boring test, already done in the unit tests. What we really should be +// be testing is that the areas don't overlap. +IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, + DISABLED_OnChangedNotificationsFromSyncNotSentToLocal) { + // We need 2 ResultCatchers because we'll be running the same test in both + // regular and incognito mode. + ResultCatcher catcher, catcher_incognito; + catcher.RestrictToProfile(browser()->profile()); + catcher_incognito.RestrictToProfile( + browser()->profile()->GetOffTheRecordProfile()); + + const Extension* extension = + LoadAndReplyWhenSatisfied(LOCAL, + "assertNoNotifications", "assertNoNotifications", "split_incognito"); + const std::string& extension_id = extension->id(); + + NoopSyncChangeProcessor sync_processor; + InitSync(&sync_processor); + + // Set "foo" to "bar" via sync. + syncer::SyncChangeList sync_changes; + StringValue bar("bar"); + sync_changes.push_back(settings_sync_util::CreateAdd( + extension_id, "foo", bar, kModelType)); + SendChanges(sync_changes); + + ReplyWhenSatisfied(LOCAL, "assertNoNotifications", "assertNoNotifications"); + + // Remove "foo" via sync. + sync_changes.clear(); + sync_changes.push_back(settings_sync_util::CreateDelete( + extension_id, "foo", kModelType)); + SendChanges(sync_changes); + + FinalReplyWhenSatisfied(LOCAL, + "assertNoNotifications", "assertNoNotifications"); + + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); + EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message(); +} + +IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, IsStorageEnabled) { + SettingsFrontend* frontend = + browser()->profile()->GetExtensionService()->settings_frontend(); + EXPECT_TRUE(frontend->IsStorageEnabled(LOCAL)); + EXPECT_TRUE(frontend->IsStorageEnabled(SYNC)); + +#if defined(ENABLE_CONFIGURATION_POLICY) + EXPECT_TRUE(frontend->IsStorageEnabled(MANAGED)); +#else + EXPECT_FALSE(frontend->IsStorageEnabled(MANAGED)); +#endif +} + +#if defined(ENABLE_CONFIGURATION_POLICY) + +IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, ManagedStorage) { + // Set policies for the test extension. + scoped_ptr policy = extensions::DictionaryBuilder() + .Set("string-policy", "value") + .Set("int-policy", -123) + .Set("double-policy", 456e7) + .SetBoolean("boolean-policy", true) + .Set("list-policy", extensions::ListBuilder() + .Append("one") + .Append("two") + .Append("three")) + .Set("dict-policy", extensions::DictionaryBuilder() + .Set("list", extensions::ListBuilder() + .Append(extensions::DictionaryBuilder() + .Set("one", 1) + .Set("two", 2)) + .Append(extensions::DictionaryBuilder() + .Set("three", 3)))) + .Build(); + SetPolicies(*policy); + // Now run the extension. + ASSERT_TRUE(RunExtensionTest("settings/managed_storage")) << message_; +} + +IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, PRE_ManagedStorageEvents) { + ResultCatcher catcher; + + // This test starts without any test extensions installed. + EXPECT_FALSE(GetSingleLoadedExtension()); + message_.clear(); + + // Set policies for the test extension. + scoped_ptr policy = extensions::DictionaryBuilder() + .Set("constant-policy", "aaa") + .Set("changes-policy", "bbb") + .Set("deleted-policy", "ccc") + .Build(); + SetPolicies(*policy); + + ExtensionTestMessageListener ready_listener("ready", false); + // Load the extension to install the event listener. + const Extension* extension = LoadExtension( + test_data_dir_.AppendASCII("settings/managed_storage_events")); + ASSERT_TRUE(extension); + // Wait until the extension sends the "ready" message. + ASSERT_TRUE(ready_listener.WaitUntilSatisfied()); + + // Now change the policies and wait until the extension is done. + policy = extensions::DictionaryBuilder() + .Set("constant-policy", "aaa") + .Set("changes-policy", "ddd") + .Set("new-policy", "eee") + .Build(); + SetPolicies(*policy); + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); +} + +// TODO(joaodasilva): This test times out on Vista. http://crbug.com/166261 +#if !defined(OS_WIN) +IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, ManagedStorageEvents) { + // This test runs after PRE_ManagedStorageEvents without having deleted the + // profile, so the extension is still around. While the browser restarted the + // policy went back to the empty default, and so the extension should receive + // the corresponding change events. + + ResultCatcher catcher; + + // Verify that the test extension is still installed. + const Extension* extension = GetSingleLoadedExtension(); + ASSERT_TRUE(extension); + EXPECT_EQ(kManagedStorageExtensionId, extension->id()); + + // Running the test again skips the onInstalled callback, and just triggers + // the onChanged notification. + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); +} +#endif // !defined(OS_WIN) + +#endif // defined(ENABLE_CONFIGURATION_POLICY) + +IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, ManagedStorageDisabled) { + // Disable the 'managed' namespace. This is redundant when + // ENABLE_CONFIGURATION_POLICY is not defined. + SettingsFrontend* frontend = + browser()->profile()->GetExtensionService()->settings_frontend(); + frontend->DisableStorageForTesting(MANAGED); + EXPECT_FALSE(frontend->IsStorageEnabled(MANAGED)); + // Now run the extension. + ASSERT_TRUE(RunExtensionTest("settings/managed_storage_disabled")) + << message_; +} + +} // namespace extensions diff --git a/chrome/browser/extensions/settings/settings_backend.cc b/chrome/browser/extensions/settings/settings_backend.cc new file mode 100644 index 0000000..99ca5e3 --- /dev/null +++ b/chrome/browser/extensions/settings/settings_backend.cc @@ -0,0 +1,285 @@ +// 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/settings/settings_backend.h" + +#include "base/file_util.h" +#include "base/logging.h" +#include "chrome/browser/extensions/settings/settings_sync_processor.h" +#include "chrome/browser/extensions/settings/settings_sync_util.h" +#include "chrome/browser/extensions/settings/syncable_settings_storage.h" +#include "content/public/browser/browser_thread.h" +#include "sync/api/sync_error_factory.h" + +using content::BrowserThread; + +namespace extensions { + +SettingsBackend::SettingsBackend( + const scoped_refptr& storage_factory, + const FilePath& base_path, + const SettingsStorageQuotaEnforcer::Limits& quota, + const scoped_refptr& observers) + : storage_factory_(storage_factory), + base_path_(base_path), + quota_(quota), + observers_(observers), + sync_type_(syncer::UNSPECIFIED) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); +} + +SettingsBackend::~SettingsBackend() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); +} + +ValueStore* SettingsBackend::GetStorage( + const std::string& extension_id) const { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + DictionaryValue empty; + return GetOrCreateStorageWithSyncData(extension_id, empty); +} + +SyncableSettingsStorage* SettingsBackend::GetOrCreateStorageWithSyncData( + const std::string& extension_id, const DictionaryValue& sync_data) const { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + + StorageObjMap::iterator maybe_storage = storage_objs_.find(extension_id); + if (maybe_storage != storage_objs_.end()) { + return maybe_storage->second.get(); + } + + ValueStore* storage = storage_factory_->Create(base_path_, extension_id); + CHECK(storage); + + // It's fine to create the quota enforcer underneath the sync layer, since + // sync will only go ahead if each underlying storage operation succeeds. + storage = new SettingsStorageQuotaEnforcer(quota_, storage); + + linked_ptr syncable_storage( + new SyncableSettingsStorage( + observers_, + extension_id, + storage)); + storage_objs_[extension_id] = syncable_storage; + + if (sync_processor_.get()) { + syncer::SyncError error = + syncable_storage->StartSyncing( + sync_data, + CreateSettingsSyncProcessor(extension_id).Pass()); + if (error.IsSet()) + syncable_storage.get()->StopSyncing(); + } + + return syncable_storage.get(); +} + +void SettingsBackend::DeleteStorage(const std::string& extension_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + + // Clear settings when the extension is uninstalled. Leveldb implementations + // will also delete the database from disk when the object is destroyed as a + // result of being removed from |storage_objs_|. + // + // TODO(kalman): always GetStorage here (rather than only clearing if it + // exists) since the storage area may have been unloaded, but we still want + // to clear the data from disk. + // However, this triggers http://crbug.com/111072. + StorageObjMap::iterator maybe_storage = storage_objs_.find(extension_id); + if (maybe_storage == storage_objs_.end()) + return; + maybe_storage->second->Clear(); + storage_objs_.erase(extension_id); +} + +std::set SettingsBackend::GetKnownExtensionIDs() const { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + std::set result; + + // Storage areas can be in-memory as well as on disk. |storage_objs_| will + // contain all that are in-memory. + for (StorageObjMap::iterator it = storage_objs_.begin(); + it != storage_objs_.end(); ++it) { + result.insert(it->first); + } + + // Leveldb databases are directories inside base_path_. + file_util::FileEnumerator::FindInfo find_info; + file_util::FileEnumerator extension_dirs( + base_path_, false, file_util::FileEnumerator::DIRECTORIES); + while (!extension_dirs.Next().empty()) { + extension_dirs.GetFindInfo(&find_info); + FilePath extension_dir(file_util::FileEnumerator::GetFilename(find_info)); + DCHECK(!extension_dir.IsAbsolute()); + // Extension IDs are created as std::strings so they *should* be ASCII. + std::string maybe_as_ascii(extension_dir.MaybeAsASCII()); + if (!maybe_as_ascii.empty()) { + result.insert(maybe_as_ascii); + } + } + + return result; +} + +static void AddAllSyncData( + const std::string& extension_id, + const DictionaryValue& src, + syncer::ModelType type, + syncer::SyncDataList* dst) { + for (DictionaryValue::Iterator it(src); it.HasNext(); it.Advance()) { + dst->push_back(settings_sync_util::CreateData( + extension_id, it.key(), it.value(), type)); + } +} + +syncer::SyncDataList SettingsBackend::GetAllSyncData( + syncer::ModelType type) const { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + // Ignore the type, it's just for sanity checking; assume that whatever base + // path we're constructed with is correct for the sync type. + DCHECK(type == syncer::EXTENSION_SETTINGS || + type == syncer::APP_SETTINGS); + + // For all extensions, get all their settings. This has the effect + // of bringing in the entire state of extension settings in memory; sad. + syncer::SyncDataList all_sync_data; + std::set known_extension_ids(GetKnownExtensionIDs()); + + for (std::set::const_iterator it = known_extension_ids.begin(); + it != known_extension_ids.end(); ++it) { + ValueStore::ReadResult maybe_settings = GetStorage(*it)->Get(); + if (maybe_settings->HasError()) { + LOG(WARNING) << "Failed to get settings for " << *it << ": " << + maybe_settings->error(); + continue; + } + AddAllSyncData(*it, *maybe_settings->settings().get(), + type, &all_sync_data); + } + + return all_sync_data; +} + +syncer::SyncMergeResult SettingsBackend::MergeDataAndStartSyncing( + syncer::ModelType type, + const syncer::SyncDataList& initial_sync_data, + scoped_ptr sync_processor, + scoped_ptr sync_error_factory) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + DCHECK(type == syncer::EXTENSION_SETTINGS || + type == syncer::APP_SETTINGS); + DCHECK_EQ(sync_type_, syncer::UNSPECIFIED); + DCHECK(!sync_processor_.get()); + DCHECK(sync_processor.get()); + DCHECK(sync_error_factory.get()); + + sync_type_ = type; + sync_processor_ = sync_processor.Pass(); + sync_error_factory_ = sync_error_factory.Pass(); + + // Group the initial sync data by extension id. + std::map > grouped_sync_data; + for (syncer::SyncDataList::const_iterator it = initial_sync_data.begin(); + it != initial_sync_data.end(); ++it) { + SettingSyncData data(*it); + linked_ptr sync_data = + grouped_sync_data[data.extension_id()]; + if (!sync_data.get()) { + sync_data = linked_ptr(new DictionaryValue()); + grouped_sync_data[data.extension_id()] = sync_data; + } + DCHECK(!sync_data->HasKey(data.key())) << + "Duplicate settings for " << data.extension_id() << "/" << data.key(); + sync_data->SetWithoutPathExpansion(data.key(), data.value().DeepCopy()); + } + + // Start syncing all existing storage areas. Any storage areas created in + // the future will start being synced as part of the creation process. + for (StorageObjMap::iterator it = storage_objs_.begin(); + it != storage_objs_.end(); ++it) { + std::map >::iterator + maybe_sync_data = grouped_sync_data.find(it->first); + syncer::SyncError error; + if (maybe_sync_data != grouped_sync_data.end()) { + error = it->second->StartSyncing( + *maybe_sync_data->second, + CreateSettingsSyncProcessor(it->first).Pass()); + grouped_sync_data.erase(it->first); + } else { + DictionaryValue empty; + error = it->second->StartSyncing( + empty, + CreateSettingsSyncProcessor(it->first).Pass()); + } + if (error.IsSet()) + it->second->StopSyncing(); + } + + // Eagerly create and init the rest of the storage areas that have sync data. + // Under normal circumstances (i.e. not first-time sync) this will be all of + // them. + for (std::map >::iterator it = + grouped_sync_data.begin(); it != grouped_sync_data.end(); ++it) { + GetOrCreateStorageWithSyncData(it->first, *it->second); + } + + return syncer::SyncMergeResult(type); +} + +syncer::SyncError SettingsBackend::ProcessSyncChanges( + const tracked_objects::Location& from_here, + const syncer::SyncChangeList& sync_changes) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + DCHECK(sync_processor_.get()); + + // Group changes by extension, to pass all changes in a single method call. + std::map grouped_sync_data; + for (syncer::SyncChangeList::const_iterator it = sync_changes.begin(); + it != sync_changes.end(); ++it) { + SettingSyncData data(*it); + grouped_sync_data[data.extension_id()].push_back(data); + } + + // Create any storage areas that don't exist yet but have sync data. + DictionaryValue empty; + for (std::map::iterator + it = grouped_sync_data.begin(); it != grouped_sync_data.end(); ++it) { + SyncableSettingsStorage* storage = + GetOrCreateStorageWithSyncData(it->first, empty); + syncer::SyncError error = storage->ProcessSyncChanges(it->second); + if (error.IsSet()) + storage->StopSyncing(); + } + + return syncer::SyncError(); +} + +void SettingsBackend::StopSyncing(syncer::ModelType type) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + DCHECK(type == syncer::EXTENSION_SETTINGS || + type == syncer::APP_SETTINGS); + DCHECK(sync_type_ == type || sync_type_ == syncer::UNSPECIFIED); + + for (StorageObjMap::iterator it = storage_objs_.begin(); + it != storage_objs_.end(); ++it) { + // Some storage areas may have already stopped syncing if they had areas + // and syncing was disabled, but StopSyncing is safe to call multiple times. + it->second->StopSyncing(); + } + + sync_type_ = syncer::UNSPECIFIED; + sync_processor_.reset(); + sync_error_factory_.reset(); +} + +scoped_ptr SettingsBackend::CreateSettingsSyncProcessor( + const std::string& extension_id) const { + CHECK(sync_processor_.get()); + return scoped_ptr( + new SettingsSyncProcessor(extension_id, + sync_type_, + sync_processor_.get())); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/settings/settings_backend.h b/chrome/browser/extensions/settings/settings_backend.h new file mode 100644 index 0000000..9560c82 --- /dev/null +++ b/chrome/browser/extensions/settings/settings_backend.h @@ -0,0 +1,116 @@ +// 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_SETTINGS_SETTINGS_BACKEND_H_ +#define CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTINGS_BACKEND_H_ + +#include +#include +#include + +#include "base/compiler_specific.h" +#include "base/file_path.h" +#include "base/memory/linked_ptr.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/extensions/settings/settings_observer.h" +#include "chrome/browser/extensions/settings/settings_storage_factory.h" +#include "chrome/browser/extensions/settings/settings_storage_quota_enforcer.h" +#include "sync/api/syncable_service.h" + +namespace syncer { +class SyncErrorFactory; +} + +namespace extensions { + +class SettingsSyncProcessor; +class SyncableSettingsStorage; + +// Manages ValueStore objects for extensions, including routing +// changes from sync to them. +// Lives entirely on the FILE thread. +class SettingsBackend : public syncer::SyncableService { + public: + // |storage_factory| is use to create leveldb storage areas. + // |base_path| is the base of the extension settings directory, so the + // databases will be at base_path/extension_id. + // |observers| is the list of observers to settings changes. + SettingsBackend( + const scoped_refptr& storage_factory, + const FilePath& base_path, + const SettingsStorageQuotaEnforcer::Limits& quota, + const scoped_refptr& observers); + + virtual ~SettingsBackend(); + + // Gets a weak reference to the storage area for |extension_id|. + // Must be run on the FILE thread. + ValueStore* GetStorage(const std::string& extension_id) const; + + // Deletes all setting data for an extension. Call on the FILE thread. + void DeleteStorage(const std::string& extension_id); + + // syncer::SyncableService implementation. + virtual syncer::SyncDataList GetAllSyncData( + syncer::ModelType type) const OVERRIDE; + virtual syncer::SyncMergeResult MergeDataAndStartSyncing( + syncer::ModelType type, + const syncer::SyncDataList& initial_sync_data, + scoped_ptr sync_processor, + scoped_ptr sync_error_factory) OVERRIDE; + virtual syncer::SyncError ProcessSyncChanges( + const tracked_objects::Location& from_here, + const syncer::SyncChangeList& change_list) OVERRIDE; + virtual void StopSyncing(syncer::ModelType type) OVERRIDE; + + private: + // Gets a weak reference to the storage area for a given extension, + // initializing sync with some initial data if sync enabled. + SyncableSettingsStorage* GetOrCreateStorageWithSyncData( + const std::string& extension_id, + const DictionaryValue& sync_data) const; + + // Gets all extension IDs known to extension settings. This may not be all + // installed extensions. + std::set GetKnownExtensionIDs() const; + + // Creates a new SettingsSyncProcessor for an extension. + scoped_ptr CreateSettingsSyncProcessor( + const std::string& extension_id) const; + + // The Factory to use for creating leveldb storage areas. + const scoped_refptr storage_factory_; + + // The base file path to create any leveldb databases at. + const FilePath base_path_; + + // Quota limits (see SettingsStorageQuotaEnforcer). + const SettingsStorageQuotaEnforcer::Limits quota_; + + // The list of observers to settings changes. + const scoped_refptr observers_; + + // A cache of ValueStore objects that have already been created. + // Ensure that there is only ever one created per extension. + typedef std::map > + StorageObjMap; + mutable StorageObjMap storage_objs_; + + // Current sync model type. Will be UNSPECIFIED if sync hasn't been enabled + // yet, and either SETTINGS or APP_SETTINGS if it has been. + syncer::ModelType sync_type_; + + // Current sync processor, if any. + scoped_ptr sync_processor_; + + // Current sync error handler if any. + scoped_ptr sync_error_factory_; + + DISALLOW_COPY_AND_ASSIGN(SettingsBackend); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTINGS_BACKEND_H_ diff --git a/chrome/browser/extensions/settings/settings_frontend.cc b/chrome/browser/extensions/settings/settings_frontend.cc new file mode 100644 index 0000000..b616edd0 --- /dev/null +++ b/chrome/browser/extensions/settings/settings_frontend.cc @@ -0,0 +1,218 @@ +// 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/settings/settings_frontend.h" + +#include + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/file_path.h" +#include "base/json/json_reader.h" +#include "chrome/browser/extensions/event_names.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/settings/leveldb_settings_storage_factory.h" +#include "chrome/browser/extensions/settings/settings_backend.h" +#include "chrome/browser/extensions/settings/sync_or_local_value_store_cache.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/extensions/api/storage.h" +#include "content/public/browser/browser_thread.h" + +#if defined(ENABLE_CONFIGURATION_POLICY) +#include "chrome/browser/extensions/settings/managed_value_store_cache.h" +#endif + +using content::BrowserThread; + +namespace extensions { + +namespace { + +// Settings change Observer which forwards changes on to the extension +// processes for |profile| and its incognito partner if it exists. +class DefaultObserver : public SettingsObserver { + public: + explicit DefaultObserver(Profile* profile) : profile_(profile) {} + + // SettingsObserver implementation. + virtual void OnSettingsChanged( + const std::string& extension_id, + settings_namespace::Namespace settings_namespace, + const std::string& change_json) OVERRIDE { + // TODO(gdk): This is a temporary hack while the refactoring for + // string-based event payloads is removed. http://crbug.com/136045 + scoped_ptr args(new ListValue()); + args->Append(base::JSONReader::Read(change_json)); + args->Append(Value::CreateStringValue(settings_namespace::ToString( + settings_namespace))); + scoped_ptr event(new Event( + event_names::kOnSettingsChanged, args.Pass())); + ExtensionSystem::Get(profile_)->event_router()-> + DispatchEventToExtension(extension_id, event.Pass()); + } + + private: + Profile* const profile_; +}; + +SettingsStorageQuotaEnforcer::Limits GetLocalLimits() { + SettingsStorageQuotaEnforcer::Limits limits = { + static_cast(api::storage::local::QUOTA_BYTES), + std::numeric_limits::max(), + std::numeric_limits::max() + }; + return limits; +} + +SettingsStorageQuotaEnforcer::Limits GetSyncLimits() { + SettingsStorageQuotaEnforcer::Limits limits = { + static_cast(api::storage::sync::QUOTA_BYTES), + static_cast(api::storage::sync::QUOTA_BYTES_PER_ITEM), + static_cast(api::storage::sync::MAX_ITEMS) + }; + return limits; +} + +} // namespace + +// static +SettingsFrontend* SettingsFrontend::Create(Profile* profile) { + return new SettingsFrontend(new LeveldbSettingsStorageFactory(), profile); +} + +// static +SettingsFrontend* SettingsFrontend::Create( + const scoped_refptr& storage_factory, + Profile* profile) { + return new SettingsFrontend(storage_factory, profile); +} + +SettingsFrontend::SettingsFrontend( + const scoped_refptr& factory, Profile* profile) + : local_quota_limit_(GetLocalLimits()), + sync_quota_limit_(GetSyncLimits()), + profile_(profile), + observers_(new SettingsObserverList()), + profile_observer_(new DefaultObserver(profile)) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(!profile->IsOffTheRecord()); + + observers_->AddObserver(profile_observer_.get()); + + const FilePath& profile_path = profile->GetPath(); + caches_[settings_namespace::LOCAL] = + new SyncOrLocalValueStoreCache( + settings_namespace::LOCAL, + factory, + local_quota_limit_, + observers_, + profile_path); + caches_[settings_namespace::SYNC] = + new SyncOrLocalValueStoreCache( + settings_namespace::SYNC, + factory, + sync_quota_limit_, + observers_, + profile_path); + +#if defined(ENABLE_CONFIGURATION_POLICY) + caches_[settings_namespace::MANAGED] = + new ManagedValueStoreCache( + profile->GetPolicyService(), + ExtensionSystem::Get(profile)->event_router(), + factory, + observers_, + profile_path); +#endif +} + +SettingsFrontend::~SettingsFrontend() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + observers_->RemoveObserver(profile_observer_.get()); + for (CacheMap::iterator it = caches_.begin(); it != caches_.end(); ++it) { + ValueStoreCache* cache = it->second; + cache->ShutdownOnUI(); + BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, cache); + } +} + +syncer::SyncableService* SettingsFrontend::GetBackendForSync( + syncer::ModelType type) const { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + CacheMap::const_iterator it = caches_.find(settings_namespace::SYNC); + DCHECK(it != caches_.end()); + const SyncOrLocalValueStoreCache* sync_cache = + static_cast(it->second); + switch (type) { + case syncer::APP_SETTINGS: + return sync_cache->GetAppBackend(); + case syncer::EXTENSION_SETTINGS: + return sync_cache->GetExtensionBackend(); + default: + NOTREACHED(); + return NULL; + } +} + +bool SettingsFrontend::IsStorageEnabled( + settings_namespace::Namespace settings_namespace) const { + return caches_.find(settings_namespace) != caches_.end(); +} + +void SettingsFrontend::RunWithStorage( + const std::string& extension_id, + settings_namespace::Namespace settings_namespace, + const ValueStoreCache::StorageCallback& callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + ValueStoreCache* cache = caches_[settings_namespace]; + CHECK(cache); + + // The |extension| has already been referenced earlier in the stack, so it + // can't be gone here. + // TODO(kalman): change RunWithStorage() to take a + // scoped_refptr instead. + scoped_refptr extension = + extensions::ExtensionSystem::Get(profile_)->extension_service()-> + GetExtensionById(extension_id, true); + CHECK(extension); + + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + base::Bind(&ValueStoreCache::RunWithValueStoreForExtension, + base::Unretained(cache), callback, extension)); +} + +void SettingsFrontend::DeleteStorageSoon( + const std::string& extension_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + for (CacheMap::iterator it = caches_.begin(); it != caches_.end(); ++it) { + ValueStoreCache* cache = it->second; + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + base::Bind(&ValueStoreCache::DeleteStorageSoon, + base::Unretained(cache), + extension_id)); + } +} + +scoped_refptr SettingsFrontend::GetObservers() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + return observers_; +} + +void SettingsFrontend::DisableStorageForTesting( + settings_namespace::Namespace settings_namespace) { + CacheMap::iterator it = caches_.find(settings_namespace); + if (it != caches_.end()) { + ValueStoreCache* cache = it->second; + cache->ShutdownOnUI(); + BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, cache); + caches_.erase(it); + } +} + +} // namespace extensions diff --git a/chrome/browser/extensions/settings/settings_frontend.h b/chrome/browser/extensions/settings/settings_frontend.h new file mode 100644 index 0000000..1c53d51 --- /dev/null +++ b/chrome/browser/extensions/settings/settings_frontend.h @@ -0,0 +1,93 @@ +// 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_SETTINGS_SETTINGS_FRONTEND_H_ +#define CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTINGS_FRONTEND_H_ + +#include +#include + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/extensions/settings/settings_namespace.h" +#include "chrome/browser/extensions/settings/settings_observer.h" +#include "chrome/browser/extensions/settings/settings_storage_factory.h" +#include "chrome/browser/extensions/settings/settings_storage_quota_enforcer.h" +#include "chrome/browser/extensions/settings/value_store_cache.h" +#include "sync/api/syncable_service.h" + +class Profile; + +namespace extensions { + +// The component of extension settings which runs on the UI thread, as opposed +// to SettingsBackend which lives on the FILE thread. +// All public methods, must be called on the UI thread, with the exception of +// GetBackendForSync(), which must be called on the FILE thread. +class SettingsFrontend { + public: + // Creates with the default factory. + static SettingsFrontend* Create(Profile* profile); + + // Creates with a specific factory |storage_factory| (presumably for tests). + static SettingsFrontend* Create( + const scoped_refptr& storage_factory, + Profile* profile); + + virtual ~SettingsFrontend(); + + // Must only be called from the FILE thread. |type| should be either + // APP_SETTINGS or EXTENSION_SETTINGS. + syncer::SyncableService* GetBackendForSync(syncer::ModelType type) const; + + // Returns true if |settings_namespace| is a valid namespace. + bool IsStorageEnabled(settings_namespace::Namespace settings_namespace) const; + + // Runs |callback| with the storage area of the given |settings_namespace| + // for the |extension_id|. + void RunWithStorage( + const std::string& extension_id, + settings_namespace::Namespace settings_namespace, + const ValueStoreCache::StorageCallback& callback); + + // Deletes the settings for the given |extension_id|. + void DeleteStorageSoon(const std::string& extension_id); + + // Gets the thread-safe observer list. + scoped_refptr GetObservers(); + + void DisableStorageForTesting( + settings_namespace::Namespace settings_namespace); + + private: + typedef std::map CacheMap; + + SettingsFrontend( + const scoped_refptr& storage_factory, + Profile* profile); + + // The quota limit configurations for the local and sync areas, taken out of + // the schema in chrome/common/extensions/api/storage.json. + const SettingsStorageQuotaEnforcer::Limits local_quota_limit_; + const SettingsStorageQuotaEnforcer::Limits sync_quota_limit_; + + // The (non-incognito) Profile this Frontend belongs to. + Profile* const profile_; + + // List of observers to settings changes. + scoped_refptr observers_; + + // Observer for |profile_|. + scoped_ptr profile_observer_; + + // Maps a known namespace to its corresponding ValueStoreCache. The caches + // are owned by this object. + CacheMap caches_; + + DISALLOW_COPY_AND_ASSIGN(SettingsFrontend); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTINGS_FRONTEND_H_ diff --git a/chrome/browser/extensions/settings/settings_frontend_unittest.cc b/chrome/browser/extensions/settings/settings_frontend_unittest.cc new file mode 100644 index 0000000..34e2318 --- /dev/null +++ b/chrome/browser/extensions/settings/settings_frontend_unittest.cc @@ -0,0 +1,289 @@ +// 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 "testing/gtest/include/gtest/gtest.h" + +#include "base/bind.h" +#include "base/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "base/stringprintf.h" +#include "chrome/browser/extensions/settings/leveldb_settings_storage_factory.h" +#include "chrome/browser/extensions/settings/settings_frontend.h" +#include "chrome/browser/extensions/settings/settings_namespace.h" +#include "chrome/browser/extensions/settings/settings_test_util.h" +#include "chrome/browser/value_store/value_store.h" +#include "chrome/common/chrome_notification_types.h" +#include "content/public/test/test_browser_thread.h" + +using content::BrowserThread; + +namespace extensions { + +namespace settings = settings_namespace; +namespace util = settings_test_util; + +namespace { + +// To save typing ValueStore::DEFAULTS everywhere. +const ValueStore::WriteOptions DEFAULTS = ValueStore::DEFAULTS; + +// Creates a kilobyte of data. +scoped_ptr CreateKilobyte() { + std::string kilobyte_string; + for (int i = 0; i < 1024; ++i) { + kilobyte_string += "a"; + } + return scoped_ptr(Value::CreateStringValue(kilobyte_string)); +} + +// Creates a megabyte of data. +scoped_ptr CreateMegabyte() { + ListValue* megabyte = new ListValue(); + for (int i = 0; i < 1000; ++i) { + megabyte->Append(CreateKilobyte().release()); + } + return scoped_ptr(megabyte); +} + +} + +class ExtensionSettingsFrontendTest : public testing::Test { + public: + ExtensionSettingsFrontendTest() + : storage_factory_(new util::ScopedSettingsStorageFactory()), + ui_thread_(BrowserThread::UI, MessageLoop::current()), + file_thread_(BrowserThread::FILE, MessageLoop::current()) {} + + virtual void SetUp() OVERRIDE { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + profile_.reset(new util::MockProfile(temp_dir_.path())); + ResetFrontend(); + } + + virtual void TearDown() OVERRIDE { + frontend_.reset(); + profile_.reset(); + // Execute any pending deletion tasks. + message_loop_.RunUntilIdle(); + } + + protected: + void ResetFrontend() { + storage_factory_->Reset(new LeveldbSettingsStorageFactory()); + frontend_.reset( + SettingsFrontend::Create(storage_factory_.get(), profile_.get())); + } + + base::ScopedTempDir temp_dir_; + scoped_ptr profile_; + scoped_ptr frontend_; + scoped_refptr storage_factory_; + + private: + MessageLoop message_loop_; + content::TestBrowserThread ui_thread_; + content::TestBrowserThread file_thread_; +}; + +// Get a semblance of coverage for both extension and app settings by +// alternating in each test. +// TODO(kalman): explicitly test the two interact correctly. + +TEST_F(ExtensionSettingsFrontendTest, SettingsPreservedAcrossReconstruction) { + const std::string id = "ext"; + ExtensionServiceInterface* esi = + extensions::ExtensionSystem::Get(profile_.get())->extension_service(); + static_cast(esi)-> + AddExtensionWithId(id, Extension::TYPE_EXTENSION); + + ValueStore* storage = util::GetStorage(id, frontend_.get()); + + // The correctness of Get/Set/Remove/Clear is tested elsewhere so no need to + // be too rigorous. + { + StringValue bar("bar"); + ValueStore::WriteResult result = storage->Set(DEFAULTS, "foo", bar); + ASSERT_FALSE(result->HasError()); + } + + { + ValueStore::ReadResult result = storage->Get(); + ASSERT_FALSE(result->HasError()); + EXPECT_FALSE(result->settings()->empty()); + } + + ResetFrontend(); + storage = util::GetStorage(id, frontend_.get()); + + { + ValueStore::ReadResult result = storage->Get(); + ASSERT_FALSE(result->HasError()); + EXPECT_FALSE(result->settings()->empty()); + } +} + +TEST_F(ExtensionSettingsFrontendTest, SettingsClearedOnUninstall) { + const std::string id = "ext"; + ExtensionServiceInterface* esi = + extensions::ExtensionSystem::Get(profile_.get())->extension_service(); + static_cast(esi)-> + AddExtensionWithId(id, Extension::TYPE_LEGACY_PACKAGED_APP); + + ValueStore* storage = util::GetStorage(id, frontend_.get()); + + { + StringValue bar("bar"); + ValueStore::WriteResult result = storage->Set(DEFAULTS, "foo", bar); + ASSERT_FALSE(result->HasError()); + } + + // This would be triggered by extension uninstall via a DataDeleter. + frontend_->DeleteStorageSoon(id); + MessageLoop::current()->RunUntilIdle(); + + // The storage area may no longer be valid post-uninstall, so re-request. + storage = util::GetStorage(id, frontend_.get()); + { + ValueStore::ReadResult result = storage->Get(); + ASSERT_FALSE(result->HasError()); + EXPECT_TRUE(result->settings()->empty()); + } +} + +TEST_F(ExtensionSettingsFrontendTest, LeveldbDatabaseDeletedFromDiskOnClear) { + const std::string id = "ext"; + ExtensionServiceInterface* esi = + extensions::ExtensionSystem::Get(profile_.get())->extension_service(); + static_cast(esi)-> + AddExtensionWithId(id, Extension::TYPE_EXTENSION); + + ValueStore* storage = util::GetStorage(id, frontend_.get()); + + { + StringValue bar("bar"); + ValueStore::WriteResult result = storage->Set(DEFAULTS, "foo", bar); + ASSERT_FALSE(result->HasError()); + EXPECT_TRUE(file_util::PathExists(temp_dir_.path())); + } + + // Should need to both clear the database and delete the frontend for the + // leveldb database to be deleted from disk. + { + ValueStore::WriteResult result = storage->Clear(); + ASSERT_FALSE(result->HasError()); + EXPECT_TRUE(file_util::PathExists(temp_dir_.path())); + } + + frontend_.reset(); + MessageLoop::current()->RunUntilIdle(); + // TODO(kalman): Figure out why this fails, despite appearing to work. + // Leaving this commented out rather than disabling the whole test so that the + // deletion code paths are at least exercised. + //EXPECT_FALSE(file_util::PathExists(temp_dir_.path())); +} + +#if defined(OS_WIN) +// Failing on vista dbg. http://crbug.com/111100, http://crbug.com/108724 +#define QuotaLimitsEnforcedCorrectlyForSyncAndLocal \ + DISABLED_QuotaLimitsEnforcedCorrectlyForSyncAndLocal +#endif +TEST_F(ExtensionSettingsFrontendTest, + QuotaLimitsEnforcedCorrectlyForSyncAndLocal) { + const std::string id = "ext"; + ExtensionServiceInterface* esi = + extensions::ExtensionSystem::Get(profile_.get())->extension_service(); + static_cast(esi)-> + AddExtensionWithId(id, Extension::TYPE_EXTENSION); + + ValueStore* sync_storage = + util::GetStorage(id, settings::SYNC, frontend_.get()); + ValueStore* local_storage = + util::GetStorage(id, settings::LOCAL, frontend_.get()); + + // Sync storage should run out after ~100K. + scoped_ptr kilobyte = CreateKilobyte(); + for (int i = 0; i < 100; ++i) { + sync_storage->Set( + ValueStore::DEFAULTS, base::StringPrintf("%d", i), *kilobyte); + } + + EXPECT_TRUE(sync_storage->Set( + ValueStore::DEFAULTS, "WillError", *kilobyte)->HasError()); + + // Local storage shouldn't run out after ~100K. + for (int i = 0; i < 100; ++i) { + local_storage->Set( + ValueStore::DEFAULTS, base::StringPrintf("%d", i), *kilobyte); + } + + EXPECT_FALSE(local_storage->Set( + ValueStore::DEFAULTS, "WontError", *kilobyte)->HasError()); + + // Local storage should run out after ~5MB. + scoped_ptr megabyte = CreateMegabyte(); + for (int i = 0; i < 5; ++i) { + local_storage->Set( + ValueStore::DEFAULTS, base::StringPrintf("%d", i), *megabyte); + } + + EXPECT_TRUE(local_storage->Set( + ValueStore::DEFAULTS, "WillError", *megabyte)->HasError()); +} + +// In other tests, we assume that the result of GetStorage is a pointer to the +// a Storage owned by a Frontend object, but for the unlimitedStorage case, this +// might not be true. So, write the tests in a "callback" style. +// We should really rewrite all tests to be asynchronous in this way. + +static void UnlimitedSyncStorageTestCallback(ValueStore* sync_storage) { + // Sync storage should still run out after ~100K; the unlimitedStorage + // permission can't apply to sync. + scoped_ptr kilobyte = CreateKilobyte(); + for (int i = 0; i < 100; ++i) { + sync_storage->Set( + ValueStore::DEFAULTS, base::StringPrintf("%d", i), *kilobyte); + } + + EXPECT_TRUE(sync_storage->Set( + ValueStore::DEFAULTS, "WillError", *kilobyte)->HasError()); +} + +static void UnlimitedLocalStorageTestCallback(ValueStore* local_storage) { + // Local storage should never run out. + scoped_ptr megabyte = CreateMegabyte(); + for (int i = 0; i < 7; ++i) { + local_storage->Set( + ValueStore::DEFAULTS, base::StringPrintf("%d", i), *megabyte); + } + + EXPECT_FALSE(local_storage->Set( + ValueStore::DEFAULTS, "WontError", *megabyte)->HasError()); +} + +#if defined(OS_WIN) +// Failing on vista dbg. http://crbug.com/111100, http://crbug.com/108724 +#define UnlimitedStorageForLocalButNotSync DISABLED_UnlimitedStorageForLocalButNotSync +#endif +TEST_F(ExtensionSettingsFrontendTest, + UnlimitedStorageForLocalButNotSync) { + const std::string id = "ext"; + std::set permissions; + permissions.insert("unlimitedStorage"); + ExtensionServiceInterface* esi = + extensions::ExtensionSystem::Get(profile_.get())->extension_service(); + static_cast(esi)-> + AddExtensionWithIdAndPermissions(id, Extension::TYPE_EXTENSION, + permissions); + + frontend_->RunWithStorage( + id, settings::SYNC, base::Bind(&UnlimitedSyncStorageTestCallback)); + frontend_->RunWithStorage( + id, settings::LOCAL, base::Bind(&UnlimitedLocalStorageTestCallback)); + + MessageLoop::current()->RunUntilIdle(); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/settings/settings_namespace.cc b/chrome/browser/extensions/settings/settings_namespace.cc new file mode 100644 index 0000000..82767ca --- /dev/null +++ b/chrome/browser/extensions/settings/settings_namespace.cc @@ -0,0 +1,42 @@ +// 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/settings/settings_namespace.h" + +#include "base/logging.h" + +namespace extensions { + +namespace settings_namespace { + +namespace { +const char kLocalNamespace[] = "local"; +const char kSyncNamespace[] = "sync"; +const char kManagedNamespace[] = "managed"; +} // namespace + +std::string ToString(Namespace settings_namespace) { + switch (settings_namespace) { + case LOCAL: return kLocalNamespace; + case SYNC: return kSyncNamespace; + case MANAGED: return kManagedNamespace; + case INVALID: break; + } + NOTREACHED(); + return std::string(); +} + +Namespace FromString(const std::string& namespace_string) { + if (namespace_string == kLocalNamespace) + return LOCAL; + if (namespace_string == kSyncNamespace) + return SYNC; + if (namespace_string == kManagedNamespace) + return MANAGED; + return INVALID; +} + +} // namespace settings_namespace + +} // namespace extensions diff --git a/chrome/browser/extensions/settings/settings_namespace.h b/chrome/browser/extensions/settings/settings_namespace.h new file mode 100644 index 0000000..c89658b --- /dev/null +++ b/chrome/browser/extensions/settings/settings_namespace.h @@ -0,0 +1,34 @@ +// 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_SETTINGS_SETTINGS_NAMESPACE_H_ +#define CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTINGS_NAMESPACE_H_ + +#include + +namespace extensions { + +namespace settings_namespace { + +// The namespaces of the storage areas. +enum Namespace { + LOCAL, // "local" i.e. chrome.storage.local + SYNC, // "sync" i.e. chrome.storage.sync + MANAGED, // "managed" i.e. chrome.storage.managed + INVALID +}; + +// Converts a namespace to its string representation. +// Namespace must not be INVALID. +std::string ToString(Namespace settings_namespace); + +// Converts a string representation of a namespace to its namespace, or INVALID +// if the string doesn't map to one. +Namespace FromString(const std::string& ns_string); + +} // namespace settings_namespace + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTINGS_NAMESPACE_H_ diff --git a/chrome/browser/extensions/settings/settings_observer.h b/chrome/browser/extensions/settings/settings_observer.h new file mode 100644 index 0000000..f789b95 --- /dev/null +++ b/chrome/browser/extensions/settings/settings_observer.h @@ -0,0 +1,30 @@ +// 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_EXTENSIONS_SETTINGS_SETTINGS_OBSERVER_H_ +#define CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTINGS_OBSERVER_H_ + +#include "base/observer_list_threadsafe.h" +#include "chrome/browser/extensions/settings/settings_namespace.h" + +namespace extensions { + +// Interface for classes that listen to changes to extension settings. +class SettingsObserver { + public: + // Called when a list of settings have changed for an extension. + virtual void OnSettingsChanged( + const std::string& extension_id, + settings_namespace::Namespace settings_namespace, + const std::string& changes_json) = 0; + + virtual ~SettingsObserver() {} +}; + +typedef ObserverListThreadSafe + SettingsObserverList; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTINGS_OBSERVER_H_ diff --git a/chrome/browser/extensions/settings/settings_quota_unittest.cc b/chrome/browser/extensions/settings/settings_quota_unittest.cc new file mode 100644 index 0000000..c8b66c6 --- /dev/null +++ b/chrome/browser/extensions/settings/settings_quota_unittest.cc @@ -0,0 +1,595 @@ +// 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 "testing/gtest/include/gtest/gtest.h" + +#include "base/json/json_writer.h" +#include "base/values.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/extensions/settings/settings_backend.h" +#include "chrome/browser/extensions/settings/settings_storage_quota_enforcer.h" +#include "chrome/browser/value_store/testing_value_store.h" + +namespace extensions { + +// To save typing ValueStore::DEFAULTS/IGNORE_QUOTA everywhere. +const ValueStore::WriteOptions DEFAULTS = ValueStore::DEFAULTS; +const ValueStore::WriteOptions IGNORE_QUOTA = + ValueStore::IGNORE_QUOTA; + +class ExtensionSettingsQuotaTest : public testing::Test { + public: + ExtensionSettingsQuotaTest() + : byte_value_1_(Value::CreateIntegerValue(1)), + byte_value_16_(Value::CreateStringValue("sixteen bytes.")), + byte_value_256_(new ListValue()), + delegate_(new TestingValueStore()) { + for (int i = 1; i < 89; ++i) { + byte_value_256_->Append(Value::CreateIntegerValue(i)); + } + ValidateByteValues(); + } + + void ValidateByteValues() { + std::string validate_sizes; + base::JSONWriter::Write(byte_value_1_.get(), &validate_sizes); + ASSERT_EQ(1u, validate_sizes.size()); + base::JSONWriter::Write(byte_value_16_.get(), &validate_sizes); + ASSERT_EQ(16u, validate_sizes.size()); + base::JSONWriter::Write(byte_value_256_.get(), &validate_sizes); + ASSERT_EQ(256u, validate_sizes.size()); + } + + virtual void TearDown() OVERRIDE { + ASSERT_TRUE(storage_.get() != NULL); + } + + protected: + // Creates |storage_|. Must only be called once. + void CreateStorage( + size_t quota_bytes, size_t quota_bytes_per_item, size_t max_items) { + ASSERT_TRUE(storage_.get() == NULL); + SettingsStorageQuotaEnforcer::Limits limits = + { quota_bytes, quota_bytes_per_item, max_items }; + storage_.reset(new SettingsStorageQuotaEnforcer(limits, delegate_)); + } + + // Returns whether the settings in |storage_| and |delegate_| are the same as + // |settings|. + bool SettingsEqual(const DictionaryValue& settings) { + return settings.Equals(storage_->Get()->settings().get()) && + settings.Equals(delegate_->Get()->settings().get()); + } + + // Values with different serialized sizes. + scoped_ptr byte_value_1_; + scoped_ptr byte_value_16_; + scoped_ptr byte_value_256_; + + // Quota enforcing storage area being tested. + scoped_ptr storage_; + + // In-memory storage area being delegated to. Always owned by |storage_|. + TestingValueStore* delegate_; +}; + +TEST_F(ExtensionSettingsQuotaTest, ZeroQuotaBytes) { + DictionaryValue empty; + CreateStorage(0, UINT_MAX, UINT_MAX); + + EXPECT_TRUE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); + EXPECT_FALSE(storage_->Remove("a")->HasError()); + EXPECT_FALSE(storage_->Remove("b")->HasError()); + EXPECT_TRUE(SettingsEqual(empty)); +} + +TEST_F(ExtensionSettingsQuotaTest, KeySizeTakenIntoAccount) { + DictionaryValue empty; + CreateStorage(8u, UINT_MAX, UINT_MAX); + EXPECT_TRUE( + storage_->Set(DEFAULTS, "Really long key", *byte_value_1_)->HasError()); + EXPECT_TRUE(SettingsEqual(empty)); +} + +TEST_F(ExtensionSettingsQuotaTest, SmallByteQuota) { + DictionaryValue settings; + CreateStorage(8u, UINT_MAX, UINT_MAX); + + EXPECT_FALSE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); + settings.Set("a", byte_value_1_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + EXPECT_TRUE(storage_->Set(DEFAULTS, "b", *byte_value_16_)->HasError()); + EXPECT_TRUE(storage_->Set(DEFAULTS, "c", *byte_value_256_)->HasError()); + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaTest, MediumByteQuota) { + DictionaryValue settings; + CreateStorage(40, UINT_MAX, UINT_MAX); + + DictionaryValue to_set; + to_set.Set("a", byte_value_1_->DeepCopy()); + to_set.Set("b", byte_value_16_->DeepCopy()); + EXPECT_FALSE(storage_->Set(DEFAULTS, to_set)->HasError()); + settings.Set("a", byte_value_1_->DeepCopy()); + settings.Set("b", byte_value_16_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Should be able to set value to other under-quota value. + to_set.Set("a", byte_value_16_->DeepCopy()); + EXPECT_FALSE(storage_->Set(DEFAULTS, to_set)->HasError()); + settings.Set("a", byte_value_16_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + EXPECT_TRUE(storage_->Set(DEFAULTS, "c", *byte_value_256_)->HasError()); + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaTest, ZeroMaxKeys) { + DictionaryValue empty; + CreateStorage(UINT_MAX, UINT_MAX, 0); + + EXPECT_TRUE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); + EXPECT_FALSE(storage_->Remove("a")->HasError()); + EXPECT_FALSE(storage_->Remove("b")->HasError()); + EXPECT_TRUE(SettingsEqual(empty)); +} + +TEST_F(ExtensionSettingsQuotaTest, SmallMaxKeys) { + DictionaryValue settings; + CreateStorage(UINT_MAX, UINT_MAX, 1); + + EXPECT_FALSE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); + settings.Set("a", byte_value_1_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Should be able to set existing key to other value without going over quota. + EXPECT_FALSE(storage_->Set(DEFAULTS, "a", *byte_value_16_)->HasError()); + settings.Set("a", byte_value_16_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + EXPECT_TRUE(storage_->Set(DEFAULTS, "b", *byte_value_16_)->HasError()); + EXPECT_TRUE(storage_->Set(DEFAULTS, "c", *byte_value_256_)->HasError()); + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaTest, MediumMaxKeys) { + DictionaryValue settings; + CreateStorage(UINT_MAX, UINT_MAX, 2); + + DictionaryValue to_set; + to_set.Set("a", byte_value_1_->DeepCopy()); + to_set.Set("b", byte_value_16_->DeepCopy()); + EXPECT_FALSE(storage_->Set(DEFAULTS, to_set)->HasError()); + settings.Set("a", byte_value_1_->DeepCopy()); + settings.Set("b", byte_value_16_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Should be able to set existing keys to other values without going over + // quota. + to_set.Set("a", byte_value_16_->DeepCopy()); + EXPECT_FALSE(storage_->Set(DEFAULTS, to_set)->HasError()); + settings.Set("a", byte_value_16_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + EXPECT_TRUE(storage_->Set(DEFAULTS, "c", *byte_value_256_)->HasError()); + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaTest, RemovingExistingSettings) { + DictionaryValue settings; + CreateStorage(266, UINT_MAX, 2); + + storage_->Set(DEFAULTS, "b", *byte_value_16_); + settings.Set("b", byte_value_16_->DeepCopy()); + // Not enough quota. + storage_->Set(DEFAULTS, "c", *byte_value_256_); + EXPECT_TRUE(SettingsEqual(settings)); + + // Try again with "b" removed, enough quota. + EXPECT_FALSE(storage_->Remove("b")->HasError()); + settings.Remove("b", NULL); + EXPECT_FALSE(storage_->Set(DEFAULTS, "c", *byte_value_256_)->HasError()); + settings.Set("c", byte_value_256_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Enough byte quota but max keys not high enough. + EXPECT_FALSE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); + settings.Set("a", byte_value_1_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + EXPECT_TRUE(storage_->Set(DEFAULTS, "b", *byte_value_1_)->HasError()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Back under max keys. + EXPECT_FALSE(storage_->Remove("a")->HasError()); + settings.Remove("a", NULL); + EXPECT_FALSE(storage_->Set(DEFAULTS, "b", *byte_value_1_)->HasError()); + settings.Set("b", byte_value_1_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaTest, RemovingNonexistentSettings) { + DictionaryValue settings; + CreateStorage(36, UINT_MAX, 3); + + // Max out bytes. + DictionaryValue to_set; + to_set.Set("b1", byte_value_16_->DeepCopy()); + to_set.Set("b2", byte_value_16_->DeepCopy()); + storage_->Set(DEFAULTS, to_set); + settings.Set("b1", byte_value_16_->DeepCopy()); + settings.Set("b2", byte_value_16_->DeepCopy()); + EXPECT_TRUE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Remove some settings that don't exist. + std::vector to_remove; + to_remove.push_back("a1"); + to_remove.push_back("a2"); + EXPECT_FALSE(storage_->Remove(to_remove)->HasError()); + EXPECT_FALSE(storage_->Remove("b")->HasError()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Still no quota. + EXPECT_TRUE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Max out key count. + to_set.Clear(); + to_set.Set("b1", byte_value_1_->DeepCopy()); + to_set.Set("b2", byte_value_1_->DeepCopy()); + storage_->Set(DEFAULTS, to_set); + settings.Set("b1", byte_value_1_->DeepCopy()); + settings.Set("b2", byte_value_1_->DeepCopy()); + storage_->Set(DEFAULTS, "b3", *byte_value_1_); + settings.Set("b3", byte_value_1_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Remove some settings that don't exist. + to_remove.clear(); + to_remove.push_back("a1"); + to_remove.push_back("a2"); + EXPECT_FALSE(storage_->Remove(to_remove)->HasError()); + EXPECT_FALSE(storage_->Remove("b")->HasError()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Still no quota. + EXPECT_TRUE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaTest, Clear) { + DictionaryValue settings; + CreateStorage(40, UINT_MAX, 5); + + // Test running out of byte quota. + { + DictionaryValue to_set; + to_set.Set("a", byte_value_16_->DeepCopy()); + to_set.Set("b", byte_value_16_->DeepCopy()); + EXPECT_FALSE(storage_->Set(DEFAULTS, to_set)->HasError()); + EXPECT_TRUE(storage_->Set(DEFAULTS, "c", *byte_value_16_)->HasError()); + + EXPECT_FALSE(storage_->Clear()->HasError()); + + // (repeat) + EXPECT_FALSE(storage_->Set(DEFAULTS, to_set)->HasError()); + EXPECT_TRUE(storage_->Set(DEFAULTS, "c", *byte_value_16_)->HasError()); + } + + // Test reaching max keys. + storage_->Clear(); + { + DictionaryValue to_set; + to_set.Set("a", byte_value_1_->DeepCopy()); + to_set.Set("b", byte_value_1_->DeepCopy()); + to_set.Set("c", byte_value_1_->DeepCopy()); + to_set.Set("d", byte_value_1_->DeepCopy()); + to_set.Set("e", byte_value_1_->DeepCopy()); + EXPECT_FALSE(storage_->Set(DEFAULTS, to_set)->HasError()); + EXPECT_TRUE(storage_->Set(DEFAULTS, "f", *byte_value_1_)->HasError()); + + storage_->Clear(); + + // (repeat) + EXPECT_FALSE(storage_->Set(DEFAULTS, to_set)->HasError()); + EXPECT_TRUE(storage_->Set(DEFAULTS, "f", *byte_value_1_)->HasError()); + } +} + +TEST_F(ExtensionSettingsQuotaTest, ChangingUsedBytesWithSet) { + DictionaryValue settings; + CreateStorage(20, UINT_MAX, UINT_MAX); + + // Change a setting to make it go over quota. + storage_->Set(DEFAULTS, "a", *byte_value_16_); + settings.Set("a", byte_value_16_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + EXPECT_TRUE(storage_->Set(DEFAULTS, "a", *byte_value_256_)->HasError()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Change a setting to reduce usage and room for another setting. + EXPECT_TRUE(storage_->Set(DEFAULTS, "foobar", *byte_value_1_)->HasError()); + storage_->Set(DEFAULTS, "a", *byte_value_1_); + settings.Set("a", byte_value_1_->DeepCopy()); + + EXPECT_FALSE(storage_->Set(DEFAULTS, "foobar", *byte_value_1_)->HasError()); + settings.Set("foobar", byte_value_1_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaTest, SetsOnlyEntirelyCompletedWithByteQuota) { + DictionaryValue settings; + CreateStorage(40, UINT_MAX, UINT_MAX); + + storage_->Set(DEFAULTS, "a", *byte_value_16_); + settings.Set("a", byte_value_16_->DeepCopy()); + + // The entire change is over quota. + DictionaryValue to_set; + to_set.Set("b", byte_value_16_->DeepCopy()); + to_set.Set("c", byte_value_16_->DeepCopy()); + EXPECT_TRUE(storage_->Set(DEFAULTS, to_set)->HasError()); + EXPECT_TRUE(SettingsEqual(settings)); + + // The entire change is over quota, but quota reduced in existing key. + to_set.Set("a", byte_value_1_->DeepCopy()); + EXPECT_FALSE(storage_->Set(DEFAULTS, to_set)->HasError()); + settings.Set("a", byte_value_1_->DeepCopy()); + settings.Set("b", byte_value_16_->DeepCopy()); + settings.Set("c", byte_value_16_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaTest, SetsOnlyEntireCompletedWithMaxKeys) { + DictionaryValue settings; + CreateStorage(UINT_MAX, UINT_MAX, 2); + + storage_->Set(DEFAULTS, "a", *byte_value_1_); + settings.Set("a", byte_value_1_->DeepCopy()); + + DictionaryValue to_set; + to_set.Set("b", byte_value_16_->DeepCopy()); + to_set.Set("c", byte_value_16_->DeepCopy()); + EXPECT_TRUE(storage_->Set(DEFAULTS, to_set)->HasError()); + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaTest, WithInitialDataAndByteQuota) { + DictionaryValue settings; + delegate_->Set(DEFAULTS, "a", *byte_value_256_); + settings.Set("a", byte_value_256_->DeepCopy()); + + CreateStorage(280, UINT_MAX, UINT_MAX); + EXPECT_TRUE(SettingsEqual(settings)); + + // Add some data. + EXPECT_FALSE(storage_->Set(DEFAULTS, "b", *byte_value_16_)->HasError()); + settings.Set("b", byte_value_16_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Not enough quota. + EXPECT_TRUE(storage_->Set(DEFAULTS, "c", *byte_value_16_)->HasError()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Reduce usage of original setting so that "c" can fit. + EXPECT_FALSE(storage_->Set(DEFAULTS, "a", *byte_value_16_)->HasError()); + settings.Set("a", byte_value_16_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + EXPECT_FALSE(storage_->Set(DEFAULTS, "c", *byte_value_16_)->HasError()); + settings.Set("c", byte_value_16_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Remove to free up some more data. + EXPECT_TRUE(storage_->Set(DEFAULTS, "d", *byte_value_256_)->HasError()); + + std::vector to_remove; + to_remove.push_back("a"); + to_remove.push_back("b"); + storage_->Remove(to_remove); + settings.Remove("a", NULL); + settings.Remove("b", NULL); + EXPECT_TRUE(SettingsEqual(settings)); + + EXPECT_FALSE(storage_->Set(DEFAULTS, "d", *byte_value_256_)->HasError()); + settings.Set("d", byte_value_256_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaTest, WithInitialDataAndMaxKeys) { + DictionaryValue settings; + delegate_->Set(DEFAULTS, "a", *byte_value_1_); + settings.Set("a", byte_value_1_->DeepCopy()); + CreateStorage(UINT_MAX, UINT_MAX, 2); + + EXPECT_FALSE(storage_->Set(DEFAULTS, "b", *byte_value_1_)->HasError()); + settings.Set("b", byte_value_1_->DeepCopy()); + + EXPECT_TRUE(storage_->Set(DEFAULTS, "c", *byte_value_1_)->HasError()); + + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaTest, InitiallyOverByteQuota) { + DictionaryValue settings; + settings.Set("a", byte_value_16_->DeepCopy()); + settings.Set("b", byte_value_16_->DeepCopy()); + settings.Set("c", byte_value_16_->DeepCopy()); + delegate_->Set(DEFAULTS, settings); + + CreateStorage(40, UINT_MAX, UINT_MAX); + EXPECT_TRUE(SettingsEqual(settings)); + + EXPECT_TRUE(storage_->Set(DEFAULTS, "d", *byte_value_16_)->HasError()); + + // Take under quota by reducing size of an existing setting + EXPECT_FALSE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); + settings.Set("a", byte_value_1_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Should be able set another small setting. + EXPECT_FALSE(storage_->Set(DEFAULTS, "d", *byte_value_1_)->HasError()); + settings.Set("d", byte_value_1_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaTest, InitiallyOverMaxKeys) { + DictionaryValue settings; + settings.Set("a", byte_value_16_->DeepCopy()); + settings.Set("b", byte_value_16_->DeepCopy()); + settings.Set("c", byte_value_16_->DeepCopy()); + delegate_->Set(DEFAULTS, settings); + + CreateStorage(UINT_MAX, UINT_MAX, 2); + EXPECT_TRUE(SettingsEqual(settings)); + + // Can't set either an existing or new setting. + EXPECT_TRUE(storage_->Set(DEFAULTS, "d", *byte_value_16_)->HasError()); + EXPECT_TRUE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Should be able after removing 2. + storage_->Remove("a"); + settings.Remove("a", NULL); + storage_->Remove("b"); + settings.Remove("b", NULL); + EXPECT_TRUE(SettingsEqual(settings)); + + EXPECT_FALSE(storage_->Set(DEFAULTS, "e", *byte_value_1_)->HasError()); + settings.Set("e", byte_value_1_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Still can't set any. + EXPECT_TRUE(storage_->Set(DEFAULTS, "d", *byte_value_16_)->HasError()); + EXPECT_TRUE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaTest, ZeroQuotaBytesPerSetting) { + DictionaryValue empty; + CreateStorage(UINT_MAX, 0, UINT_MAX); + + EXPECT_TRUE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); + EXPECT_FALSE(storage_->Remove("a")->HasError()); + EXPECT_FALSE(storage_->Remove("b")->HasError()); + EXPECT_TRUE(SettingsEqual(empty)); +} + +TEST_F(ExtensionSettingsQuotaTest, QuotaBytesPerSetting) { + DictionaryValue settings; + + CreateStorage(UINT_MAX, 20, UINT_MAX); + + EXPECT_FALSE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); + EXPECT_FALSE(storage_->Set(DEFAULTS, "a", *byte_value_16_)->HasError()); + settings.Set("a", byte_value_16_->DeepCopy()); + EXPECT_TRUE(storage_->Set(DEFAULTS, "a", *byte_value_256_)->HasError()); + + EXPECT_FALSE(storage_->Set(DEFAULTS, "b", *byte_value_1_)->HasError()); + EXPECT_FALSE(storage_->Set(DEFAULTS, "b", *byte_value_16_)->HasError()); + settings.Set("b", byte_value_16_->DeepCopy()); + EXPECT_TRUE(storage_->Set(DEFAULTS, "b", *byte_value_256_)->HasError()); + + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaTest, QuotaBytesPerSettingWithInitialSettings) { + DictionaryValue settings; + + delegate_->Set(DEFAULTS, "a", *byte_value_1_); + delegate_->Set(DEFAULTS, "b", *byte_value_16_); + delegate_->Set(DEFAULTS, "c", *byte_value_256_); + CreateStorage(UINT_MAX, 20, UINT_MAX); + + EXPECT_FALSE(storage_->Set(DEFAULTS, "a", *byte_value_1_)->HasError()); + EXPECT_FALSE(storage_->Set(DEFAULTS, "a", *byte_value_16_)->HasError()); + settings.Set("a", byte_value_16_->DeepCopy()); + EXPECT_TRUE(storage_->Set(DEFAULTS, "a", *byte_value_256_)->HasError()); + + EXPECT_FALSE(storage_->Set(DEFAULTS, "b", *byte_value_1_)->HasError()); + EXPECT_FALSE(storage_->Set(DEFAULTS, "b", *byte_value_16_)->HasError()); + settings.Set("b", byte_value_16_->DeepCopy()); + EXPECT_TRUE(storage_->Set(DEFAULTS, "b", *byte_value_256_)->HasError()); + + EXPECT_FALSE(storage_->Set(DEFAULTS, "c", *byte_value_1_)->HasError()); + EXPECT_FALSE(storage_->Set(DEFAULTS, "c", *byte_value_16_)->HasError()); + settings.Set("c", byte_value_16_->DeepCopy()); + EXPECT_TRUE(storage_->Set(DEFAULTS, "c", *byte_value_256_)->HasError()); + + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaTest, + QuotaBytesPerSettingWithInitialSettingsForced) { + // This is a lazy test to make sure IGNORE_QUOTA lets through changes: the + // test above copied, but using IGNORE_QUOTA and asserting nothing is ever + // rejected... + DictionaryValue settings; + + delegate_->Set(DEFAULTS, "a", *byte_value_1_); + delegate_->Set(DEFAULTS, "b", *byte_value_16_); + delegate_->Set(DEFAULTS, "c", *byte_value_256_); + CreateStorage(UINT_MAX, 20, UINT_MAX); + + EXPECT_FALSE(storage_->Set(IGNORE_QUOTA, "a", *byte_value_1_)->HasError()); + EXPECT_FALSE(storage_->Set(IGNORE_QUOTA, "a", *byte_value_16_)->HasError()); + EXPECT_FALSE(storage_->Set(IGNORE_QUOTA, "a", *byte_value_256_)->HasError()); + settings.Set("a", byte_value_256_->DeepCopy()); + + EXPECT_FALSE(storage_->Set(IGNORE_QUOTA, "b", *byte_value_1_)->HasError()); + EXPECT_FALSE(storage_->Set(IGNORE_QUOTA, "b", *byte_value_16_)->HasError()); + EXPECT_FALSE(storage_->Set(IGNORE_QUOTA, "b", *byte_value_256_)->HasError()); + settings.Set("b", byte_value_256_->DeepCopy()); + + EXPECT_FALSE(storage_->Set(IGNORE_QUOTA, "c", *byte_value_1_)->HasError()); + EXPECT_FALSE(storage_->Set(IGNORE_QUOTA, "c", *byte_value_16_)->HasError()); + settings.Set("c", byte_value_16_->DeepCopy()); + + // ... except the last. Make sure it can still fail. + EXPECT_TRUE(storage_->Set(DEFAULTS, "c", *byte_value_256_)->HasError()); + + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaTest, GetBytesInUse) { + // Just testing GetBytesInUse, no need for a quota. + CreateStorage(UINT_MAX, UINT_MAX, UINT_MAX); + + std::vector ab; + ab.push_back("a"); + ab.push_back("b"); + + EXPECT_EQ(0u, storage_->GetBytesInUse()); + EXPECT_EQ(0u, storage_->GetBytesInUse("a")); + EXPECT_EQ(0u, storage_->GetBytesInUse("b")); + EXPECT_EQ(0u, storage_->GetBytesInUse(ab)); + + storage_->Set(DEFAULTS, "a", *byte_value_1_); + + EXPECT_EQ(2u, storage_->GetBytesInUse()); + EXPECT_EQ(2u, storage_->GetBytesInUse("a")); + EXPECT_EQ(0u, storage_->GetBytesInUse("b")); + EXPECT_EQ(2u, storage_->GetBytesInUse(ab)); + + storage_->Set(DEFAULTS, "b", *byte_value_1_); + + EXPECT_EQ(4u, storage_->GetBytesInUse()); + EXPECT_EQ(2u, storage_->GetBytesInUse("a")); + EXPECT_EQ(2u, storage_->GetBytesInUse("b")); + EXPECT_EQ(4u, storage_->GetBytesInUse(ab)); + + storage_->Set(DEFAULTS, "c", *byte_value_1_); + + EXPECT_EQ(6u, storage_->GetBytesInUse()); + EXPECT_EQ(2u, storage_->GetBytesInUse("a")); + EXPECT_EQ(2u, storage_->GetBytesInUse("b")); + EXPECT_EQ(4u, storage_->GetBytesInUse(ab)); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/settings/settings_storage_factory.h b/chrome/browser/extensions/settings/settings_storage_factory.h new file mode 100644 index 0000000..844e80d --- /dev/null +++ b/chrome/browser/extensions/settings/settings_storage_factory.h @@ -0,0 +1,36 @@ +// 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_SETTINGS_SETTINGS_STORAGE_FACTORY_H_ +#define CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTINGS_STORAGE_FACTORY_H_ + +#include + +#include "base/file_path.h" +#include "base/memory/ref_counted.h" + +class ValueStore; + +namespace extensions { + +// Factory for creating SettingStorage instances. +// +// Refcouted because it's just too messy to distribute these objects between +// SettingsBackend instances any other way. +class SettingsStorageFactory + : public base::RefCountedThreadSafe { + public: + // Creates a new ValueStore area for an extension under |base_path|. + // Return NULL to indicate failure. Must be called on the FILE thread. + virtual ValueStore* Create(const FilePath& base_path, + const std::string& extension_id) = 0; + + protected: + friend class base::RefCountedThreadSafe; + virtual ~SettingsStorageFactory() {} +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTINGS_STORAGE_FACTORY_H_ diff --git a/chrome/browser/extensions/settings/settings_storage_quota_enforcer.cc b/chrome/browser/extensions/settings/settings_storage_quota_enforcer.cc new file mode 100644 index 0000000..a4c81ef --- /dev/null +++ b/chrome/browser/extensions/settings/settings_storage_quota_enforcer.cc @@ -0,0 +1,235 @@ +// 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/settings/settings_storage_quota_enforcer.h" + +#include "base/bind.h" +#include "base/json/json_writer.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "base/metrics/histogram.h" +#include "chrome/common/extensions/api/extension_api.h" +#include "extensions/common/error_utils.h" + +namespace extensions { + +namespace { + +const char* kQuotaExceededError = "* quota exceeded."; + +// Resources there are a quota for. +enum Resource { + QUOTA_BYTES, + QUOTA_BYTES_PER_ITEM, + MAX_ITEMS +}; + +// Allocates a setting in a record of total and per-setting usage. +void Allocate( + const std::string& key, + const Value& value, + size_t* used_total, + std::map* used_per_setting) { + // Calculate the setting size based on its JSON serialization size. + // TODO(kalman): Does this work with different encodings? + // TODO(kalman): This is duplicating work that the leveldb delegate + // implementation is about to do, and it would be nice to avoid this. + std::string value_as_json; + base::JSONWriter::Write(&value, &value_as_json); + size_t new_size = key.size() + value_as_json.size(); + size_t existing_size = (*used_per_setting)[key]; + + *used_total += (new_size - existing_size); + (*used_per_setting)[key] = new_size; +} + +// Frees the allocation of a setting in a record of total and per-setting usage. +void Free( + size_t* used_total, + std::map* used_per_setting, + const std::string& key) { + *used_total -= (*used_per_setting)[key]; + used_per_setting->erase(key); +} + +// Returns an error result and logs the quota exceeded to UMA. +ValueStore::WriteResult QuotaExceededFor(Resource resource) { + std::string name; + switch (resource) { + case QUOTA_BYTES: + name = "QUOTA_BYTES"; + UMA_HISTOGRAM_COUNTS_100( + "Extensions.SettingsQuotaExceeded.TotalBytes", 1); + break; + case QUOTA_BYTES_PER_ITEM: + name = "QUOTA_BYTES_PER_ITEM"; + UMA_HISTOGRAM_COUNTS_100( + "Extensions.SettingsQuotaExceeded.BytesPerSetting", 1); + break; + case MAX_ITEMS: + name = "MAX_ITEMS"; + UMA_HISTOGRAM_COUNTS_100( + "Extensions.SettingsQuotaExceeded.KeyCount", 1); + break; + default: + NOTREACHED(); + } + return ValueStore::MakeWriteResult( + ErrorUtils::FormatErrorMessage(kQuotaExceededError, name)); +} + +} // namespace + +SettingsStorageQuotaEnforcer::SettingsStorageQuotaEnforcer( + const Limits& limits, ValueStore* delegate) + : limits_(limits), delegate_(delegate), used_total_(0) { + ReadResult maybe_settings = delegate_->Get(); + if (maybe_settings->HasError()) { + LOG(WARNING) << "Failed to get initial settings for quota: " << + maybe_settings->error(); + return; + } + + for (DictionaryValue::Iterator it(*maybe_settings->settings().get()); + it.HasNext(); it.Advance()) { + Allocate( + it.key(), it.value(), &used_total_, &used_per_setting_); + } +} + +SettingsStorageQuotaEnforcer::~SettingsStorageQuotaEnforcer() {} + +size_t SettingsStorageQuotaEnforcer::GetBytesInUse(const std::string& key) { + std::map::iterator maybe_used = + used_per_setting_.find(key); + return maybe_used == used_per_setting_.end() ? 0u : maybe_used->second; +} + +size_t SettingsStorageQuotaEnforcer::GetBytesInUse( + const std::vector& keys) { + size_t used = 0; + for (std::vector::const_iterator it = keys.begin(); + it != keys.end(); ++it) { + used += GetBytesInUse(*it); + } + return used; +} + +size_t SettingsStorageQuotaEnforcer::GetBytesInUse() { + // All ValueStore implementations rely on GetBytesInUse being + // implemented here. + return used_total_; +} + +ValueStore::ReadResult SettingsStorageQuotaEnforcer::Get( + const std::string& key) { + return delegate_->Get(key); +} + +ValueStore::ReadResult SettingsStorageQuotaEnforcer::Get( + const std::vector& keys) { + return delegate_->Get(keys); +} + +ValueStore::ReadResult SettingsStorageQuotaEnforcer::Get() { + return delegate_->Get(); +} + +ValueStore::WriteResult SettingsStorageQuotaEnforcer::Set( + WriteOptions options, const std::string& key, const Value& value) { + size_t new_used_total = used_total_; + std::map new_used_per_setting = used_per_setting_; + Allocate(key, value, &new_used_total, &new_used_per_setting); + + if (!(options & IGNORE_QUOTA)) { + if (new_used_total > limits_.quota_bytes) { + return QuotaExceededFor(QUOTA_BYTES); + } + if (new_used_per_setting[key] > limits_.quota_bytes_per_item) { + return QuotaExceededFor(QUOTA_BYTES_PER_ITEM); + } + if (new_used_per_setting.size() > limits_.max_items) { + return QuotaExceededFor(MAX_ITEMS); + } + } + + WriteResult result = delegate_->Set(options, key, value); + if (result->HasError()) { + return result.Pass(); + } + + used_total_ = new_used_total; + used_per_setting_.swap(new_used_per_setting); + return result.Pass(); +} + +ValueStore::WriteResult SettingsStorageQuotaEnforcer::Set( + WriteOptions options, const DictionaryValue& values) { + size_t new_used_total = used_total_; + std::map new_used_per_setting = used_per_setting_; + for (DictionaryValue::Iterator it(values); it.HasNext(); it.Advance()) { + Allocate(it.key(), it.value(), &new_used_total, &new_used_per_setting); + + if (!(options & IGNORE_QUOTA) && + new_used_per_setting[it.key()] > limits_.quota_bytes_per_item) { + return QuotaExceededFor(QUOTA_BYTES_PER_ITEM); + } + } + + if (!(options & IGNORE_QUOTA)) { + if (new_used_total > limits_.quota_bytes) { + return QuotaExceededFor(QUOTA_BYTES); + } + if (new_used_per_setting.size() > limits_.max_items) { + return QuotaExceededFor(MAX_ITEMS); + } + } + + WriteResult result = delegate_->Set(options, values); + if (result->HasError()) { + return result.Pass(); + } + + used_total_ = new_used_total; + used_per_setting_ = new_used_per_setting; + return result.Pass(); +} + +ValueStore::WriteResult SettingsStorageQuotaEnforcer::Remove( + const std::string& key) { + WriteResult result = delegate_->Remove(key); + if (result->HasError()) { + return result.Pass(); + } + Free(&used_total_, &used_per_setting_, key); + return result.Pass(); +} + +ValueStore::WriteResult SettingsStorageQuotaEnforcer::Remove( + const std::vector& keys) { + WriteResult result = delegate_->Remove(keys); + if (result->HasError()) { + return result.Pass(); + } + + for (std::vector::const_iterator it = keys.begin(); + it != keys.end(); ++it) { + Free(&used_total_, &used_per_setting_, *it); + } + return result.Pass(); +} + +ValueStore::WriteResult SettingsStorageQuotaEnforcer::Clear() { + WriteResult result = delegate_->Clear(); + if (result->HasError()) { + return result.Pass(); + } + + while (!used_per_setting_.empty()) { + Free(&used_total_, &used_per_setting_, used_per_setting_.begin()->first); + } + return result.Pass(); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/settings/settings_storage_quota_enforcer.h b/chrome/browser/extensions/settings/settings_storage_quota_enforcer.h new file mode 100644 index 0000000..2b71ff9 --- /dev/null +++ b/chrome/browser/extensions/settings/settings_storage_quota_enforcer.h @@ -0,0 +1,69 @@ +// 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_SETTINGS_SETTINGS_STORAGE_QUOTA_ENFORCER_H_ +#define CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTINGS_STORAGE_QUOTA_ENFORCER_H_ + +#include "base/compiler_specific.h" +#include "base/memory/weak_ptr.h" +#include "chrome/browser/value_store/value_store.h" + +namespace extensions { + +// Enforces total quota and a per-setting quota in bytes, and a maximum number +// of setting keys, for a delegate storage area. +class SettingsStorageQuotaEnforcer : public ValueStore { + public: + struct Limits { + // The total quota in bytes. + size_t quota_bytes; + + // The quota for each individual item in bytes. + size_t quota_bytes_per_item; + + // The maximum number of items allowed. + size_t max_items; + }; + + SettingsStorageQuotaEnforcer(const Limits& limits, ValueStore* delegate); + + virtual ~SettingsStorageQuotaEnforcer(); + + // ValueStore implementation. + virtual size_t GetBytesInUse(const std::string& key) OVERRIDE; + virtual size_t GetBytesInUse(const std::vector& keys) OVERRIDE; + virtual size_t GetBytesInUse() OVERRIDE; + virtual ReadResult Get(const std::string& key) OVERRIDE; + virtual ReadResult Get(const std::vector& keys) OVERRIDE; + virtual ReadResult Get() OVERRIDE; + virtual WriteResult Set( + WriteOptions options, + const std::string& key, + const Value& value) OVERRIDE; + virtual WriteResult Set( + WriteOptions options, const DictionaryValue& values) OVERRIDE; + virtual WriteResult Remove(const std::string& key) OVERRIDE; + virtual WriteResult Remove(const std::vector& keys) OVERRIDE; + virtual WriteResult Clear() OVERRIDE; + + private: + // Limits configuration. + const Limits limits_; + + // The delegate storage area. + scoped_ptr const delegate_; + + // Total bytes in used by |delegate_|. Includes both key lengths and + // JSON-encoded values. + size_t used_total_; + + // Map of item key to its size, including the key itself. + std::map used_per_setting_; + + DISALLOW_COPY_AND_ASSIGN(SettingsStorageQuotaEnforcer); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTINGS_STORAGE_QUOTA_ENFORCER_H_ diff --git a/chrome/browser/extensions/settings/settings_sync_processor.cc b/chrome/browser/extensions/settings/settings_sync_processor.cc new file mode 100644 index 0000000..18c4f07 --- /dev/null +++ b/chrome/browser/extensions/settings/settings_sync_processor.cc @@ -0,0 +1,110 @@ +// 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/settings/settings_namespace.h" +#include "chrome/browser/extensions/settings/settings_sync_processor.h" +#include "chrome/browser/extensions/settings/settings_sync_util.h" +#include "content/public/browser/browser_thread.h" +#include "sync/api/sync_change_processor.h" +#include "sync/api/sync_data.h" +#include "sync/protocol/extension_setting_specifics.pb.h" + +using content::BrowserThread; + +namespace extensions { + +SettingsSyncProcessor::SettingsSyncProcessor( + const std::string& extension_id, + syncer::ModelType type, + syncer::SyncChangeProcessor* sync_processor) + : extension_id_(extension_id), + type_(type), + sync_processor_(sync_processor), + initialized_(false) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + CHECK(type == syncer::EXTENSION_SETTINGS || type == syncer::APP_SETTINGS); + CHECK(sync_processor); +} + +SettingsSyncProcessor::~SettingsSyncProcessor() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); +} + +void SettingsSyncProcessor::Init(const DictionaryValue& initial_state) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + CHECK(!initialized_) << "Init called multiple times"; + + for (DictionaryValue::Iterator i(initial_state); i.HasNext(); i.Advance()) + synced_keys_.insert(i.key()); + + initialized_ = true; +} + +syncer::SyncError SettingsSyncProcessor::SendChanges( + const ValueStoreChangeList& changes) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + CHECK(initialized_) << "Init not called"; + + syncer::SyncChangeList sync_changes; + std::set added_keys; + std::set deleted_keys; + + for (ValueStoreChangeList::const_iterator i = changes.begin(); + i != changes.end(); ++i) { + const std::string& key = i->key(); + const Value* value = i->new_value(); + if (value) { + if (synced_keys_.count(key)) { + // New value, key is synced; send ACTION_UPDATE. + sync_changes.push_back(settings_sync_util::CreateUpdate( + extension_id_, key, *value, type_)); + } else { + // New value, key is not synced; send ACTION_ADD. + sync_changes.push_back(settings_sync_util::CreateAdd( + extension_id_, key, *value, type_)); + added_keys.insert(key); + } + } else { + if (synced_keys_.count(key)) { + // Clearing value, key is synced; send ACTION_DELETE. + sync_changes.push_back(settings_sync_util::CreateDelete( + extension_id_, key, type_)); + deleted_keys.insert(key); + } else { + LOG(WARNING) << "Deleted " << key << " but not in synced_keys_"; + } + } + } + + if (sync_changes.empty()) + return syncer::SyncError(); + + syncer::SyncError error = + sync_processor_->ProcessSyncChanges(FROM_HERE, sync_changes); + if (error.IsSet()) + return error; + + synced_keys_.insert(added_keys.begin(), added_keys.end()); + for (std::set::iterator i = deleted_keys.begin(); + i != deleted_keys.end(); ++i) { + synced_keys_.erase(*i); + } + + return syncer::SyncError(); +} + +void SettingsSyncProcessor::NotifyChanges(const ValueStoreChangeList& changes) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + CHECK(initialized_) << "Init not called"; + + for (ValueStoreChangeList::const_iterator i = changes.begin(); + i != changes.end(); ++i) { + if (i->new_value()) + synced_keys_.insert(i->key()); + else + synced_keys_.erase(i->key()); + } +} + +} // namespace extensions diff --git a/chrome/browser/extensions/settings/settings_sync_processor.h b/chrome/browser/extensions/settings/settings_sync_processor.h new file mode 100644 index 0000000..cd39815 --- /dev/null +++ b/chrome/browser/extensions/settings/settings_sync_processor.h @@ -0,0 +1,67 @@ +// 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_SETTINGS_SETTINGS_SYNC_PROCESSOR_H_ +#define CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTINGS_SYNC_PROCESSOR_H_ + +#include +#include + +#include "chrome/browser/value_store/value_store_change.h" +#include "sync/api/sync_error.h" + +namespace syncer { +class SyncChangeProcessor; +} // namespace syncer + +namespace extensions { + +// A wrapper for a SyncChangeProcessor that deals specifically with the syncing +// of a single extension's settings. Handles: +// - translating SettingChanges into calls into the Sync API. +// - deciding whether to ADD/REMOVE/SET depending on the current state of +// settings. +// - rate limiting (inherently per-extension, which is what we want). +class SettingsSyncProcessor { + public: + SettingsSyncProcessor(const std::string& extension_id, + syncer::ModelType type, + syncer::SyncChangeProcessor* sync_processor); + ~SettingsSyncProcessor(); + + // Initializes this with the initial state of sync. + void Init(const DictionaryValue& initial_state); + + // Sends |changes| to sync. + syncer::SyncError SendChanges(const ValueStoreChangeList& changes); + + // Informs this that |changes| have been receieved from sync. No action will + // be taken, but this must be notified for internal bookkeeping. + void NotifyChanges(const ValueStoreChangeList& changes); + + syncer::ModelType type() { return type_; } + + private: + // ID of the extension the changes are for. + const std::string extension_id_; + + // Sync model type. Either EXTENSION_SETTING or APP_SETTING. + const syncer::ModelType type_; + + // The sync processor used to send changes to sync. + syncer::SyncChangeProcessor* const sync_processor_; + + // Whether Init() has been called. + bool initialized_; + + // Keys of the settings that are currently being synced. Used to decide what + // kind of action (ADD, UPDATE, REMOVE) to send to sync. + std::set synced_keys_; + + DISALLOW_COPY_AND_ASSIGN(SettingsSyncProcessor); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTINGS_SYNC_PROCESSOR_H_ diff --git a/chrome/browser/extensions/settings/settings_sync_unittest.cc b/chrome/browser/extensions/settings/settings_sync_unittest.cc new file mode 100644 index 0000000..1386244 --- /dev/null +++ b/chrome/browser/extensions/settings/settings_sync_unittest.cc @@ -0,0 +1,1426 @@ +// 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 "testing/gtest/include/gtest/gtest.h" + +#include "base/bind.h" +#include "base/files/scoped_temp_dir.h" +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/settings/leveldb_settings_storage_factory.h" +#include "chrome/browser/extensions/settings/settings_frontend.h" +#include "chrome/browser/extensions/settings/settings_storage_factory.h" +#include "chrome/browser/extensions/settings/settings_sync_util.h" +#include "chrome/browser/extensions/settings/settings_test_util.h" +#include "chrome/browser/extensions/settings/syncable_settings_storage.h" +#include "chrome/browser/extensions/test_extension_service.h" +#include "chrome/browser/value_store/testing_value_store.h" +#include "content/public/test/test_browser_thread.h" +#include "sync/api/sync_change_processor.h" +#include "sync/api/sync_error_factory.h" +#include "sync/api/sync_error_factory_mock.h" + +using content::BrowserThread; + +namespace extensions { + +namespace util = settings_test_util; + +namespace { + +// To save typing ValueStore::DEFAULTS everywhere. +const ValueStore::WriteOptions DEFAULTS = ValueStore::DEFAULTS; + +// Gets the pretty-printed JSON for a value. +static std::string GetJson(const Value& value) { + std::string json; + base::JSONWriter::WriteWithOptions(&value, + base::JSONWriter::OPTIONS_PRETTY_PRINT, + &json); + return json; +} + +// Returns whether two Values are equal. +testing::AssertionResult ValuesEq( + const char* _1, const char* _2, + const Value* expected, + const Value* actual) { + if (expected == actual) { + return testing::AssertionSuccess(); + } + if (!expected && actual) { + return testing::AssertionFailure() << + "Expected NULL, actual: " << GetJson(*actual); + } + if (expected && !actual) { + return testing::AssertionFailure() << + "Expected: " << GetJson(*expected) << ", actual NULL"; + } + if (!expected->Equals(actual)) { + return testing::AssertionFailure() << + "Expected: " << GetJson(*expected) << ", actual: " << GetJson(*actual); + } + return testing::AssertionSuccess(); +} + +// Returns whether the result of a storage operation is an expected value. +// Logs when different. +testing::AssertionResult SettingsEq( + const char* _1, const char* _2, + const DictionaryValue& expected, + ValueStore::ReadResult actual) { + if (actual->HasError()) { + return testing::AssertionFailure() << + "Expected: " << GetJson(expected) << + ", actual has error: " << actual->error(); + } + return ValuesEq(_1, _2, &expected, actual->settings().get()); +} + +// SyncChangeProcessor which just records the changes made, accessed after +// being converted to the more useful SettingSyncData via changes(). +class MockSyncChangeProcessor : public syncer::SyncChangeProcessor { + public: + MockSyncChangeProcessor() : fail_all_requests_(false) {} + + // syncer::SyncChangeProcessor implementation. + virtual syncer::SyncError ProcessSyncChanges( + const tracked_objects::Location& from_here, + const syncer::SyncChangeList& change_list) OVERRIDE { + if (fail_all_requests_) { + return syncer::SyncError( + FROM_HERE, + "MockSyncChangeProcessor: configured to fail", + change_list[0].sync_data().GetDataType()); + } + for (syncer::SyncChangeList::const_iterator it = change_list.begin(); + it != change_list.end(); ++it) { + changes_.push_back(SettingSyncData(*it)); + } + return syncer::SyncError(); + } + + // Mock methods. + + const SettingSyncDataList& changes() { return changes_; } + + void ClearChanges() { + changes_.clear(); + } + + void SetFailAllRequests(bool fail_all_requests) { + fail_all_requests_ = fail_all_requests; + } + + // Returns the only change for a given extension setting. If there is not + // exactly 1 change for that key, a test assertion will fail. + SettingSyncData GetOnlyChange( + const std::string& extension_id, const std::string& key) { + SettingSyncDataList matching_changes; + for (SettingSyncDataList::iterator it = changes_.begin(); + it != changes_.end(); ++it) { + if (it->extension_id() == extension_id && it->key() == key) { + matching_changes.push_back(*it); + } + } + if (matching_changes.empty()) { + ADD_FAILURE() << "No matching changes for " << extension_id << "/" << + key << " (out of " << changes_.size() << ")"; + return SettingSyncData( + syncer::SyncChange::ACTION_INVALID, "", "", + scoped_ptr(new DictionaryValue())); + } + if (matching_changes.size() != 1u) { + ADD_FAILURE() << matching_changes.size() << " matching changes for " << + extension_id << "/" << key << " (out of " << changes_.size() << ")"; + } + return matching_changes[0]; + } + + private: + SettingSyncDataList changes_; + bool fail_all_requests_; +}; + +class SyncChangeProcessorDelegate : public syncer::SyncChangeProcessor { + public: + explicit SyncChangeProcessorDelegate(syncer::SyncChangeProcessor* recipient) + : recipient_(recipient) { + DCHECK(recipient_); + } + virtual ~SyncChangeProcessorDelegate() {} + + // syncer::SyncChangeProcessor implementation. + virtual syncer::SyncError ProcessSyncChanges( + const tracked_objects::Location& from_here, + const syncer::SyncChangeList& change_list) OVERRIDE { + return recipient_->ProcessSyncChanges(from_here, change_list); + } + + private: + // The recipient of all sync changes. + syncer::SyncChangeProcessor* recipient_; + + DISALLOW_COPY_AND_ASSIGN(SyncChangeProcessorDelegate); +}; + +// SettingsStorageFactory which always returns TestingValueStore objects, +// and allows individually created objects to be returned. +class TestingValueStoreFactory : public SettingsStorageFactory { + public: + TestingValueStore* GetExisting(const std::string& extension_id) { + DCHECK(created_.count(extension_id)); + return created_[extension_id]; + } + + // SettingsStorageFactory implementation. + virtual ValueStore* Create(const FilePath& base_path, + const std::string& extension_id) OVERRIDE { + TestingValueStore* new_storage = new TestingValueStore(); + DCHECK(!created_.count(extension_id)); + created_[extension_id] = new_storage; + return new_storage; + } + + private: + // SettingsStorageFactory is refcounted. + virtual ~TestingValueStoreFactory() {} + + // None of these storage areas are owned by this factory, so care must be + // taken when calling GetExisting. + std::map created_; +}; + +} // namespace + +class ExtensionSettingsSyncTest : public testing::Test { + public: + ExtensionSettingsSyncTest() + : ui_thread_(BrowserThread::UI, MessageLoop::current()), + file_thread_(BrowserThread::FILE, MessageLoop::current()), + storage_factory_(new util::ScopedSettingsStorageFactory()), + sync_processor_(new MockSyncChangeProcessor), + sync_processor_delegate_(new SyncChangeProcessorDelegate( + sync_processor_.get())) {} + + virtual void SetUp() OVERRIDE { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + profile_.reset(new util::MockProfile(temp_dir_.path())); + storage_factory_->Reset(new LeveldbSettingsStorageFactory()); + frontend_.reset( + SettingsFrontend::Create(storage_factory_.get(), profile_.get())); + } + + virtual void TearDown() OVERRIDE { + frontend_.reset(); + profile_.reset(); + // Execute any pending deletion tasks. + message_loop_.RunUntilIdle(); + } + + protected: + // Adds a record of an extension or app to the extension service, then returns + // its storage area. + ValueStore* AddExtensionAndGetStorage( + const std::string& id, Extension::Type type) { + ExtensionServiceInterface* esi = + extensions::ExtensionSystem::Get(profile_.get())->extension_service(); + static_cast(esi)-> + AddExtensionWithId(id, type); + return util::GetStorage(id, frontend_.get()); + } + + // Gets the syncer::SyncableService for the given sync type. + syncer::SyncableService* GetSyncableService(syncer::ModelType model_type) { + MessageLoop::current()->RunUntilIdle(); + return frontend_->GetBackendForSync(model_type); + } + + // Gets all the sync data from the SyncableService for a sync type as a map + // from extension id to its sync data. + std::map GetAllSyncData( + syncer::ModelType model_type) { + syncer::SyncDataList as_list = + GetSyncableService(model_type)->GetAllSyncData(model_type); + std::map as_map; + for (syncer::SyncDataList::iterator it = as_list.begin(); + it != as_list.end(); ++it) { + SettingSyncData sync_data(*it); + as_map[sync_data.extension_id()].push_back(sync_data); + } + return as_map; + } + + // Need these so that the DCHECKs for running on FILE or UI threads pass. + MessageLoop message_loop_; + content::TestBrowserThread ui_thread_; + content::TestBrowserThread file_thread_; + + base::ScopedTempDir temp_dir_; + scoped_ptr profile_; + scoped_ptr frontend_; + scoped_refptr storage_factory_; + scoped_ptr sync_processor_; + scoped_ptr sync_processor_delegate_; +}; + +// Get a semblance of coverage for both EXTENSION_SETTINGS and APP_SETTINGS +// sync by roughly alternative which one to test. + +TEST_F(ExtensionSettingsSyncTest, NoDataDoesNotInvokeSync) { + syncer::ModelType model_type = syncer::EXTENSION_SETTINGS; + Extension::Type type = Extension::TYPE_EXTENSION; + + EXPECT_EQ(0u, GetAllSyncData(model_type).size()); + + // Have one extension created before sync is set up, the other created after. + AddExtensionAndGetStorage("s1", type); + EXPECT_EQ(0u, GetAllSyncData(model_type).size()); + + GetSyncableService(model_type)->MergeDataAndStartSyncing( + model_type, + syncer::SyncDataList(), + sync_processor_delegate_.PassAs(), + scoped_ptr(new syncer::SyncErrorFactoryMock())); + + AddExtensionAndGetStorage("s2", type); + EXPECT_EQ(0u, GetAllSyncData(model_type).size()); + + GetSyncableService(model_type)->StopSyncing(model_type); + + EXPECT_EQ(0u, sync_processor_->changes().size()); + EXPECT_EQ(0u, GetAllSyncData(model_type).size()); +} + +TEST_F(ExtensionSettingsSyncTest, InSyncDataDoesNotInvokeSync) { + syncer::ModelType model_type = syncer::APP_SETTINGS; + Extension::Type type = Extension::TYPE_LEGACY_PACKAGED_APP; + + StringValue value1("fooValue"); + ListValue value2; + value2.Append(StringValue::CreateStringValue("barValue")); + + ValueStore* storage1 = AddExtensionAndGetStorage("s1", type); + ValueStore* storage2 = AddExtensionAndGetStorage("s2", type); + + storage1->Set(DEFAULTS, "foo", value1); + storage2->Set(DEFAULTS, "bar", value2); + + std::map all_sync_data = + GetAllSyncData(model_type); + EXPECT_EQ(2u, all_sync_data.size()); + EXPECT_EQ(1u, all_sync_data["s1"].size()); + EXPECT_PRED_FORMAT2(ValuesEq, &value1, &all_sync_data["s1"][0].value()); + EXPECT_EQ(1u, all_sync_data["s2"].size()); + EXPECT_PRED_FORMAT2(ValuesEq, &value2, &all_sync_data["s2"][0].value()); + + syncer::SyncDataList sync_data; + sync_data.push_back(settings_sync_util::CreateData( + "s1", "foo", value1, model_type)); + sync_data.push_back(settings_sync_util::CreateData( + "s2", "bar", value2, model_type)); + + GetSyncableService(model_type)->MergeDataAndStartSyncing( + model_type, sync_data, + sync_processor_delegate_.PassAs(), + scoped_ptr(new syncer::SyncErrorFactoryMock())); + + // Already in sync, so no changes. + EXPECT_EQ(0u, sync_processor_->changes().size()); + + // Regression test: not-changing the synced value shouldn't result in a sync + // change, and changing the synced value should result in an update. + storage1->Set(DEFAULTS, "foo", value1); + EXPECT_EQ(0u, sync_processor_->changes().size()); + + storage1->Set(DEFAULTS, "foo", value2); + EXPECT_EQ(1u, sync_processor_->changes().size()); + SettingSyncData change = sync_processor_->GetOnlyChange("s1", "foo"); + EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change.change_type()); + EXPECT_TRUE(value2.Equals(&change.value())); + + GetSyncableService(model_type)->StopSyncing(model_type); +} + +TEST_F(ExtensionSettingsSyncTest, LocalDataWithNoSyncDataIsPushedToSync) { + syncer::ModelType model_type = syncer::EXTENSION_SETTINGS; + Extension::Type type = Extension::TYPE_EXTENSION; + + StringValue value1("fooValue"); + ListValue value2; + value2.Append(StringValue::CreateStringValue("barValue")); + + ValueStore* storage1 = AddExtensionAndGetStorage("s1", type); + ValueStore* storage2 = AddExtensionAndGetStorage("s2", type); + + storage1->Set(DEFAULTS, "foo", value1); + storage2->Set(DEFAULTS, "bar", value2); + + GetSyncableService(model_type)->MergeDataAndStartSyncing( + model_type, + syncer::SyncDataList(), + sync_processor_delegate_.PassAs(), + scoped_ptr(new syncer::SyncErrorFactoryMock())); + + // All settings should have been pushed to sync. + EXPECT_EQ(2u, sync_processor_->changes().size()); + SettingSyncData change = sync_processor_->GetOnlyChange("s1", "foo"); + EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change.change_type()); + EXPECT_TRUE(value1.Equals(&change.value())); + change = sync_processor_->GetOnlyChange("s2", "bar"); + EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change.change_type()); + EXPECT_TRUE(value2.Equals(&change.value())); + + GetSyncableService(model_type)->StopSyncing(model_type); +} + +TEST_F(ExtensionSettingsSyncTest, AnySyncDataOverwritesLocalData) { + syncer::ModelType model_type = syncer::APP_SETTINGS; + Extension::Type type = Extension::TYPE_LEGACY_PACKAGED_APP; + + StringValue value1("fooValue"); + ListValue value2; + value2.Append(StringValue::CreateStringValue("barValue")); + + // Maintain dictionaries mirrored to the expected values of the settings in + // each storage area. + DictionaryValue expected1, expected2; + + // Pre-populate one of the storage areas. + ValueStore* storage1 = AddExtensionAndGetStorage("s1", type); + storage1->Set(DEFAULTS, "overwriteMe", value1); + + syncer::SyncDataList sync_data; + sync_data.push_back(settings_sync_util::CreateData( + "s1", "foo", value1, model_type)); + sync_data.push_back(settings_sync_util::CreateData( + "s2", "bar", value2, model_type)); + GetSyncableService(model_type)->MergeDataAndStartSyncing( + model_type, sync_data, + sync_processor_delegate_.PassAs(), + scoped_ptr(new syncer::SyncErrorFactoryMock())); + expected1.Set("foo", value1.DeepCopy()); + expected2.Set("bar", value2.DeepCopy()); + + ValueStore* storage2 = AddExtensionAndGetStorage("s2", type); + + // All changes should be local, so no sync changes. + EXPECT_EQ(0u, sync_processor_->changes().size()); + + // Sync settings should have been pushed to local settings. + EXPECT_PRED_FORMAT2(SettingsEq, expected1, storage1->Get()); + EXPECT_PRED_FORMAT2(SettingsEq, expected2, storage2->Get()); + + GetSyncableService(model_type)->StopSyncing(model_type); +} + +TEST_F(ExtensionSettingsSyncTest, ProcessSyncChanges) { + syncer::ModelType model_type = syncer::EXTENSION_SETTINGS; + Extension::Type type = Extension::TYPE_EXTENSION; + + StringValue value1("fooValue"); + ListValue value2; + value2.Append(StringValue::CreateStringValue("barValue")); + + // Maintain dictionaries mirrored to the expected values of the settings in + // each storage area. + DictionaryValue expected1, expected2; + + // Make storage1 initialised from local data, storage2 initialised from sync. + ValueStore* storage1 = AddExtensionAndGetStorage("s1", type); + ValueStore* storage2 = AddExtensionAndGetStorage("s2", type); + + storage1->Set(DEFAULTS, "foo", value1); + expected1.Set("foo", value1.DeepCopy()); + + syncer::SyncDataList sync_data; + sync_data.push_back(settings_sync_util::CreateData( + "s2", "bar", value2, model_type)); + + GetSyncableService(model_type)->MergeDataAndStartSyncing( + model_type, sync_data, + sync_processor_delegate_.PassAs(), + scoped_ptr(new syncer::SyncErrorFactoryMock())); + expected2.Set("bar", value2.DeepCopy()); + + // Make sync add some settings. + syncer::SyncChangeList change_list; + change_list.push_back(settings_sync_util::CreateAdd( + "s1", "bar", value2, model_type)); + change_list.push_back(settings_sync_util::CreateAdd( + "s2", "foo", value1, model_type)); + GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); + expected1.Set("bar", value2.DeepCopy()); + expected2.Set("foo", value1.DeepCopy()); + + EXPECT_PRED_FORMAT2(SettingsEq, expected1, storage1->Get()); + EXPECT_PRED_FORMAT2(SettingsEq, expected2, storage2->Get()); + + // Make sync update some settings, storage1 the new setting, storage2 the + // initial setting. + change_list.clear(); + change_list.push_back(settings_sync_util::CreateUpdate( + "s1", "bar", value2, model_type)); + change_list.push_back(settings_sync_util::CreateUpdate( + "s2", "bar", value1, model_type)); + GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); + expected1.Set("bar", value2.DeepCopy()); + expected2.Set("bar", value1.DeepCopy()); + + EXPECT_PRED_FORMAT2(SettingsEq, expected1, storage1->Get()); + EXPECT_PRED_FORMAT2(SettingsEq, expected2, storage2->Get()); + + // Make sync remove some settings, storage1 the initial setting, storage2 the + // new setting. + change_list.clear(); + change_list.push_back(settings_sync_util::CreateDelete( + "s1", "foo", model_type)); + change_list.push_back(settings_sync_util::CreateDelete( + "s2", "foo", model_type)); + GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); + expected1.Remove("foo", NULL); + expected2.Remove("foo", NULL); + + EXPECT_PRED_FORMAT2(SettingsEq, expected1, storage1->Get()); + EXPECT_PRED_FORMAT2(SettingsEq, expected2, storage2->Get()); + + GetSyncableService(model_type)->StopSyncing(model_type); +} + +TEST_F(ExtensionSettingsSyncTest, PushToSync) { + syncer::ModelType model_type = syncer::APP_SETTINGS; + Extension::Type type = Extension::TYPE_LEGACY_PACKAGED_APP; + + StringValue value1("fooValue"); + ListValue value2; + value2.Append(StringValue::CreateStringValue("barValue")); + + // Make storage1/2 initialised from local data, storage3/4 initialised from + // sync. + ValueStore* storage1 = AddExtensionAndGetStorage("s1", type); + ValueStore* storage2 = AddExtensionAndGetStorage("s2", type); + ValueStore* storage3 = AddExtensionAndGetStorage("s3", type); + ValueStore* storage4 = AddExtensionAndGetStorage("s4", type); + + storage1->Set(DEFAULTS, "foo", value1); + storage2->Set(DEFAULTS, "foo", value1); + + syncer::SyncDataList sync_data; + sync_data.push_back(settings_sync_util::CreateData( + "s3", "bar", value2, model_type)); + sync_data.push_back(settings_sync_util::CreateData( + "s4", "bar", value2, model_type)); + + GetSyncableService(model_type)->MergeDataAndStartSyncing( + model_type, sync_data, + sync_processor_delegate_.PassAs(), + scoped_ptr(new syncer::SyncErrorFactoryMock())); + + // Add something locally. + storage1->Set(DEFAULTS, "bar", value2); + storage2->Set(DEFAULTS, "bar", value2); + storage3->Set(DEFAULTS, "foo", value1); + storage4->Set(DEFAULTS, "foo", value1); + + SettingSyncData change = sync_processor_->GetOnlyChange("s1", "bar"); + EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change.change_type()); + EXPECT_TRUE(value2.Equals(&change.value())); + sync_processor_->GetOnlyChange("s2", "bar"); + EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change.change_type()); + EXPECT_TRUE(value2.Equals(&change.value())); + change = sync_processor_->GetOnlyChange("s3", "foo"); + EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change.change_type()); + EXPECT_TRUE(value1.Equals(&change.value())); + change = sync_processor_->GetOnlyChange("s4", "foo"); + EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change.change_type()); + EXPECT_TRUE(value1.Equals(&change.value())); + + // Change something locally, storage1/3 the new setting and storage2/4 the + // initial setting, for all combinations of local vs sync intialisation and + // new vs initial. + sync_processor_->ClearChanges(); + storage1->Set(DEFAULTS, "bar", value1); + storage2->Set(DEFAULTS, "foo", value2); + storage3->Set(DEFAULTS, "bar", value1); + storage4->Set(DEFAULTS, "foo", value2); + + change = sync_processor_->GetOnlyChange("s1", "bar"); + EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change.change_type()); + EXPECT_TRUE(value1.Equals(&change.value())); + change = sync_processor_->GetOnlyChange("s2", "foo"); + EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change.change_type()); + EXPECT_TRUE(value2.Equals(&change.value())); + change = sync_processor_->GetOnlyChange("s3", "bar"); + EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change.change_type()); + EXPECT_TRUE(value1.Equals(&change.value())); + change = sync_processor_->GetOnlyChange("s4", "foo"); + EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change.change_type()); + EXPECT_TRUE(value2.Equals(&change.value())); + + // Remove something locally, storage1/3 the new setting and storage2/4 the + // initial setting, for all combinations of local vs sync intialisation and + // new vs initial. + sync_processor_->ClearChanges(); + storage1->Remove("foo"); + storage2->Remove("bar"); + storage3->Remove("foo"); + storage4->Remove("bar"); + + EXPECT_EQ( + syncer::SyncChange::ACTION_DELETE, + sync_processor_->GetOnlyChange("s1", "foo").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_DELETE, + sync_processor_->GetOnlyChange("s2", "bar").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_DELETE, + sync_processor_->GetOnlyChange("s3", "foo").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_DELETE, + sync_processor_->GetOnlyChange("s4", "bar").change_type()); + + // Remove some nonexistent settings. + sync_processor_->ClearChanges(); + storage1->Remove("foo"); + storage2->Remove("bar"); + storage3->Remove("foo"); + storage4->Remove("bar"); + + EXPECT_EQ(0u, sync_processor_->changes().size()); + + // Clear the rest of the settings. Add the removed ones back first so that + // more than one setting is cleared. + storage1->Set(DEFAULTS, "foo", value1); + storage2->Set(DEFAULTS, "bar", value2); + storage3->Set(DEFAULTS, "foo", value1); + storage4->Set(DEFAULTS, "bar", value2); + + sync_processor_->ClearChanges(); + storage1->Clear(); + storage2->Clear(); + storage3->Clear(); + storage4->Clear(); + + EXPECT_EQ( + syncer::SyncChange::ACTION_DELETE, + sync_processor_->GetOnlyChange("s1", "foo").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_DELETE, + sync_processor_->GetOnlyChange("s1", "bar").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_DELETE, + sync_processor_->GetOnlyChange("s2", "foo").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_DELETE, + sync_processor_->GetOnlyChange("s2", "bar").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_DELETE, + sync_processor_->GetOnlyChange("s3", "foo").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_DELETE, + sync_processor_->GetOnlyChange("s3", "bar").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_DELETE, + sync_processor_->GetOnlyChange("s4", "foo").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_DELETE, + sync_processor_->GetOnlyChange("s4", "bar").change_type()); + + GetSyncableService(model_type)->StopSyncing(model_type); +} + +TEST_F(ExtensionSettingsSyncTest, ExtensionAndAppSettingsSyncSeparately) { + StringValue value1("fooValue"); + ListValue value2; + value2.Append(StringValue::CreateStringValue("barValue")); + + // storage1 is an extension, storage2 is an app. + ValueStore* storage1 = AddExtensionAndGetStorage( + "s1", Extension::TYPE_EXTENSION); + ValueStore* storage2 = AddExtensionAndGetStorage( + "s2", Extension::TYPE_LEGACY_PACKAGED_APP); + + storage1->Set(DEFAULTS, "foo", value1); + storage2->Set(DEFAULTS, "bar", value2); + + std::map extension_sync_data = + GetAllSyncData(syncer::EXTENSION_SETTINGS); + EXPECT_EQ(1u, extension_sync_data.size()); + EXPECT_EQ(1u, extension_sync_data["s1"].size()); + EXPECT_PRED_FORMAT2(ValuesEq, &value1, &extension_sync_data["s1"][0].value()); + + std::map app_sync_data = + GetAllSyncData(syncer::APP_SETTINGS); + EXPECT_EQ(1u, app_sync_data.size()); + EXPECT_EQ(1u, app_sync_data["s2"].size()); + EXPECT_PRED_FORMAT2(ValuesEq, &value2, &app_sync_data["s2"][0].value()); + + // Stop each separately, there should be no changes either time. + syncer::SyncDataList sync_data; + sync_data.push_back(settings_sync_util::CreateData( + "s1", "foo", value1, syncer::EXTENSION_SETTINGS)); + + GetSyncableService(syncer::EXTENSION_SETTINGS)->MergeDataAndStartSyncing( + syncer::EXTENSION_SETTINGS, + sync_data, + sync_processor_delegate_.PassAs(), + scoped_ptr(new syncer::SyncErrorFactoryMock())); + GetSyncableService(syncer::EXTENSION_SETTINGS)-> + StopSyncing(syncer::EXTENSION_SETTINGS); + EXPECT_EQ(0u, sync_processor_->changes().size()); + + sync_data.clear(); + sync_data.push_back(settings_sync_util::CreateData( + "s2", "bar", value2, syncer::APP_SETTINGS)); + + scoped_ptr app_settings_delegate_( + new SyncChangeProcessorDelegate(sync_processor_.get())); + GetSyncableService(syncer::APP_SETTINGS)->MergeDataAndStartSyncing( + syncer::APP_SETTINGS, + sync_data, + app_settings_delegate_.PassAs(), + scoped_ptr(new syncer::SyncErrorFactoryMock())); + GetSyncableService(syncer::APP_SETTINGS)-> + StopSyncing(syncer::APP_SETTINGS); + EXPECT_EQ(0u, sync_processor_->changes().size()); +} + +TEST_F(ExtensionSettingsSyncTest, FailingStartSyncingDisablesSync) { + syncer::ModelType model_type = syncer::EXTENSION_SETTINGS; + Extension::Type type = Extension::TYPE_EXTENSION; + + StringValue fooValue("fooValue"); + StringValue barValue("barValue"); + + // There is a bit of a convoluted method to get storage areas that can fail; + // hand out TestingValueStore object then toggle them failing/succeeding + // as necessary. + TestingValueStoreFactory* testing_factory = new TestingValueStoreFactory(); + storage_factory_->Reset(testing_factory); + + ValueStore* good = AddExtensionAndGetStorage("good", type); + ValueStore* bad = AddExtensionAndGetStorage("bad", type); + + // Make bad fail for incoming sync changes. + testing_factory->GetExisting("bad")->SetFailAllRequests(true); + { + syncer::SyncDataList sync_data; + sync_data.push_back(settings_sync_util::CreateData( + "good", "foo", fooValue, model_type)); + sync_data.push_back(settings_sync_util::CreateData( + "bad", "foo", fooValue, model_type)); + GetSyncableService(model_type)->MergeDataAndStartSyncing( + model_type, + sync_data, + sync_processor_delegate_.PassAs(), + scoped_ptr( + new syncer::SyncErrorFactoryMock())); + } + testing_factory->GetExisting("bad")->SetFailAllRequests(false); + + { + DictionaryValue dict; + dict.Set("foo", fooValue.DeepCopy()); + EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); + } + { + DictionaryValue dict; + EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); + } + + // Changes made to good should be sent to sync, changes from bad shouldn't. + sync_processor_->ClearChanges(); + good->Set(DEFAULTS, "bar", barValue); + bad->Set(DEFAULTS, "bar", barValue); + + EXPECT_EQ( + syncer::SyncChange::ACTION_ADD, + sync_processor_->GetOnlyChange("good", "bar").change_type()); + EXPECT_EQ(1u, sync_processor_->changes().size()); + + { + DictionaryValue dict; + dict.Set("foo", fooValue.DeepCopy()); + dict.Set("bar", barValue.DeepCopy()); + EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); + } + { + DictionaryValue dict; + dict.Set("bar", barValue.DeepCopy()); + EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); + } + + // Changes received from sync should go to good but not bad (even when it's + // not failing). + { + syncer::SyncChangeList change_list; + change_list.push_back(settings_sync_util::CreateUpdate( + "good", "foo", barValue, model_type)); + // (Sending UPDATE here even though it's adding, since that's what the state + // of sync is. In any case, it won't work.) + change_list.push_back(settings_sync_util::CreateUpdate( + "bad", "foo", barValue, model_type)); + GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); + } + + { + DictionaryValue dict; + dict.Set("foo", barValue.DeepCopy()); + dict.Set("bar", barValue.DeepCopy()); + EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); + } + { + DictionaryValue dict; + dict.Set("bar", barValue.DeepCopy()); + EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); + } + + // Changes made to bad still shouldn't go to sync, even though it didn't fail + // last time. + sync_processor_->ClearChanges(); + good->Set(DEFAULTS, "bar", fooValue); + bad->Set(DEFAULTS, "bar", fooValue); + + EXPECT_EQ( + syncer::SyncChange::ACTION_UPDATE, + sync_processor_->GetOnlyChange("good", "bar").change_type()); + EXPECT_EQ(1u, sync_processor_->changes().size()); + + { + DictionaryValue dict; + dict.Set("foo", barValue.DeepCopy()); + dict.Set("bar", fooValue.DeepCopy()); + EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); + } + { + DictionaryValue dict; + dict.Set("bar", fooValue.DeepCopy()); + EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); + } + + // Failing ProcessSyncChanges shouldn't go to the storage. + testing_factory->GetExisting("bad")->SetFailAllRequests(true); + { + syncer::SyncChangeList change_list; + change_list.push_back(settings_sync_util::CreateUpdate( + "good", "foo", fooValue, model_type)); + // (Ditto.) + change_list.push_back(settings_sync_util::CreateUpdate( + "bad", "foo", fooValue, model_type)); + GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); + } + testing_factory->GetExisting("bad")->SetFailAllRequests(false); + + { + DictionaryValue dict; + dict.Set("foo", fooValue.DeepCopy()); + dict.Set("bar", fooValue.DeepCopy()); + EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); + } + { + DictionaryValue dict; + dict.Set("bar", fooValue.DeepCopy()); + EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); + } + + // Restarting sync should make bad start syncing again. + sync_processor_->ClearChanges(); + GetSyncableService(model_type)->StopSyncing(model_type); + sync_processor_delegate_.reset(new SyncChangeProcessorDelegate( + sync_processor_.get())); + GetSyncableService(model_type)->MergeDataAndStartSyncing( + model_type, + syncer::SyncDataList(), + sync_processor_delegate_.PassAs(), + scoped_ptr(new syncer::SyncErrorFactoryMock())); + + // Local settings will have been pushed to sync, since it's empty (in this + // test; presumably it wouldn't be live, since we've been getting changes). + EXPECT_EQ( + syncer::SyncChange::ACTION_ADD, + sync_processor_->GetOnlyChange("good", "foo").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_ADD, + sync_processor_->GetOnlyChange("good", "bar").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_ADD, + sync_processor_->GetOnlyChange("bad", "bar").change_type()); + EXPECT_EQ(3u, sync_processor_->changes().size()); + + // Live local changes now get pushed, too. + sync_processor_->ClearChanges(); + good->Set(DEFAULTS, "bar", barValue); + bad->Set(DEFAULTS, "bar", barValue); + + EXPECT_EQ( + syncer::SyncChange::ACTION_UPDATE, + sync_processor_->GetOnlyChange("good", "bar").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_UPDATE, + sync_processor_->GetOnlyChange("bad", "bar").change_type()); + EXPECT_EQ(2u, sync_processor_->changes().size()); + + // And ProcessSyncChanges work, too. + { + syncer::SyncChangeList change_list; + change_list.push_back(settings_sync_util::CreateUpdate( + "good", "bar", fooValue, model_type)); + change_list.push_back(settings_sync_util::CreateUpdate( + "bad", "bar", fooValue, model_type)); + GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); + } + + { + DictionaryValue dict; + dict.Set("foo", fooValue.DeepCopy()); + dict.Set("bar", fooValue.DeepCopy()); + EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); + } + { + DictionaryValue dict; + dict.Set("bar", fooValue.DeepCopy()); + EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); + } +} + +TEST_F(ExtensionSettingsSyncTest, FailingProcessChangesDisablesSync) { + // The test above tests a failing ProcessSyncChanges too, but here test with + // an initially passing MergeDataAndStartSyncing. + syncer::ModelType model_type = syncer::APP_SETTINGS; + Extension::Type type = Extension::TYPE_LEGACY_PACKAGED_APP; + + StringValue fooValue("fooValue"); + StringValue barValue("barValue"); + + TestingValueStoreFactory* testing_factory = new TestingValueStoreFactory(); + storage_factory_->Reset(testing_factory); + + ValueStore* good = AddExtensionAndGetStorage("good", type); + ValueStore* bad = AddExtensionAndGetStorage("bad", type); + + // Unlike before, initially succeeding MergeDataAndStartSyncing. + { + syncer::SyncDataList sync_data; + sync_data.push_back(settings_sync_util::CreateData( + "good", "foo", fooValue, model_type)); + sync_data.push_back(settings_sync_util::CreateData( + "bad", "foo", fooValue, model_type)); + GetSyncableService(model_type)->MergeDataAndStartSyncing( + model_type, + sync_data, + sync_processor_delegate_.PassAs(), + scoped_ptr( + new syncer::SyncErrorFactoryMock())); + } + + EXPECT_EQ(0u, sync_processor_->changes().size()); + + { + DictionaryValue dict; + dict.Set("foo", fooValue.DeepCopy()); + EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); + } + { + DictionaryValue dict; + dict.Set("foo", fooValue.DeepCopy()); + EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); + } + + // Now fail ProcessSyncChanges for bad. + testing_factory->GetExisting("bad")->SetFailAllRequests(true); + { + syncer::SyncChangeList change_list; + change_list.push_back(settings_sync_util::CreateAdd( + "good", "bar", barValue, model_type)); + change_list.push_back(settings_sync_util::CreateAdd( + "bad", "bar", barValue, model_type)); + GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); + } + testing_factory->GetExisting("bad")->SetFailAllRequests(false); + + { + DictionaryValue dict; + dict.Set("foo", fooValue.DeepCopy()); + dict.Set("bar", barValue.DeepCopy()); + EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); + } + { + DictionaryValue dict; + dict.Set("foo", fooValue.DeepCopy()); + EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); + } + + // No more changes sent to sync for bad. + sync_processor_->ClearChanges(); + good->Set(DEFAULTS, "foo", barValue); + bad->Set(DEFAULTS, "foo", barValue); + + EXPECT_EQ( + syncer::SyncChange::ACTION_UPDATE, + sync_processor_->GetOnlyChange("good", "foo").change_type()); + EXPECT_EQ(1u, sync_processor_->changes().size()); + + // No more changes received from sync should go to bad. + { + syncer::SyncChangeList change_list; + change_list.push_back(settings_sync_util::CreateAdd( + "good", "foo", fooValue, model_type)); + change_list.push_back(settings_sync_util::CreateAdd( + "bad", "foo", fooValue, model_type)); + GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); + } + + { + DictionaryValue dict; + dict.Set("foo", fooValue.DeepCopy()); + dict.Set("bar", barValue.DeepCopy()); + EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); + } + { + DictionaryValue dict; + dict.Set("foo", barValue.DeepCopy()); + EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); + } +} + +TEST_F(ExtensionSettingsSyncTest, FailingGetAllSyncDataDoesntStopSync) { + syncer::ModelType model_type = syncer::EXTENSION_SETTINGS; + Extension::Type type = Extension::TYPE_EXTENSION; + + StringValue fooValue("fooValue"); + StringValue barValue("barValue"); + + TestingValueStoreFactory* testing_factory = new TestingValueStoreFactory(); + storage_factory_->Reset(testing_factory); + + ValueStore* good = AddExtensionAndGetStorage("good", type); + ValueStore* bad = AddExtensionAndGetStorage("bad", type); + + good->Set(DEFAULTS, "foo", fooValue); + bad->Set(DEFAULTS, "foo", fooValue); + + // Even though bad will fail to get all sync data, sync data should still + // include that from good. + testing_factory->GetExisting("bad")->SetFailAllRequests(true); + { + syncer::SyncDataList all_sync_data = + GetSyncableService(model_type)->GetAllSyncData(model_type); + EXPECT_EQ(1u, all_sync_data.size()); + EXPECT_EQ("good/foo", all_sync_data[0].GetTag()); + } + testing_factory->GetExisting("bad")->SetFailAllRequests(false); + + // Sync shouldn't be disabled for good (nor bad -- but this is unimportant). + GetSyncableService(model_type)->MergeDataAndStartSyncing( + model_type, + syncer::SyncDataList(), + sync_processor_delegate_.PassAs(), + scoped_ptr(new syncer::SyncErrorFactoryMock())); + + EXPECT_EQ( + syncer::SyncChange::ACTION_ADD, + sync_processor_->GetOnlyChange("good", "foo").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_ADD, + sync_processor_->GetOnlyChange("bad", "foo").change_type()); + EXPECT_EQ(2u, sync_processor_->changes().size()); + + sync_processor_->ClearChanges(); + good->Set(DEFAULTS, "bar", barValue); + bad->Set(DEFAULTS, "bar", barValue); + + EXPECT_EQ( + syncer::SyncChange::ACTION_ADD, + sync_processor_->GetOnlyChange("good", "bar").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_ADD, + sync_processor_->GetOnlyChange("bad", "bar").change_type()); + EXPECT_EQ(2u, sync_processor_->changes().size()); +} + +TEST_F(ExtensionSettingsSyncTest, FailureToReadChangesToPushDisablesSync) { + syncer::ModelType model_type = syncer::APP_SETTINGS; + Extension::Type type = Extension::TYPE_LEGACY_PACKAGED_APP; + + StringValue fooValue("fooValue"); + StringValue barValue("barValue"); + + TestingValueStoreFactory* testing_factory = new TestingValueStoreFactory(); + storage_factory_->Reset(testing_factory); + + ValueStore* good = AddExtensionAndGetStorage("good", type); + ValueStore* bad = AddExtensionAndGetStorage("bad", type); + + good->Set(DEFAULTS, "foo", fooValue); + bad->Set(DEFAULTS, "foo", fooValue); + + // good will successfully push foo:fooValue to sync, but bad will fail to + // get them so won't. + testing_factory->GetExisting("bad")->SetFailAllRequests(true); + GetSyncableService(model_type)->MergeDataAndStartSyncing( + model_type, + syncer::SyncDataList(), + sync_processor_delegate_.PassAs(), + scoped_ptr(new syncer::SyncErrorFactoryMock())); + testing_factory->GetExisting("bad")->SetFailAllRequests(false); + + EXPECT_EQ( + syncer::SyncChange::ACTION_ADD, + sync_processor_->GetOnlyChange("good", "foo").change_type()); + EXPECT_EQ(1u, sync_processor_->changes().size()); + + // bad should now be disabled for sync. + sync_processor_->ClearChanges(); + good->Set(DEFAULTS, "bar", barValue); + bad->Set(DEFAULTS, "bar", barValue); + + EXPECT_EQ( + syncer::SyncChange::ACTION_ADD, + sync_processor_->GetOnlyChange("good", "bar").change_type()); + EXPECT_EQ(1u, sync_processor_->changes().size()); + + { + syncer::SyncChangeList change_list; + change_list.push_back(settings_sync_util::CreateUpdate( + "good", "foo", barValue, model_type)); + // (Sending ADD here even though it's updating, since that's what the state + // of sync is. In any case, it won't work.) + change_list.push_back(settings_sync_util::CreateAdd( + "bad", "foo", barValue, model_type)); + GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); + } + + { + DictionaryValue dict; + dict.Set("foo", barValue.DeepCopy()); + dict.Set("bar", barValue.DeepCopy()); + EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); + } + { + DictionaryValue dict; + dict.Set("foo", fooValue.DeepCopy()); + dict.Set("bar", barValue.DeepCopy()); + EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); + } + + // Re-enabling sync without failing should cause the local changes from bad + // to be pushed to sync successfully, as should future changes to bad. + sync_processor_->ClearChanges(); + GetSyncableService(model_type)->StopSyncing(model_type); + sync_processor_delegate_.reset(new SyncChangeProcessorDelegate( + sync_processor_.get())); + GetSyncableService(model_type)->MergeDataAndStartSyncing( + model_type, + syncer::SyncDataList(), + sync_processor_delegate_.PassAs(), + scoped_ptr(new syncer::SyncErrorFactoryMock())); + + EXPECT_EQ( + syncer::SyncChange::ACTION_ADD, + sync_processor_->GetOnlyChange("good", "foo").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_ADD, + sync_processor_->GetOnlyChange("good", "bar").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_ADD, + sync_processor_->GetOnlyChange("bad", "foo").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_ADD, + sync_processor_->GetOnlyChange("bad", "bar").change_type()); + EXPECT_EQ(4u, sync_processor_->changes().size()); + + sync_processor_->ClearChanges(); + good->Set(DEFAULTS, "bar", fooValue); + bad->Set(DEFAULTS, "bar", fooValue); + + EXPECT_EQ( + syncer::SyncChange::ACTION_UPDATE, + sync_processor_->GetOnlyChange("good", "bar").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_UPDATE, + sync_processor_->GetOnlyChange("good", "bar").change_type()); + EXPECT_EQ(2u, sync_processor_->changes().size()); +} + +TEST_F(ExtensionSettingsSyncTest, FailureToPushLocalStateDisablesSync) { + syncer::ModelType model_type = syncer::EXTENSION_SETTINGS; + Extension::Type type = Extension::TYPE_EXTENSION; + + StringValue fooValue("fooValue"); + StringValue barValue("barValue"); + + TestingValueStoreFactory* testing_factory = new TestingValueStoreFactory(); + storage_factory_->Reset(testing_factory); + + ValueStore* good = AddExtensionAndGetStorage("good", type); + ValueStore* bad = AddExtensionAndGetStorage("bad", type); + + // Only set bad; setting good will cause it to fail below. + bad->Set(DEFAULTS, "foo", fooValue); + + sync_processor_->SetFailAllRequests(true); + GetSyncableService(model_type)->MergeDataAndStartSyncing( + model_type, + syncer::SyncDataList(), + sync_processor_delegate_.PassAs(), + scoped_ptr(new syncer::SyncErrorFactoryMock())); + sync_processor_->SetFailAllRequests(false); + + // Changes from good will be send to sync, changes from bad won't. + sync_processor_->ClearChanges(); + good->Set(DEFAULTS, "foo", barValue); + bad->Set(DEFAULTS, "foo", barValue); + + EXPECT_EQ( + syncer::SyncChange::ACTION_ADD, + sync_processor_->GetOnlyChange("good", "foo").change_type()); + EXPECT_EQ(1u, sync_processor_->changes().size()); + + // Changes from sync will be sent to good, not to bad. + { + syncer::SyncChangeList change_list; + change_list.push_back(settings_sync_util::CreateAdd( + "good", "bar", barValue, model_type)); + change_list.push_back(settings_sync_util::CreateAdd( + "bad", "bar", barValue, model_type)); + GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); + } + + { + DictionaryValue dict; + dict.Set("foo", barValue.DeepCopy()); + dict.Set("bar", barValue.DeepCopy()); + EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); + } + { + DictionaryValue dict; + dict.Set("foo", barValue.DeepCopy()); + EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); + } + + // Restarting sync makes everything work again. + sync_processor_->ClearChanges(); + GetSyncableService(model_type)->StopSyncing(model_type); + sync_processor_delegate_.reset(new SyncChangeProcessorDelegate( + sync_processor_.get())); + GetSyncableService(model_type)->MergeDataAndStartSyncing( + model_type, + syncer::SyncDataList(), + sync_processor_delegate_.PassAs(), + scoped_ptr(new syncer::SyncErrorFactoryMock())); + + EXPECT_EQ( + syncer::SyncChange::ACTION_ADD, + sync_processor_->GetOnlyChange("good", "foo").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_ADD, + sync_processor_->GetOnlyChange("good", "bar").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_ADD, + sync_processor_->GetOnlyChange("bad", "foo").change_type()); + EXPECT_EQ(3u, sync_processor_->changes().size()); + + sync_processor_->ClearChanges(); + good->Set(DEFAULTS, "foo", fooValue); + bad->Set(DEFAULTS, "foo", fooValue); + + EXPECT_EQ( + syncer::SyncChange::ACTION_UPDATE, + sync_processor_->GetOnlyChange("good", "foo").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_UPDATE, + sync_processor_->GetOnlyChange("good", "foo").change_type()); + EXPECT_EQ(2u, sync_processor_->changes().size()); +} + +TEST_F(ExtensionSettingsSyncTest, FailureToPushLocalChangeDisablesSync) { + syncer::ModelType model_type = syncer::EXTENSION_SETTINGS; + Extension::Type type = Extension::TYPE_EXTENSION; + + StringValue fooValue("fooValue"); + StringValue barValue("barValue"); + + TestingValueStoreFactory* testing_factory = new TestingValueStoreFactory(); + storage_factory_->Reset(testing_factory); + + ValueStore* good = AddExtensionAndGetStorage("good", type); + ValueStore* bad = AddExtensionAndGetStorage("bad", type); + + GetSyncableService(model_type)->MergeDataAndStartSyncing( + model_type, + syncer::SyncDataList(), + sync_processor_delegate_.PassAs(), + scoped_ptr(new syncer::SyncErrorFactoryMock())); + + // bad will fail to send changes. + good->Set(DEFAULTS, "foo", fooValue); + sync_processor_->SetFailAllRequests(true); + bad->Set(DEFAULTS, "foo", fooValue); + sync_processor_->SetFailAllRequests(false); + + EXPECT_EQ( + syncer::SyncChange::ACTION_ADD, + sync_processor_->GetOnlyChange("good", "foo").change_type()); + EXPECT_EQ(1u, sync_processor_->changes().size()); + + // No further changes should be sent from bad. + sync_processor_->ClearChanges(); + good->Set(DEFAULTS, "foo", barValue); + bad->Set(DEFAULTS, "foo", barValue); + + EXPECT_EQ( + syncer::SyncChange::ACTION_UPDATE, + sync_processor_->GetOnlyChange("good", "foo").change_type()); + EXPECT_EQ(1u, sync_processor_->changes().size()); + + // Changes from sync will be sent to good, not to bad. + { + syncer::SyncChangeList change_list; + change_list.push_back(settings_sync_util::CreateAdd( + "good", "bar", barValue, model_type)); + change_list.push_back(settings_sync_util::CreateAdd( + "bad", "bar", barValue, model_type)); + GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); + } + + { + DictionaryValue dict; + dict.Set("foo", barValue.DeepCopy()); + dict.Set("bar", barValue.DeepCopy()); + EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); + } + { + DictionaryValue dict; + dict.Set("foo", barValue.DeepCopy()); + EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); + } + + // Restarting sync makes everything work again. + sync_processor_->ClearChanges(); + GetSyncableService(model_type)->StopSyncing(model_type); + sync_processor_delegate_.reset(new SyncChangeProcessorDelegate( + sync_processor_.get())); + GetSyncableService(model_type)->MergeDataAndStartSyncing( + model_type, + syncer::SyncDataList(), + sync_processor_delegate_.PassAs(), + scoped_ptr(new syncer::SyncErrorFactoryMock())); + + EXPECT_EQ( + syncer::SyncChange::ACTION_ADD, + sync_processor_->GetOnlyChange("good", "foo").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_ADD, + sync_processor_->GetOnlyChange("good", "bar").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_ADD, + sync_processor_->GetOnlyChange("bad", "foo").change_type()); + EXPECT_EQ(3u, sync_processor_->changes().size()); + + sync_processor_->ClearChanges(); + good->Set(DEFAULTS, "foo", fooValue); + bad->Set(DEFAULTS, "foo", fooValue); + + EXPECT_EQ( + syncer::SyncChange::ACTION_UPDATE, + sync_processor_->GetOnlyChange("good", "foo").change_type()); + EXPECT_EQ( + syncer::SyncChange::ACTION_UPDATE, + sync_processor_->GetOnlyChange("good", "foo").change_type()); + EXPECT_EQ(2u, sync_processor_->changes().size()); +} + +TEST_F(ExtensionSettingsSyncTest, + LargeOutgoingChangeRejectedButIncomingAccepted) { + syncer::ModelType model_type = syncer::APP_SETTINGS; + Extension::Type type = Extension::TYPE_LEGACY_PACKAGED_APP; + + // This value should be larger than the limit in settings_backend.cc. + std::string string_5k; + for (size_t i = 0; i < 5000; ++i) { + string_5k.append("a"); + } + StringValue large_value(string_5k); + + GetSyncableService(model_type)->MergeDataAndStartSyncing( + model_type, + syncer::SyncDataList(), + sync_processor_delegate_.PassAs(), + scoped_ptr(new syncer::SyncErrorFactoryMock())); + + // Large local change rejected and doesn't get sent out. + ValueStore* storage1 = AddExtensionAndGetStorage("s1", type); + EXPECT_TRUE(storage1->Set(DEFAULTS, "large_value", large_value)->HasError()); + EXPECT_EQ(0u, sync_processor_->changes().size()); + + // Large incoming change should still get accepted. + ValueStore* storage2 = AddExtensionAndGetStorage("s2", type); + { + syncer::SyncChangeList change_list; + change_list.push_back(settings_sync_util::CreateAdd( + "s1", "large_value", large_value, model_type)); + change_list.push_back(settings_sync_util::CreateAdd( + "s2", "large_value", large_value, model_type)); + GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); + } + { + DictionaryValue expected; + expected.Set("large_value", large_value.DeepCopy()); + EXPECT_PRED_FORMAT2(SettingsEq, expected, storage1->Get()); + EXPECT_PRED_FORMAT2(SettingsEq, expected, storage2->Get()); + } + + GetSyncableService(model_type)->StopSyncing(model_type); +} + +TEST_F(ExtensionSettingsSyncTest, Dots) { + syncer::ModelType model_type = syncer::EXTENSION_SETTINGS; + Extension::Type type = Extension::TYPE_EXTENSION; + + ValueStore* storage = AddExtensionAndGetStorage("ext", type); + + { + syncer::SyncDataList sync_data_list; + scoped_ptr string_value(Value::CreateStringValue("value")); + sync_data_list.push_back(settings_sync_util::CreateData( + "ext", "key.with.dot", *string_value, model_type)); + + GetSyncableService(model_type)->MergeDataAndStartSyncing( + model_type, + sync_data_list, + sync_processor_delegate_.PassAs(), + scoped_ptr( + new syncer::SyncErrorFactoryMock())); + } + + // Test dots in keys that come from sync. + { + ValueStore::ReadResult data = storage->Get(); + ASSERT_FALSE(data->HasError()); + + DictionaryValue expected_data; + expected_data.SetWithoutPathExpansion( + "key.with.dot", + Value::CreateStringValue("value")); + EXPECT_TRUE(Value::Equals(&expected_data, data->settings().get())); + } + + // Test dots in keys going to sync. + { + scoped_ptr string_value(Value::CreateStringValue("spot")); + storage->Set(DEFAULTS, "key.with.spot", *string_value); + + ASSERT_EQ(1u, sync_processor_->changes().size()); + SettingSyncData sync_data = sync_processor_->changes()[0]; + EXPECT_EQ(syncer::SyncChange::ACTION_ADD, sync_data.change_type()); + EXPECT_EQ("ext", sync_data.extension_id()); + EXPECT_EQ("key.with.spot", sync_data.key()); + EXPECT_TRUE(sync_data.value().Equals(string_value.get())); + } +} + +} // namespace extensions diff --git a/chrome/browser/extensions/settings/settings_sync_util.cc b/chrome/browser/extensions/settings/settings_sync_util.cc new file mode 100644 index 0000000..5c6c28a --- /dev/null +++ b/chrome/browser/extensions/settings/settings_sync_util.cc @@ -0,0 +1,110 @@ +// 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/settings/settings_sync_util.h" + +#include "base/json/json_writer.h" +#include "base/values.h" +#include "sync/protocol/app_setting_specifics.pb.h" +#include "sync/protocol/extension_setting_specifics.pb.h" +#include "sync/protocol/sync.pb.h" + +namespace extensions { + +namespace settings_sync_util { + +namespace { + +void PopulateExtensionSettingSpecifics( + const std::string& extension_id, + const std::string& key, + const Value& value, + sync_pb::ExtensionSettingSpecifics* specifics) { + specifics->set_extension_id(extension_id); + specifics->set_key(key); + { + std::string value_as_json; + base::JSONWriter::Write(&value, &value_as_json); + specifics->set_value(value_as_json); + } +} + +void PopulateAppSettingSpecifics( + const std::string& extension_id, + const std::string& key, + const Value& value, + sync_pb::AppSettingSpecifics* specifics) { + PopulateExtensionSettingSpecifics( + extension_id, key, value, specifics->mutable_extension_setting()); +} + +} // namespace + +syncer::SyncData CreateData( + const std::string& extension_id, + const std::string& key, + const Value& value, + syncer::ModelType type) { + sync_pb::EntitySpecifics specifics; + switch (type) { + case syncer::EXTENSION_SETTINGS: + PopulateExtensionSettingSpecifics( + extension_id, + key, + value, + specifics.mutable_extension_setting()); + break; + + case syncer::APP_SETTINGS: + PopulateAppSettingSpecifics( + extension_id, + key, + value, + specifics.mutable_app_setting()); + break; + + default: + NOTREACHED(); + } + + return syncer::SyncData::CreateLocalData( + extension_id + "/" + key, key, specifics); +} + +syncer::SyncChange CreateAdd( + const std::string& extension_id, + const std::string& key, + const Value& value, + syncer::ModelType type) { + return syncer::SyncChange( + FROM_HERE, + syncer::SyncChange::ACTION_ADD, + CreateData(extension_id, key, value, type)); +} + +syncer::SyncChange CreateUpdate( + const std::string& extension_id, + const std::string& key, + const Value& value, + syncer::ModelType type) { + return syncer::SyncChange( + FROM_HERE, + syncer::SyncChange::ACTION_UPDATE, + CreateData(extension_id, key, value, type)); +} + +syncer::SyncChange CreateDelete( + const std::string& extension_id, + const std::string& key, + syncer::ModelType type) { + DictionaryValue no_value; + return syncer::SyncChange( + FROM_HERE, + syncer::SyncChange::ACTION_DELETE, + CreateData(extension_id, key, no_value, type)); +} + +} // namespace settings_sync_util + +} // namespace extensions diff --git a/chrome/browser/extensions/settings/settings_sync_util.h b/chrome/browser/extensions/settings/settings_sync_util.h new file mode 100644 index 0000000..08769a3 --- /dev/null +++ b/chrome/browser/extensions/settings/settings_sync_util.h @@ -0,0 +1,51 @@ +// 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_SETTINGS_SETTINGS_SYNC_UTIL_H_ +#define CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTINGS_SYNC_UTIL_H_ + + +#include "sync/api/sync_change.h" +#include "sync/api/sync_data.h" + +namespace base { +class Value; +} // namespace base + +namespace extensions { + +namespace settings_sync_util { + +// Creates a syncer::SyncData object for an extension or app setting. +syncer::SyncData CreateData( + const std::string& extension_id, + const std::string& key, + const base::Value& value, + syncer::ModelType type); + +// Creates an "add" sync change for an extension or app setting. +syncer::SyncChange CreateAdd( + const std::string& extension_id, + const std::string& key, + const base::Value& value, + syncer::ModelType type); + +// Creates an "update" sync change for an extension or app setting. +syncer::SyncChange CreateUpdate( + const std::string& extension_id, + const std::string& key, + const base::Value& value, + syncer::ModelType type); + +// Creates a "delete" sync change for an extension or app setting. +syncer::SyncChange CreateDelete( + const std::string& extension_id, + const std::string& key, + syncer::ModelType type); + +} // namespace settings_sync_util + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTINGS_SYNC_UTIL_H_ diff --git a/chrome/browser/extensions/settings/settings_test_util.cc b/chrome/browser/extensions/settings/settings_test_util.cc new file mode 100644 index 0000000..7f92850 --- /dev/null +++ b/chrome/browser/extensions/settings/settings_test_util.cc @@ -0,0 +1,165 @@ +// 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/settings/settings_test_util.h" + +#include "base/file_path.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/browser/extensions/extension_system_factory.h" +#include "chrome/browser/extensions/settings/settings_frontend.h" + +namespace extensions { + +namespace settings_test_util { + +// Intended as a StorageCallback from GetStorage. +static void AssignStorage(ValueStore** dst, ValueStore* src) { + *dst = src; +} + +ValueStore* GetStorage( + const std::string& extension_id, + settings_namespace::Namespace settings_namespace, + SettingsFrontend* frontend) { + ValueStore* storage = NULL; + frontend->RunWithStorage( + extension_id, + settings_namespace, + base::Bind(&AssignStorage, &storage)); + MessageLoop::current()->RunUntilIdle(); + return storage; +} + +ValueStore* GetStorage( + const std::string& extension_id, SettingsFrontend* frontend) { + return GetStorage(extension_id, settings_namespace::SYNC, frontend); +} + +// MockExtensionService + +MockExtensionService::MockExtensionService() {} + +MockExtensionService::~MockExtensionService() {} + +const Extension* MockExtensionService::GetExtensionById( + const std::string& id, bool include_disabled) const { + std::map >::const_iterator + maybe_extension = extensions_.find(id); + return maybe_extension == extensions_.end() ? + NULL : maybe_extension->second.get(); +} + +void MockExtensionService::AddExtensionWithId( + const std::string& id, Extension::Type type) { + std::set empty_permissions; + AddExtensionWithIdAndPermissions(id, type, empty_permissions); +} + +void MockExtensionService::AddExtensionWithIdAndPermissions( + const std::string& id, + Extension::Type type, + const std::set& permissions_set) { + DictionaryValue manifest; + manifest.SetString("name", std::string("Test extension ") + id); + manifest.SetString("version", "1.0"); + + scoped_ptr permissions(new ListValue()); + for (std::set::const_iterator it = permissions_set.begin(); + it != permissions_set.end(); ++it) { + permissions->Append(Value::CreateStringValue(*it)); + } + manifest.Set("permissions", permissions.release()); + + switch (type) { + case Extension::TYPE_EXTENSION: + break; + + case Extension::TYPE_LEGACY_PACKAGED_APP: { + DictionaryValue* app = new DictionaryValue(); + DictionaryValue* app_launch = new DictionaryValue(); + app_launch->SetString("local_path", "fake.html"); + app->Set("launch", app_launch); + manifest.Set("app", app); + break; + } + + default: + NOTREACHED(); + } + + std::string error; + scoped_refptr extension(Extension::Create( + FilePath(), + Extension::INTERNAL, + manifest, + Extension::NO_FLAGS, + id, + &error)); + DCHECK(extension.get()); + DCHECK(error.empty()); + extensions_[id] = extension; + + for (std::set::const_iterator it = permissions_set.begin(); + it != permissions_set.end(); ++it) { + DCHECK(extension->HasAPIPermission(*it)); + } +} + +// MockExtensionSystem + +MockExtensionSystem::MockExtensionSystem(Profile* profile) + : TestExtensionSystem(profile) {} +MockExtensionSystem::~MockExtensionSystem() {} + +EventRouter* MockExtensionSystem::event_router() { + if (!event_router_.get()) + event_router_.reset(new EventRouter(profile_, NULL)); + return event_router_.get(); +} + +ExtensionService* MockExtensionSystem::extension_service() { + ExtensionServiceInterface* as_interface = + static_cast(&extension_service_); + return static_cast(as_interface); +} + +ProfileKeyedService* BuildMockExtensionSystem(Profile* profile) { + return new MockExtensionSystem(profile); +} + +// MockProfile + +MockProfile::MockProfile(const FilePath& file_path) + : TestingProfile(file_path) { + ExtensionSystemFactory::GetInstance()->SetTestingFactoryAndUse(this, + &BuildMockExtensionSystem); +} + +MockProfile::~MockProfile() {} + +// ScopedSettingsFactory + +ScopedSettingsStorageFactory::ScopedSettingsStorageFactory() {} + +ScopedSettingsStorageFactory::ScopedSettingsStorageFactory( + const scoped_refptr& delegate) + : delegate_(delegate) {} + +ScopedSettingsStorageFactory::~ScopedSettingsStorageFactory() {} + +void ScopedSettingsStorageFactory::Reset( + const scoped_refptr& delegate) { + delegate_ = delegate; +} + +ValueStore* ScopedSettingsStorageFactory::Create( + const FilePath& base_path, + const std::string& extension_id) { + DCHECK(delegate_.get()); + return delegate_->Create(base_path, extension_id); +} + +} // namespace settings_test_util + +} // namespace extensions diff --git a/chrome/browser/extensions/settings/settings_test_util.h b/chrome/browser/extensions/settings/settings_test_util.h new file mode 100644 index 0000000..d1f3e44 --- /dev/null +++ b/chrome/browser/extensions/settings/settings_test_util.h @@ -0,0 +1,117 @@ +// 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_SETTINGS_SETTINGS_TEST_UTIL_H_ +#define CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTINGS_TEST_UTIL_H_ + +#include +#include + +#include "base/compiler_specific.h" +#include "base/memory/linked_ptr.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/extensions/event_router.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/settings/settings_namespace.h" +#include "chrome/browser/extensions/settings/settings_storage_factory.h" +#include "chrome/browser/extensions/test_extension_service.h" +#include "chrome/browser/extensions/test_extension_system.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/test/base/testing_profile.h" + +class ValueStore; + +namespace extensions { + +class SettingsFrontend; +// Utilities for extension settings API tests. +namespace settings_test_util { + +// Synchronously gets the storage area for an extension from |frontend|. +ValueStore* GetStorage( + const std::string& extension_id, + settings_namespace::Namespace setting_namespace, + SettingsFrontend* frontend); + +// Synchronously gets the SYNC storage for an extension from |frontend|. +ValueStore* GetStorage( + const std::string& extension_id, + SettingsFrontend* frontend); + +// An ExtensionService which allows extensions to be hand-added to be returned +// by GetExtensionById. +class MockExtensionService : public TestExtensionService { + public: + MockExtensionService(); + virtual ~MockExtensionService(); + + // Adds an extension with id |id| to be returned by GetExtensionById. + void AddExtensionWithId(const std::string& id, Extension::Type type); + + // Adds an extension with id |id| to be returned by GetExtensionById, with + // a set of permissions. + void AddExtensionWithIdAndPermissions( + const std::string& id, + Extension::Type type, + const std::set& permissions); + + virtual const Extension* GetExtensionById( + const std::string& id, bool include_disabled) const OVERRIDE; + + private: + std::map > extensions_; +}; + +// A mock ExtensionSystem to serve an EventRouter. +class MockExtensionSystem : public TestExtensionSystem { + public: + explicit MockExtensionSystem(Profile* profile); + virtual ~MockExtensionSystem(); + + virtual EventRouter* event_router() OVERRIDE; + virtual ExtensionService* extension_service() OVERRIDE; + + private: + scoped_ptr event_router_; + MockExtensionService extension_service_; + + DISALLOW_COPY_AND_ASSIGN(MockExtensionSystem); +}; + +// A Profile which returns an ExtensionService with enough functionality for +// the tests. +class MockProfile : public TestingProfile { + public: + explicit MockProfile(const FilePath& file_path); + virtual ~MockProfile(); +}; + +// SettingsStorageFactory which acts as a wrapper for other factories. +class ScopedSettingsStorageFactory : public SettingsStorageFactory { + public: + ScopedSettingsStorageFactory(); + + explicit ScopedSettingsStorageFactory( + const scoped_refptr& delegate); + + // Sets the delegate factory (equivalent to scoped_ptr::reset). + void Reset(const scoped_refptr& delegate); + + // SettingsStorageFactory implementation. + virtual ValueStore* Create(const FilePath& base_path, + const std::string& extension_id) OVERRIDE; + + private: + // SettingsStorageFactory is refcounted. + virtual ~ScopedSettingsStorageFactory(); + + scoped_refptr delegate_; +}; + +} // namespace settings_test_util + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTINGS_TEST_UTIL_H_ diff --git a/chrome/browser/extensions/settings/sync_or_local_value_store_cache.cc b/chrome/browser/extensions/settings/sync_or_local_value_store_cache.cc new file mode 100644 index 0000000..e3d951f --- /dev/null +++ b/chrome/browser/extensions/settings/sync_or_local_value_store_cache.cc @@ -0,0 +1,115 @@ +// 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/settings/sync_or_local_value_store_cache.h" + +#include "base/bind.h" +#include "base/callback.h" +#include "base/file_path.h" +#include "base/sequenced_task_runner.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/settings/settings_backend.h" +#include "chrome/browser/extensions/settings/settings_frontend.h" +#include "chrome/browser/extensions/settings/settings_storage_quota_enforcer.h" +#include "chrome/browser/extensions/settings/weak_unlimited_settings_storage.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/permissions/api_permission.h" +#include "content/public/browser/browser_thread.h" + +using content::BrowserThread; + +namespace extensions { + +SyncOrLocalValueStoreCache::SyncOrLocalValueStoreCache( + settings_namespace::Namespace settings_namespace, + const scoped_refptr& factory, + const SettingsStorageQuotaEnforcer::Limits& quota, + const scoped_refptr& observers, + const FilePath& profile_path) + : settings_namespace_(settings_namespace) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(settings_namespace_ == settings_namespace::LOCAL || + settings_namespace_ == settings_namespace::SYNC); + + // This post is safe since the destructor can only be invoked from the + // same message loop, and any potential post of a deletion task must come + // after the constructor returns. + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + base::Bind(&SyncOrLocalValueStoreCache::InitOnFileThread, + base::Unretained(this), + factory, quota, observers, profile_path)); +} + +SyncOrLocalValueStoreCache::~SyncOrLocalValueStoreCache() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); +} + +SettingsBackend* SyncOrLocalValueStoreCache::GetAppBackend() const { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + DCHECK(app_backend_.get()); + return app_backend_.get(); +} + +SettingsBackend* SyncOrLocalValueStoreCache::GetExtensionBackend() const { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + DCHECK(extension_backend_.get()); + return extension_backend_.get(); +} + +void SyncOrLocalValueStoreCache::RunWithValueStoreForExtension( + const StorageCallback& callback, + scoped_refptr extension) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + DCHECK(app_backend_.get()); + DCHECK(extension_backend_.get()); + SettingsBackend* backend = + extension->is_app() ? app_backend_.get() : extension_backend_.get(); + ValueStore* storage = backend->GetStorage(extension->id()); + + // A neat way to implement unlimited storage; if the extension has the + // unlimited storage permission, force through all calls to Set() (in the + // same way that writes from sync ignore quota). + // But only if it's local storage (bad stuff would happen if sync'ed + // storage is allowed to be unlimited). + bool is_unlimited = + settings_namespace_ == settings_namespace::LOCAL && + extension->HasAPIPermission(APIPermission::kUnlimitedStorage); + + if (is_unlimited) { + WeakUnlimitedSettingsStorage unlimited_storage(storage); + callback.Run(&unlimited_storage); + } else { + callback.Run(storage); + } +} + +void SyncOrLocalValueStoreCache::DeleteStorageSoon( + const std::string& extension_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + app_backend_->DeleteStorage(extension_id); + extension_backend_->DeleteStorage(extension_id); +} + +void SyncOrLocalValueStoreCache::InitOnFileThread( + const scoped_refptr& factory, + const SettingsStorageQuotaEnforcer::Limits& quota, + const scoped_refptr& observers, + const FilePath& profile_path) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + DCHECK(!app_backend_.get()); + DCHECK(!extension_backend_.get()); + const bool local = settings_namespace_ == settings_namespace::LOCAL; + const FilePath app_path = profile_path.AppendASCII( + local ? ExtensionService::kLocalAppSettingsDirectoryName + : ExtensionService::kSyncAppSettingsDirectoryName); + const FilePath extension_path = profile_path.AppendASCII( + local ? ExtensionService::kLocalExtensionSettingsDirectoryName + : ExtensionService::kSyncExtensionSettingsDirectoryName); + app_backend_.reset(new SettingsBackend(factory, app_path, quota, observers)); + extension_backend_.reset( + new SettingsBackend(factory, extension_path, quota, observers)); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/settings/sync_or_local_value_store_cache.h b/chrome/browser/extensions/settings/sync_or_local_value_store_cache.h new file mode 100644 index 0000000..05cd59e --- /dev/null +++ b/chrome/browser/extensions/settings/sync_or_local_value_store_cache.h @@ -0,0 +1,62 @@ +// 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_SETTINGS_SYNC_OR_LOCAL_VALUE_STORE_CACHE_H_ +#define CHROME_BROWSER_EXTENSIONS_SETTINGS_SYNC_OR_LOCAL_VALUE_STORE_CACHE_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/extensions/settings/settings_observer.h" +#include "chrome/browser/extensions/settings/settings_storage_quota_enforcer.h" +#include "chrome/browser/extensions/settings/value_store_cache.h" + +class FilePath; + +namespace extensions { + +class SettingsBackend; +class SettingsStorageFactory; + +// ValueStoreCache for the LOCAL and SYNC namespaces. It owns a backend for +// apps and another for extensions. Each backend takes care of persistence and +// syncing. +class SyncOrLocalValueStoreCache : public ValueStoreCache { + public: + SyncOrLocalValueStoreCache( + settings_namespace::Namespace settings_namespace, + const scoped_refptr& factory, + const SettingsStorageQuotaEnforcer::Limits& quota, + const scoped_refptr& observers, + const FilePath& profile_path); + virtual ~SyncOrLocalValueStoreCache(); + + SettingsBackend* GetAppBackend() const; + SettingsBackend* GetExtensionBackend() const; + + // ValueStoreCache implementation: + + virtual void RunWithValueStoreForExtension( + const StorageCallback& callback, + scoped_refptr extension) OVERRIDE; + + virtual void DeleteStorageSoon(const std::string& extension_id) OVERRIDE; + + private: + void InitOnFileThread(const scoped_refptr& factory, + const SettingsStorageQuotaEnforcer::Limits& quota, + const scoped_refptr& observers, + const FilePath& profile_path); + + settings_namespace::Namespace settings_namespace_; + scoped_ptr app_backend_; + scoped_ptr extension_backend_; + + DISALLOW_COPY_AND_ASSIGN(SyncOrLocalValueStoreCache); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_SETTINGS_SYNC_OR_LOCAL_VALUE_STORE_CACHE_H_ diff --git a/chrome/browser/extensions/settings/syncable_settings_storage.cc b/chrome/browser/extensions/settings/syncable_settings_storage.cc new file mode 100644 index 0000000..c4edd9c --- /dev/null +++ b/chrome/browser/extensions/settings/syncable_settings_storage.cc @@ -0,0 +1,384 @@ +// 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/settings/syncable_settings_storage.h" + +#include "chrome/browser/extensions/settings/settings_namespace.h" +#include "chrome/browser/extensions/settings/settings_sync_processor.h" +#include "chrome/browser/extensions/settings/settings_sync_util.h" +#include "content/public/browser/browser_thread.h" +#include "sync/api/sync_data.h" +#include "sync/protocol/extension_setting_specifics.pb.h" + +namespace extensions { + +using content::BrowserThread; + +SyncableSettingsStorage::SyncableSettingsStorage( + const scoped_refptr >& + observers, + const std::string& extension_id, + ValueStore* delegate) + : observers_(observers), + extension_id_(extension_id), + delegate_(delegate) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); +} + +SyncableSettingsStorage::~SyncableSettingsStorage() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); +} + +size_t SyncableSettingsStorage::GetBytesInUse(const std::string& key) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + return delegate_->GetBytesInUse(key); +} + +size_t SyncableSettingsStorage::GetBytesInUse( + const std::vector& keys) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + return delegate_->GetBytesInUse(keys); +} + +size_t SyncableSettingsStorage::GetBytesInUse() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + return delegate_->GetBytesInUse(); +} + +ValueStore::ReadResult SyncableSettingsStorage::Get( + const std::string& key) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + return delegate_->Get(key); +} + +ValueStore::ReadResult SyncableSettingsStorage::Get( + const std::vector& keys) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + return delegate_->Get(keys); +} + +ValueStore::ReadResult SyncableSettingsStorage::Get() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + return delegate_->Get(); +} + +ValueStore::WriteResult SyncableSettingsStorage::Set( + WriteOptions options, const std::string& key, const Value& value) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + WriteResult result = delegate_->Set(options, key, value); + if (result->HasError()) { + return result.Pass(); + } + SyncResultIfEnabled(result); + return result.Pass(); +} + +ValueStore::WriteResult SyncableSettingsStorage::Set( + WriteOptions options, const DictionaryValue& values) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + WriteResult result = delegate_->Set(options, values); + if (result->HasError()) { + return result.Pass(); + } + SyncResultIfEnabled(result); + return result.Pass(); +} + +ValueStore::WriteResult SyncableSettingsStorage::Remove( + const std::string& key) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + WriteResult result = delegate_->Remove(key); + if (result->HasError()) { + return result.Pass(); + } + SyncResultIfEnabled(result); + return result.Pass(); +} + +ValueStore::WriteResult SyncableSettingsStorage::Remove( + const std::vector& keys) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + WriteResult result = delegate_->Remove(keys); + if (result->HasError()) { + return result.Pass(); + } + SyncResultIfEnabled(result); + return result.Pass(); +} + +ValueStore::WriteResult SyncableSettingsStorage::Clear() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + WriteResult result = delegate_->Clear(); + if (result->HasError()) { + return result.Pass(); + } + SyncResultIfEnabled(result); + return result.Pass(); +} + +void SyncableSettingsStorage::SyncResultIfEnabled( + const ValueStore::WriteResult& result) { + if (sync_processor_.get() && !result->changes().empty()) { + syncer::SyncError error = sync_processor_->SendChanges(result->changes()); + if (error.IsSet()) + StopSyncing(); + } +} + +// Sync-related methods. + +syncer::SyncError SyncableSettingsStorage::StartSyncing( + const DictionaryValue& sync_state, + scoped_ptr sync_processor) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + DCHECK(!sync_processor_.get()); + + sync_processor_ = sync_processor.Pass(); + sync_processor_->Init(sync_state); + + ReadResult maybe_settings = delegate_->Get(); + if (maybe_settings->HasError()) { + return syncer::SyncError( + FROM_HERE, + std::string("Failed to get settings: ") + maybe_settings->error(), + sync_processor_->type()); + } + + const DictionaryValue& settings = *maybe_settings->settings().get(); + if (sync_state.empty()) + return SendLocalSettingsToSync(settings); + else + return OverwriteLocalSettingsWithSync(sync_state, settings); +} + +syncer::SyncError SyncableSettingsStorage::SendLocalSettingsToSync( + const DictionaryValue& settings) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + + ValueStoreChangeList changes; + for (DictionaryValue::Iterator i(settings); i.HasNext(); i.Advance()) { + changes.push_back(ValueStoreChange(i.key(), NULL, i.value().DeepCopy())); + } + + if (changes.empty()) + return syncer::SyncError(); + + syncer::SyncError error = sync_processor_->SendChanges(changes); + if (error.IsSet()) + StopSyncing(); + + return error; +} + +syncer::SyncError SyncableSettingsStorage::OverwriteLocalSettingsWithSync( + const DictionaryValue& sync_state, const DictionaryValue& settings) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + // Treat this as a list of changes to sync and use ProcessSyncChanges. + // This gives notifications etc for free. + scoped_ptr new_sync_state(sync_state.DeepCopy()); + + SettingSyncDataList changes; + for (DictionaryValue::Iterator it(settings); it.HasNext(); it.Advance()) { + Value* orphaned_sync_value = NULL; + if (new_sync_state->RemoveWithoutPathExpansion( + it.key(), &orphaned_sync_value)) { + scoped_ptr sync_value(orphaned_sync_value); + if (sync_value->Equals(&it.value())) { + // Sync and local values are the same, no changes to send. + } else { + // Sync value is different, update local setting with new value. + changes.push_back( + SettingSyncData( + syncer::SyncChange::ACTION_UPDATE, + extension_id_, + it.key(), + sync_value.Pass())); + } + } else { + // Not synced, delete local setting. + changes.push_back( + SettingSyncData( + syncer::SyncChange::ACTION_DELETE, + extension_id_, + it.key(), + scoped_ptr(new DictionaryValue()))); + } + } + + // Add all new settings to local settings. + while (!new_sync_state->empty()) { + std::string key = *new_sync_state->begin_keys(); + Value* value = NULL; + CHECK(new_sync_state->RemoveWithoutPathExpansion(key, &value)); + changes.push_back( + SettingSyncData( + syncer::SyncChange::ACTION_ADD, + extension_id_, + key, + scoped_ptr(value))); + } + + if (changes.empty()) + return syncer::SyncError(); + + return ProcessSyncChanges(changes); +} + +void SyncableSettingsStorage::StopSyncing() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + sync_processor_.reset(); +} + +syncer::SyncError SyncableSettingsStorage::ProcessSyncChanges( + const SettingSyncDataList& sync_changes) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + DCHECK(!sync_changes.empty()) << "No sync changes for " << extension_id_; + + if (!sync_processor_.get()) { + return syncer::SyncError( + FROM_HERE, + std::string("Sync is inactive for ") + extension_id_, + syncer::UNSPECIFIED); + } + + std::vector errors; + ValueStoreChangeList changes; + + for (SettingSyncDataList::const_iterator it = sync_changes.begin(); + it != sync_changes.end(); ++it) { + DCHECK_EQ(extension_id_, it->extension_id()); + + const std::string& key = it->key(); + const Value& value = it->value(); + + scoped_ptr current_value; + { + ReadResult maybe_settings = Get(it->key()); + if (maybe_settings->HasError()) { + errors.push_back(syncer::SyncError( + FROM_HERE, + std::string("Error getting current sync state for ") + + extension_id_ + "/" + key + ": " + maybe_settings->error(), + sync_processor_->type())); + continue; + } + Value* value = NULL; + if (maybe_settings->settings()->GetWithoutPathExpansion(key, &value)) { + current_value.reset(value->DeepCopy()); + } + } + + syncer::SyncError error; + + switch (it->change_type()) { + case syncer::SyncChange::ACTION_ADD: + if (!current_value.get()) { + error = OnSyncAdd(key, value.DeepCopy(), &changes); + } else { + // Already a value; hopefully a local change has beaten sync in a + // race and it's not a bug, so pretend it's an update. + LOG(WARNING) << "Got add from sync for existing setting " << + extension_id_ << "/" << key; + error = OnSyncUpdate( + key, current_value.release(), value.DeepCopy(), &changes); + } + break; + + case syncer::SyncChange::ACTION_UPDATE: + if (current_value.get()) { + error = OnSyncUpdate( + key, current_value.release(), value.DeepCopy(), &changes); + } else { + // Similarly, pretend it's an add. + LOG(WARNING) << "Got update from sync for nonexistent setting" << + extension_id_ << "/" << key; + error = OnSyncAdd(key, value.DeepCopy(), &changes); + } + break; + + case syncer::SyncChange::ACTION_DELETE: + if (current_value.get()) { + error = OnSyncDelete(key, current_value.release(), &changes); + } else { + // Similarly, ignore it. + LOG(WARNING) << "Got delete from sync for nonexistent setting " << + extension_id_ << "/" << key; + } + break; + + default: + NOTREACHED(); + } + + if (error.IsSet()) { + errors.push_back(error); + } + } + + sync_processor_->NotifyChanges(changes); + + observers_->Notify( + &SettingsObserver::OnSettingsChanged, + extension_id_, + settings_namespace::SYNC, + ValueStoreChange::ToJson(changes)); + + // TODO(kalman): Something sensible with multiple errors. + return errors.empty() ? syncer::SyncError() : errors[0]; +} + +syncer::SyncError SyncableSettingsStorage::OnSyncAdd( + const std::string& key, + Value* new_value, + ValueStoreChangeList* changes) { + DCHECK(new_value); + WriteResult result = delegate_->Set(IGNORE_QUOTA, key, *new_value); + if (result->HasError()) { + return syncer::SyncError( + FROM_HERE, + std::string("Error pushing sync add to local settings: ") + + result->error(), + sync_processor_->type()); + } + changes->push_back(ValueStoreChange(key, NULL, new_value)); + return syncer::SyncError(); +} + +syncer::SyncError SyncableSettingsStorage::OnSyncUpdate( + const std::string& key, + Value* old_value, + Value* new_value, + ValueStoreChangeList* changes) { + DCHECK(old_value); + DCHECK(new_value); + WriteResult result = delegate_->Set(IGNORE_QUOTA, key, *new_value); + if (result->HasError()) { + return syncer::SyncError( + FROM_HERE, + std::string("Error pushing sync update to local settings: ") + + result->error(), + sync_processor_->type()); + } + changes->push_back(ValueStoreChange(key, old_value, new_value)); + return syncer::SyncError(); +} + +syncer::SyncError SyncableSettingsStorage::OnSyncDelete( + const std::string& key, + Value* old_value, + ValueStoreChangeList* changes) { + DCHECK(old_value); + WriteResult result = delegate_->Remove(key); + if (result->HasError()) { + return syncer::SyncError( + FROM_HERE, + std::string("Error pushing sync remove to local settings: ") + + result->error(), + sync_processor_->type()); + } + changes->push_back(ValueStoreChange(key, old_value, NULL)); + return syncer::SyncError(); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/settings/syncable_settings_storage.h b/chrome/browser/extensions/settings/syncable_settings_storage.h new file mode 100644 index 0000000..993a226 --- /dev/null +++ b/chrome/browser/extensions/settings/syncable_settings_storage.h @@ -0,0 +1,111 @@ +// 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_SETTINGS_SYNCABLE_SETTINGS_STORAGE_H_ +#define CHROME_BROWSER_EXTENSIONS_SETTINGS_SYNCABLE_SETTINGS_STORAGE_H_ + +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/observer_list_threadsafe.h" +#include "base/values.h" +#include "chrome/browser/extensions/settings/setting_sync_data.h" +#include "chrome/browser/extensions/settings/settings_observer.h" +#include "chrome/browser/value_store/value_store.h" +#include "sync/api/sync_change.h" +#include "sync/api/syncable_service.h" + +namespace extensions { + +class SettingsSyncProcessor; + +// Decorates a ValueStore with sync behaviour. +class SyncableSettingsStorage : public ValueStore { + public: + SyncableSettingsStorage( + const scoped_refptr& observers, + const std::string& extension_id, + // Ownership taken. + ValueStore* delegate); + + virtual ~SyncableSettingsStorage(); + + // ValueStore implementation. + virtual size_t GetBytesInUse(const std::string& key) OVERRIDE; + virtual size_t GetBytesInUse(const std::vector& keys) OVERRIDE; + virtual size_t GetBytesInUse() OVERRIDE; + virtual ReadResult Get(const std::string& key) OVERRIDE; + virtual ReadResult Get(const std::vector& keys) OVERRIDE; + virtual ReadResult Get() OVERRIDE; + virtual WriteResult Set( + WriteOptions options, + const std::string& key, + const Value& value) OVERRIDE; + virtual WriteResult Set( + WriteOptions options, const DictionaryValue& values) OVERRIDE; + virtual WriteResult Remove(const std::string& key) OVERRIDE; + virtual WriteResult Remove(const std::vector& keys) OVERRIDE; + virtual WriteResult Clear() OVERRIDE; + + // Sync-related methods, analogous to those on SyncableService (handled by + // ExtensionSettings), but with looser guarantees about when the methods + // can be called. + + // Must only be called if sync isn't already active. + syncer::SyncError StartSyncing( + const DictionaryValue& sync_state, + scoped_ptr sync_processor); + + // May be called at any time (idempotent). + void StopSyncing(); + + // May be called at any time; changes will be ignored if sync isn't active. + syncer::SyncError ProcessSyncChanges(const SettingSyncDataList& sync_changes); + + private: + // Sends the changes from |result| to sync if it's enabled. + void SyncResultIfEnabled(const ValueStore::WriteResult& result); + + // Sends all local settings to sync (synced settings assumed to be empty). + syncer::SyncError SendLocalSettingsToSync( + const DictionaryValue& settings); + + // Overwrites local state with sync state. + syncer::SyncError OverwriteLocalSettingsWithSync( + const DictionaryValue& sync_state, const DictionaryValue& settings); + + // Called when an Add/Update/Remove comes from sync. Ownership of Value*s + // are taken. + syncer::SyncError OnSyncAdd( + const std::string& key, + Value* new_value, + ValueStoreChangeList* changes); + syncer::SyncError OnSyncUpdate( + const std::string& key, + Value* old_value, + Value* new_value, + ValueStoreChangeList* changes); + syncer::SyncError OnSyncDelete( + const std::string& key, + Value* old_value, + ValueStoreChangeList* changes); + + // List of observers to settings changes. + const scoped_refptr observers_; + + // Id of the extension these settings are for. + std::string const extension_id_; + + // Storage area to sync. + const scoped_ptr delegate_; + + // Object which sends changes to sync. + scoped_ptr sync_processor_; + + DISALLOW_COPY_AND_ASSIGN(SyncableSettingsStorage); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_SETTINGS_SYNCABLE_SETTINGS_STORAGE_H_ diff --git a/chrome/browser/extensions/settings/value_store_cache.cc b/chrome/browser/extensions/settings/value_store_cache.cc new file mode 100644 index 0000000..4d3fd70 --- /dev/null +++ b/chrome/browser/extensions/settings/value_store_cache.cc @@ -0,0 +1,13 @@ +// 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/settings/value_store_cache.h" + +namespace extensions { + +ValueStoreCache::~ValueStoreCache() {} + +void ValueStoreCache::ShutdownOnUI() {} + +} // namespace extensions diff --git a/chrome/browser/extensions/settings/value_store_cache.h b/chrome/browser/extensions/settings/value_store_cache.h new file mode 100644 index 0000000..b3322f8 --- /dev/null +++ b/chrome/browser/extensions/settings/value_store_cache.h @@ -0,0 +1,57 @@ +// 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_SETTINGS_VALUE_STORE_CACHE_H_ +#define CHROME_BROWSER_EXTENSIONS_SETTINGS_VALUE_STORE_CACHE_H_ + +#include + +#include "base/callback.h" +#include "base/memory/ref_counted.h" + +class ValueStore; + +namespace extensions { + +class Extension; + +// Each namespace of the storage API implements this interface. +// Instances are created on the UI thread, but from then on live on the FILE +// thread. At shutdown, ShutdownOnUI() is first invoked on the UI thread, and +// the destructor is invoked soon after on the FILE thread. This gives +// implementations the chance to work with ValueStores on FILE but observe +// events on UI. +// It also means that any methods invoked on UI *before ShutdownOnUI()* can +// safely post other methods to the FILE thread, since the deletion task is only +// posted to FILE after ShutdownOnUI(). +class ValueStoreCache { + public: + typedef base::Callback StorageCallback; + + // Invoked on FILE. + virtual ~ValueStoreCache(); + + // This is invoked from the UI thread during destruction of the Profile that + // ultimately owns this object. Any Profile-related cleanups should be + // performed in this method, since the destructor will execute later, after + // the Profile is already gone. + virtual void ShutdownOnUI(); + + // Requests the cache to invoke |callback| with the appropriate ValueStore + // for the given |extension|. |callback| should be invoked with a NULL + // ValueStore in case of errors. + // |extension| is passed in a scoped_refptr<> because this method is + // asynchronously posted as a task to the loop returned by GetMessageLoop(), + // and this guarantees the Extension is still valid when the method executes. + virtual void RunWithValueStoreForExtension( + const StorageCallback& callback, + scoped_refptr extension) = 0; + + // Requests the cache to delete any storage used by |extension_id|. + virtual void DeleteStorageSoon(const std::string& extension_id) = 0; +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_SETTINGS_VALUE_STORE_CACHE_H_ diff --git a/chrome/browser/extensions/settings/weak_unlimited_settings_storage.cc b/chrome/browser/extensions/settings/weak_unlimited_settings_storage.cc new file mode 100644 index 0000000..7afbc2c --- /dev/null +++ b/chrome/browser/extensions/settings/weak_unlimited_settings_storage.cc @@ -0,0 +1,67 @@ +// 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/settings/weak_unlimited_settings_storage.h" + +namespace extensions { + +WeakUnlimitedSettingsStorage::WeakUnlimitedSettingsStorage( + ValueStore* delegate) + : delegate_(delegate) {} + +WeakUnlimitedSettingsStorage::~WeakUnlimitedSettingsStorage() {} + +size_t WeakUnlimitedSettingsStorage::GetBytesInUse(const std::string& key) { + return delegate_->GetBytesInUse(key); +} + +size_t WeakUnlimitedSettingsStorage::GetBytesInUse( + const std::vector& keys) { + return delegate_->GetBytesInUse(keys); +} + + +size_t WeakUnlimitedSettingsStorage::GetBytesInUse() { + return delegate_->GetBytesInUse(); +} + +ValueStore::ReadResult WeakUnlimitedSettingsStorage::Get( + const std::string& key) { + return delegate_->Get(key); +} + +ValueStore::ReadResult WeakUnlimitedSettingsStorage::Get( + const std::vector& keys) { + return delegate_->Get(keys); +} + +ValueStore::ReadResult WeakUnlimitedSettingsStorage::Get() { + return delegate_->Get(); +} + +ValueStore::WriteResult WeakUnlimitedSettingsStorage::Set( + WriteOptions options, const std::string& key, const Value& value) { + return delegate_->Set(IGNORE_QUOTA, key, value); +} + +ValueStore::WriteResult WeakUnlimitedSettingsStorage::Set( + WriteOptions options, const DictionaryValue& values) { + return delegate_->Set(IGNORE_QUOTA, values); +} + +ValueStore::WriteResult WeakUnlimitedSettingsStorage::Remove( + const std::string& key) { + return delegate_->Remove(key); +} + +ValueStore::WriteResult WeakUnlimitedSettingsStorage::Remove( + const std::vector& keys) { + return delegate_->Remove(keys); +} + +ValueStore::WriteResult WeakUnlimitedSettingsStorage::Clear() { + return delegate_->Clear(); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/settings/weak_unlimited_settings_storage.h b/chrome/browser/extensions/settings/weak_unlimited_settings_storage.h new file mode 100644 index 0000000..c9e6014 --- /dev/null +++ b/chrome/browser/extensions/settings/weak_unlimited_settings_storage.h @@ -0,0 +1,49 @@ +// 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_SETTINGS_WEAK_UNLIMITED_SETTINGS_STORAGE_H_ +#define CHROME_BROWSER_EXTENSIONS_SETTINGS_WEAK_UNLIMITED_SETTINGS_STORAGE_H_ + +#include "base/compiler_specific.h" +#include "chrome/browser/value_store/value_store.h" + +namespace extensions { + +// A ValueStore decorator which makes calls through |Set| ignore quota. +// "Weak" because ownership of the delegate isn't taken; this is designed to be +// temporarily attached to storage areas. +class WeakUnlimitedSettingsStorage : public ValueStore { + public: + // Ownership of |delegate| NOT taken. + explicit WeakUnlimitedSettingsStorage(ValueStore* delegate); + + virtual ~WeakUnlimitedSettingsStorage(); + + // ValueStore implementation. + virtual size_t GetBytesInUse(const std::string& key) OVERRIDE; + virtual size_t GetBytesInUse(const std::vector& keys) OVERRIDE; + virtual size_t GetBytesInUse() OVERRIDE; + virtual ReadResult Get(const std::string& key) OVERRIDE; + virtual ReadResult Get(const std::vector& keys) OVERRIDE; + virtual ReadResult Get() OVERRIDE; + virtual WriteResult Set( + WriteOptions options, + const std::string& key, + const Value& value) OVERRIDE; + virtual WriteResult Set( + WriteOptions options, const DictionaryValue& values) OVERRIDE; + virtual WriteResult Remove(const std::string& key) OVERRIDE; + virtual WriteResult Remove(const std::vector& keys) OVERRIDE; + virtual WriteResult Clear() OVERRIDE; + + private: + // The delegate storage area, NOT OWNED. + ValueStore* const delegate_; + + DISALLOW_COPY_AND_ASSIGN(WeakUnlimitedSettingsStorage); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_SETTINGS_WEAK_UNLIMITED_SETTINGS_STORAGE_H_ diff --git a/chrome/browser/performance_monitor/performance_monitor.gyp b/chrome/browser/performance_monitor/performance_monitor.gyp index e3587f4..08fe90e 100644 --- a/chrome/browser/performance_monitor/performance_monitor.gyp +++ b/chrome/browser/performance_monitor/performance_monitor.gyp @@ -8,14 +8,14 @@ 'target_name': 'performance_monitor', 'type': 'static_library', 'sources': [ - '<@(schema_files)', + '<@(json_schema_files)', ], 'includes': [ '../../../build/json_schema_compile.gypi', ], 'variables': { 'chromium_code': 1, - 'schema_files': [ + 'json_schema_files': [ 'events.json', ], 'cc_dir': 'chrome/browser/performance_monitor', diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index c888a88..ff03982 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc @@ -17,7 +17,7 @@ #include "chrome/browser/devtools/devtools_window.h" #include "chrome/browser/download/download_prefs.h" #include "chrome/browser/extensions/api/commands/command_service.h" -#include "chrome/browser/extensions/api/tabs/tabs_api.h" +#include "chrome/browser/extensions/api/tabs/tabs.h" #include "chrome/browser/extensions/component_loader.h" #include "chrome/browser/extensions/extension_prefs.h" #include "chrome/browser/extensions/extension_web_ui.h" @@ -278,7 +278,7 @@ void RegisterUserPrefs(PrefServiceSyncable* user_prefs) { #endif #if !defined(OS_ANDROID) - TabsCaptureVisibleTabFunction::RegisterUserPrefs(user_prefs); + CaptureVisibleTabFunction::RegisterUserPrefs(user_prefs); ChromeToMobileService::RegisterUserPrefs(user_prefs); DevToolsWindow::RegisterUserPrefs(user_prefs); extensions::CommandService::RegisterUserPrefs(user_prefs); diff --git a/chrome/browser/sync/glue/chrome_extensions_activity_monitor.cc b/chrome/browser/sync/glue/chrome_extensions_activity_monitor.cc index 7518a83..1c6d472 100644 --- a/chrome/browser/sync/glue/chrome_extensions_activity_monitor.cc +++ b/chrome/browser/sync/glue/chrome_extensions_activity_monitor.cc @@ -5,7 +5,7 @@ #include "chrome/browser/sync/glue/chrome_extensions_activity_monitor.h" #include "base/bind.h" -#include "chrome/browser/extensions/api/bookmarks/bookmarks_api.h" +#include "chrome/browser/extensions/api/bookmarks/bookmark_api.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/extensions/extension.h" #include "content/public/browser/browser_thread.h" diff --git a/chrome/browser/sync/glue/chrome_extensions_activity_monitor_unittest.cc b/chrome/browser/sync/glue/chrome_extensions_activity_monitor_unittest.cc index 37ddfd6..f03e231 100644 --- a/chrome/browser/sync/glue/chrome_extensions_activity_monitor_unittest.cc +++ b/chrome/browser/sync/glue/chrome_extensions_activity_monitor_unittest.cc @@ -8,7 +8,7 @@ #include "base/message_loop.h" #include "base/path_service.h" #include "base/values.h" -#include "chrome/browser/extensions/api/bookmarks/bookmarks_api.h" +#include "chrome/browser/extensions/api/bookmarks/bookmark_api.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/extensions/extension.h" @@ -86,20 +86,18 @@ class SyncChromeExtensionsActivityMonitorTest : public testing::Test { // 2. Only the mutating events should be recorded by the // syncer::ExtensionsActivityMonitor. TEST_F(SyncChromeExtensionsActivityMonitorTest, Basic) { - FireBookmarksApiEvent(extension1_, 1); - FireBookmarksApiEvent(extension1_, 1); - FireBookmarksApiEvent(extension1_, 2); - FireBookmarksApiEvent(extension1_, 3); - FireBookmarksApiEvent(extension1_, 5); + FireBookmarksApiEvent(extension1_, 1); + FireBookmarksApiEvent(extension1_, 1); + FireBookmarksApiEvent(extension1_, 2); + FireBookmarksApiEvent(extension1_, 3); + FireBookmarksApiEvent(extension1_, 5); const uint32 writes_by_extension1 = 1 + 1 + 2 + 3; - FireBookmarksApiEvent( - extension2_, 8); - FireBookmarksApiEvent( - extension2_, 13); - FireBookmarksApiEvent( + FireBookmarksApiEvent(extension2_, 8); + FireBookmarksApiEvent(extension2_, 13); + FireBookmarksApiEvent( extension2_, 21); - FireBookmarksApiEvent(extension2_, 33); + FireBookmarksApiEvent(extension2_, 33); const uint32 writes_by_extension2 = 8; syncer::ExtensionsActivityMonitor::Records results; @@ -117,8 +115,8 @@ TEST_F(SyncChromeExtensionsActivityMonitorTest, Basic) { // and put the old records back. Those should be merged with the new // records correctly. TEST_F(SyncChromeExtensionsActivityMonitorTest, Put) { - FireBookmarksApiEvent(extension1_, 5); - FireBookmarksApiEvent(extension2_, 8); + FireBookmarksApiEvent(extension1_, 5); + FireBookmarksApiEvent(extension2_, 8); syncer::ExtensionsActivityMonitor::Records results; monitor_.GetAndClearRecords(&results); @@ -127,8 +125,8 @@ TEST_F(SyncChromeExtensionsActivityMonitorTest, Put) { EXPECT_EQ(5U, results[id1_].bookmark_write_count); EXPECT_EQ(8U, results[id2_].bookmark_write_count); - FireBookmarksApiEvent(extension2_, 3); - FireBookmarksApiEvent(extension2_, 2); + FireBookmarksApiEvent(extension2_, 3); + FireBookmarksApiEvent(extension2_, 2); // Simulate a commit failure, which augments the active record set with the // refugee records. @@ -147,7 +145,7 @@ TEST_F(SyncChromeExtensionsActivityMonitorTest, Put) { // times. The mintor should correctly clear its records every time // they're returned. TEST_F(SyncChromeExtensionsActivityMonitorTest, MultiGet) { - FireBookmarksApiEvent(extension1_, 5); + FireBookmarksApiEvent(extension1_, 5); syncer::ExtensionsActivityMonitor::Records results; monitor_.GetAndClearRecords(&results); @@ -158,7 +156,7 @@ TEST_F(SyncChromeExtensionsActivityMonitorTest, MultiGet) { monitor_.GetAndClearRecords(&results); EXPECT_TRUE(results.empty()); - FireBookmarksApiEvent(extension1_, 3); + FireBookmarksApiEvent(extension1_, 3); monitor_.GetAndClearRecords(&results); EXPECT_EQ(1U, results.size()); diff --git a/chrome/browser/sync/profile_sync_components_factory_impl.cc b/chrome/browser/sync/profile_sync_components_factory_impl.cc index acdefba..a9d9a52 100644 --- a/chrome/browser/sync/profile_sync_components_factory_impl.cc +++ b/chrome/browser/sync/profile_sync_components_factory_impl.cc @@ -5,11 +5,11 @@ #include "base/command_line.h" #include "build/build_config.h" #include "chrome/browser/bookmarks/bookmark_model_factory.h" -#include "chrome/browser/extensions/api/storage/settings_frontend.h" #include "chrome/browser/extensions/app_notification_manager.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/settings/settings_frontend.h" #include "chrome/browser/history/history.h" #include "chrome/browser/history/history_service_factory.h" #include "chrome/browser/prefs/pref_model_associator.h" @@ -46,8 +46,8 @@ #include "chrome/browser/sync/glue/ui_data_type_controller.h" #include "chrome/browser/sync/profile_sync_components_factory_impl.h" #include "chrome/browser/sync/profile_sync_service.h" -#include "chrome/browser/themes/theme_service.h" #include "chrome/browser/themes/theme_service_factory.h" +#include "chrome/browser/themes/theme_service.h" #include "chrome/browser/themes/theme_syncable_service.h" #include "chrome/browser/webdata/autocomplete_syncable_service.h" #include "chrome/browser/webdata/autofill_profile_syncable_service.h" diff --git a/chrome/browser/sync/test/integration/extension_settings_helper.cc b/chrome/browser/sync/test/integration/extension_settings_helper.cc index be57a4a..5c42b95 100644 --- a/chrome/browser/sync/test/integration/extension_settings_helper.cc +++ b/chrome/browser/sync/test/integration/extension_settings_helper.cc @@ -6,12 +6,12 @@ #include "base/bind.h" #include "base/json/json_writer.h" -#include "base/logging.h" #include "base/memory/scoped_ptr.h" +#include "base/logging.h" #include "base/synchronization/waitable_event.h" #include "base/values.h" -#include "chrome/browser/extensions/api/storage/settings_frontend.h" #include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/settings/settings_frontend.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/sync/test/integration/extensions_helper.h" #include "chrome/browser/sync/test/integration/sync_datatype_helper.h" diff --git a/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm b/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm index ebdaa95..97894ed 100644 --- a/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm +++ b/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm @@ -17,7 +17,7 @@ #include "chrome/browser/command_updater.h" #include "chrome/browser/defaults.h" #include "chrome/browser/extensions/api/omnibox/omnibox_api.h" -#include "chrome/browser/extensions/api/tabs/tabs_api.h" +#include "chrome/browser/extensions/api/tabs/tabs.h" #include "chrome/browser/extensions/extension_action.h" #include "chrome/browser/extensions/location_bar_controller.h" #include "chrome/browser/extensions/tab_helper.h" diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi index 1765329..97b6e33 100644 --- a/chrome/chrome_browser_extensions.gypi +++ b/chrome/chrome_browser_extensions.gypi @@ -97,14 +97,14 @@ 'browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.h', 'browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api_constants.cc', 'browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api_constants.h', + 'browser/extensions/api/bookmarks/bookmark_api.cc', + 'browser/extensions/api/bookmarks/bookmark_api.h', 'browser/extensions/api/bookmarks/bookmark_api_constants.cc', 'browser/extensions/api/bookmarks/bookmark_api_constants.h', 'browser/extensions/api/bookmarks/bookmark_api_factory.cc', 'browser/extensions/api/bookmarks/bookmark_api_factory.h', 'browser/extensions/api/bookmarks/bookmark_api_helpers.cc', 'browser/extensions/api/bookmarks/bookmark_api_helpers.h', - 'browser/extensions/api/bookmarks/bookmarks_api.cc', - 'browser/extensions/api/bookmarks/bookmarks_api.h', 'browser/extensions/api/browsing_data/browsing_data_api.cc', 'browser/extensions/api/browsing_data/browsing_data_api.h', 'browser/extensions/api/cloud_print_private/cloud_print_private_api.cc', @@ -123,8 +123,8 @@ 'browser/extensions/api/content_settings/content_settings_store.h', 'browser/extensions/api/commands/commands.cc', 'browser/extensions/api/commands/commands.h', - 'browser/extensions/api/context_menus/context_menus_api.cc', - 'browser/extensions/api/context_menus/context_menus_api.h', + 'browser/extensions/api/context_menu/context_menu_api.cc', + 'browser/extensions/api/context_menu/context_menu_api.h', 'browser/extensions/api/cookies/cookies_api.cc', 'browser/extensions/api/cookies/cookies_api.h', 'browser/extensions/api/cookies/cookies_api_constants.cc', @@ -324,38 +324,6 @@ 'browser/extensions/api/socket/tcp_socket.h', 'browser/extensions/api/socket/udp_socket.cc', 'browser/extensions/api/socket/udp_socket.h', - 'browser/extensions/api/storage/leveldb_settings_storage_factory.cc', - 'browser/extensions/api/storage/leveldb_settings_storage_factory.h', - 'browser/extensions/api/storage/managed_value_store_cache.cc', - 'browser/extensions/api/storage/managed_value_store_cache.h', - 'browser/extensions/api/storage/policy_value_store.cc', - 'browser/extensions/api/storage/policy_value_store.h', - 'browser/extensions/api/storage/setting_sync_data.cc', - 'browser/extensions/api/storage/setting_sync_data.h', - 'browser/extensions/api/storage/settings_backend.cc', - 'browser/extensions/api/storage/settings_backend.h', - 'browser/extensions/api/storage/settings_frontend.cc', - 'browser/extensions/api/storage/settings_frontend.h', - 'browser/extensions/api/storage/settings_namespace.cc', - 'browser/extensions/api/storage/settings_namespace.h', - 'browser/extensions/api/storage/settings_observer.h', - 'browser/extensions/api/storage/settings_storage_factory.h', - 'browser/extensions/api/storage/settings_storage_quota_enforcer.cc', - 'browser/extensions/api/storage/settings_storage_quota_enforcer.h', - 'browser/extensions/api/storage/settings_sync_processor.cc', - 'browser/extensions/api/storage/settings_sync_processor.h', - 'browser/extensions/api/storage/settings_sync_util.cc', - 'browser/extensions/api/storage/settings_sync_util.h', - 'browser/extensions/api/storage/storage_api.cc', - 'browser/extensions/api/storage/storage_api.h', - 'browser/extensions/api/storage/sync_or_local_value_store_cache.cc', - 'browser/extensions/api/storage/sync_or_local_value_store_cache.h', - 'browser/extensions/api/storage/syncable_settings_storage.cc', - 'browser/extensions/api/storage/syncable_settings_storage.h', - 'browser/extensions/api/storage/value_store_cache.cc', - 'browser/extensions/api/storage/value_store_cache.h', - 'browser/extensions/api/storage/weak_unlimited_settings_storage.cc', - 'browser/extensions/api/storage/weak_unlimited_settings_storage.h', 'browser/extensions/api/sync_file_system/extension_sync_event_observer_factory.cc', 'browser/extensions/api/sync_file_system/extension_sync_event_observer_factory.h', 'browser/extensions/api/sync_file_system/extension_sync_event_observer.cc', @@ -399,8 +367,10 @@ 'browser/extensions/api/tab_capture/tab_capture_registry.h', 'browser/extensions/api/tab_capture/tab_capture_registry_factory.cc', 'browser/extensions/api/tab_capture/tab_capture_registry_factory.h', - 'browser/extensions/api/tabs/tabs_api.cc', - 'browser/extensions/api/tabs/tabs_api.h', + 'browser/extensions/api/tabs/execute_code_in_tab_function.cc', + 'browser/extensions/api/tabs/execute_code_in_tab_function.h', + 'browser/extensions/api/tabs/tabs.cc', + 'browser/extensions/api/tabs/tabs.h', 'browser/extensions/api/tabs/tabs_windows_api.cc', 'browser/extensions/api/tabs/tabs_windows_api.h', 'browser/extensions/api/tabs/tabs_constants.cc', @@ -666,6 +636,38 @@ 'browser/extensions/script_bubble_controller.h', 'browser/extensions/script_executor.cc', 'browser/extensions/script_executor.h', + 'browser/extensions/settings/leveldb_settings_storage_factory.cc', + 'browser/extensions/settings/leveldb_settings_storage_factory.h', + 'browser/extensions/settings/managed_value_store_cache.cc', + 'browser/extensions/settings/managed_value_store_cache.h', + 'browser/extensions/settings/policy_value_store.cc', + 'browser/extensions/settings/policy_value_store.h', + 'browser/extensions/settings/setting_sync_data.cc', + 'browser/extensions/settings/setting_sync_data.h', + 'browser/extensions/settings/settings_api.cc', + 'browser/extensions/settings/settings_api.h', + 'browser/extensions/settings/settings_backend.cc', + 'browser/extensions/settings/settings_backend.h', + 'browser/extensions/settings/settings_frontend.cc', + 'browser/extensions/settings/settings_frontend.h', + 'browser/extensions/settings/settings_namespace.cc', + 'browser/extensions/settings/settings_namespace.h', + 'browser/extensions/settings/settings_observer.h', + 'browser/extensions/settings/settings_storage_factory.h', + 'browser/extensions/settings/settings_storage_quota_enforcer.cc', + 'browser/extensions/settings/settings_storage_quota_enforcer.h', + 'browser/extensions/settings/settings_sync_processor.cc', + 'browser/extensions/settings/settings_sync_processor.h', + 'browser/extensions/settings/settings_sync_util.cc', + 'browser/extensions/settings/settings_sync_util.h', + 'browser/extensions/settings/sync_or_local_value_store_cache.cc', + 'browser/extensions/settings/sync_or_local_value_store_cache.h', + 'browser/extensions/settings/syncable_settings_storage.cc', + 'browser/extensions/settings/syncable_settings_storage.h', + 'browser/extensions/settings/value_store_cache.cc', + 'browser/extensions/settings/value_store_cache.h', + 'browser/extensions/settings/weak_unlimited_settings_storage.cc', + 'browser/extensions/settings/weak_unlimited_settings_storage.h', 'browser/extensions/shell_window_geometry_cache.cc', 'browser/extensions/shell_window_geometry_cache.h', 'browser/extensions/shell_window_registry.cc', @@ -784,7 +786,6 @@ ['include', '^browser/extensions/api/proxy/proxy_api_constants.cc'], ['include', '^browser/extensions/api/push_messaging/push_messaging_api.cc'], ['include', '^browser/extensions/api/runtime/runtime_api.cc'], - ['include', '^browser/extensions/api/storage/.*\.cc'], ['include', '^browser/extensions/api/system_info_cpu/cpu_info_provider.cc'], ['include', '^browser/extensions/api/system_info_cpu/cpu_info_provider_android.cc'], ['include', '^browser/extensions/api/tabs/tabs_constants.cc'], @@ -831,10 +832,10 @@ ], }, { # configuration_policy==0 'sources!': [ - 'browser/extensions/api/storage/managed_value_store_cache.cc', - 'browser/extensions/api/storage/managed_value_store_cache.h', - 'browser/extensions/api/storage/policy_value_store.cc', - 'browser/extensions/api/storage/policy_value_store.h', + 'browser/extensions/settings/managed_value_store_cache.cc', + 'browser/extensions/settings/managed_value_store_cache.h', + 'browser/extensions/settings/policy_value_store.cc', + 'browser/extensions/settings/policy_value_store.h', ], }], ['OS=="win"', { diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 7449b3e..ad69e62 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -969,7 +969,7 @@ 'browser/extensions/api/browsing_data/browsing_data_test.cc', 'browser/extensions/api/cloud_print_private/cloud_print_private_apitest.cc', 'browser/extensions/api/content_settings/content_settings_apitest.cc', - 'browser/extensions/api/context_menus/context_menu_apitest.cc', + 'browser/extensions/api/context_menu/context_menu_apitest.cc', 'browser/extensions/api/cookies/cookies_apitest.cc', 'browser/extensions/api/debugger/debugger_apitest.cc', 'browser/extensions/api/declarative/declarative_apitest.cc', @@ -1088,7 +1088,7 @@ 'browser/extensions/process_management_browsertest.cc', 'browser/extensions/requirements_checker_browsertest.cc', 'browser/extensions/sandboxed_pages_apitest.cc', - 'browser/extensions/api/storage/settings_apitest.cc', + 'browser/extensions/settings/settings_apitest.cc', 'browser/extensions/stubs_apitest.cc', 'browser/extensions/subscribe_page_action_browsertest.cc', 'browser/extensions/system/system_apitest.cc', diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index e0240b7..16a90bd 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -735,12 +735,12 @@ 'browser/extensions/sandboxed_unpacker_unittest.cc', 'browser/extensions/script_badge_controller_unittest.cc', 'browser/extensions/script_bubble_controller_unittest.cc', - 'browser/extensions/api/storage/policy_value_store_unittest.cc', - 'browser/extensions/api/storage/settings_frontend_unittest.cc', - 'browser/extensions/api/storage/settings_quota_unittest.cc', - 'browser/extensions/api/storage/settings_sync_unittest.cc', - 'browser/extensions/api/storage/settings_test_util.cc', - 'browser/extensions/api/storage/settings_test_util.h', + 'browser/extensions/settings/policy_value_store_unittest.cc', + 'browser/extensions/settings/settings_frontend_unittest.cc', + 'browser/extensions/settings/settings_quota_unittest.cc', + 'browser/extensions/settings/settings_sync_unittest.cc', + 'browser/extensions/settings/settings_test_util.cc', + 'browser/extensions/settings/settings_test_util.h', 'browser/extensions/shell_window_geometry_cache_unittest.cc', 'browser/extensions/standard_management_policy_provider_unittest.cc', 'browser/extensions/updater/extension_updater_unittest.cc', @@ -1788,7 +1788,7 @@ }], ['configuration_policy==0', { 'sources!': [ - 'browser/extensions/api/storage/policy_value_store_unittest.cc', + 'browser/extensions/settings/policy_value_store_unittest.cc', 'browser/managed_mode/managed_mode_url_filter_unittest.cc', 'browser/prefs/proxy_policy_unittest.cc', 'browser/ui/webui/policy_ui_unittest.cc', diff --git a/chrome/common/extensions/api/api.gyp b/chrome/common/extensions/api/api.gyp index fce4407..95af0dc 100644 --- a/chrome/common/extensions/api/api.gyp +++ b/chrome/common/extensions/api/api.gyp @@ -8,7 +8,8 @@ 'target_name': 'api', 'type': 'static_library', 'sources': [ - '<@(schema_files)', + '<@(idl_schema_files)', + '<@(json_schema_files)', ], 'includes': [ '../../../../build/json_schema_bundle_compile.gypi', @@ -16,71 +17,75 @@ ], 'variables': { 'chromium_code': 1, - 'schema_files': [ - 'alarms.idl', - 'app_current_window_internal.idl', - 'app_runtime.idl', - 'app_window.idl', - 'autotest_private.idl', - 'bluetooth.idl', + 'json_schema_files': [ 'bookmarks.json', 'cloud_print_private.json', 'content_settings.json', 'context_menus.json', 'cookies.json', 'debugger.json', + 'events.json', + 'experimental_history.json', + 'experimental_record.json', + 'file_browser_handler_internal.json', + 'i18n.json', + 'font_settings.json', + 'history.json', + 'management.json', + 'page_capture.json', + 'permissions.json', + 'storage.json', + 'tabs.json', + 'web_navigation.json', + 'web_request.json', + 'windows.json', + ], + 'idl_schema_files': [ + 'alarms.idl', + 'app_current_window_internal.idl', + 'app_runtime.idl', + 'app_window.idl', + 'autotest_private.idl', + 'bluetooth.idl', 'developer_private.idl', 'dial.idl', 'downloads.idl', - 'events.json', 'experimental_discovery.idl', 'experimental_dns.idl', - 'experimental_history.json', 'experimental_identity.idl', 'experimental_idltest.idl', 'experimental_media_galleries.idl', 'experimental_notification.idl', - 'experimental_record.json', 'experimental_system_info_cpu.idl', 'experimental_system_info_display.idl', 'experimental_system_info_memory.idl', 'experimental_system_info_storage.idl', - 'file_browser_handler_internal.json', 'file_system.idl', - 'font_settings.json', - 'history.json', - 'i18n.json', - 'management.json', 'media_galleries.idl', 'media_galleries_private.idl', - 'page_capture.json', - 'permissions.json', 'push_messaging.idl', 'rtc_private.idl', 'serial.idl', 'socket.idl', - 'storage.json', 'sync_file_system.idl', 'system_indicator.idl', 'tab_capture.idl', - 'tabs.json', 'usb.idl', - 'web_navigation.json', - 'web_request.json', - 'windows.json', ], 'cc_dir': 'chrome/common/extensions/api', 'root_namespace': 'extensions::api', }, 'conditions': [ ['OS=="android"', { - 'schema_files!': [ + 'idl_schema_files!': [ 'usb.idl', ], }], ['OS!="chromeos"', { - 'schema_files!': [ + 'json_schema_files!': [ 'file_browser_handler_internal.json', + ], + 'idl_schema_files!': [ 'rtc_private.idl', ], }], diff --git a/chrome/common/extensions/api/content_settings.json b/chrome/common/extensions/api/content_settings.json index 43643dd..3c04bac 100644 --- a/chrome/common/extensions/api/content_settings.json +++ b/chrome/common/extensions/api/content_settings.json @@ -5,9 +5,6 @@ [ { "namespace": "contentSettings", - "compiler_options": { - "generate_type_functions": true - }, "types": [ { "id": "ResourceIdentifier", diff --git a/chrome/common/extensions/api/events.json b/chrome/common/extensions/api/events.json index b003fb6..cacc8dc 100644 --- a/chrome/common/extensions/api/events.json +++ b/chrome/common/extensions/api/events.json @@ -5,10 +5,6 @@ [ { "namespace": "events", - "compiler_options": { - "implemented_in": "chrome/browser/extensions/api/declarative/declarative_api.h", - "generate_type_functions": true - }, "internal": true, "unprivileged": true, "types": [ diff --git a/chrome/common/extensions/api/extension_api.cc b/chrome/common/extensions/api/extension_api.cc index 85207e7..fc38a24 100644 --- a/chrome/common/extensions/api/extension_api.cc +++ b/chrome/common/extensions/api/extension_api.cc @@ -344,6 +344,8 @@ void ExtensionAPI::InitDefaultConfiguration() { CHECK(unloaded_schemas_.empty()); RegisterSchema("app", ReadFromResource( IDR_EXTENSION_API_JSON_APP)); + RegisterSchema("bookmarks", ReadFromResource( + IDR_EXTENSION_API_JSON_BOOKMARKS)); RegisterSchema("bookmarkManagerPrivate", ReadFromResource( IDR_EXTENSION_API_JSON_BOOKMARKMANAGERPRIVATE)); RegisterSchema("browserAction", ReadFromResource( @@ -352,16 +354,30 @@ void ExtensionAPI::InitDefaultConfiguration() { IDR_EXTENSION_API_JSON_BROWSINGDATA)); RegisterSchema("chromeosInfoPrivate", ReadFromResource( IDR_EXTENSION_API_JSON_CHROMEOSINFOPRIVATE)); + RegisterSchema("cloudPrintPrivate", ReadFromResource( + IDR_EXTENSION_API_JSON_CLOUDPRINTPRIVATE)); RegisterSchema("commands", ReadFromResource( IDR_EXTENSION_API_JSON_COMMANDS)); + RegisterSchema("contentSettings", ReadFromResource( + IDR_EXTENSION_API_JSON_CONTENTSETTINGS)); + RegisterSchema("contextMenus", ReadFromResource( + IDR_EXTENSION_API_JSON_CONTEXTMENUS)); + RegisterSchema("cookies", ReadFromResource( + IDR_EXTENSION_API_JSON_COOKIES)); + RegisterSchema("debugger", ReadFromResource( + IDR_EXTENSION_API_JSON_DEBUGGER)); RegisterSchema("declarativeWebRequest", ReadFromResource( IDR_EXTENSION_API_JSON_DECLARATIVE_WEBREQUEST)); RegisterSchema("devtools", ReadFromResource( IDR_EXTENSION_API_JSON_DEVTOOLS)); + RegisterSchema("events", ReadFromResource( + IDR_EXTENSION_API_JSON_EVENTS)); RegisterSchema("experimental.accessibility", ReadFromResource( IDR_EXTENSION_API_JSON_EXPERIMENTAL_ACCESSIBILITY)); RegisterSchema("experimental.app", ReadFromResource( IDR_EXTENSION_API_JSON_EXPERIMENTAL_APP)); + RegisterSchema("experimental.history", ReadFromResource( + IDR_EXTENSION_API_JSON_EXPERIMENTAL_HISTORY)); RegisterSchema("experimental.infobars", ReadFromResource( IDR_EXTENSION_API_JSON_EXPERIMENTAL_INFOBARS)); RegisterSchema("experimental.input.virtualKeyboard", ReadFromResource( @@ -370,6 +386,8 @@ void ExtensionAPI::InitDefaultConfiguration() { IDR_EXTENSION_API_JSON_EXPERIMENTAL_POWER)); RegisterSchema("experimental.processes", ReadFromResource( IDR_EXTENSION_API_JSON_EXPERIMENTAL_PROCESSES)); + RegisterSchema("experimental.record", ReadFromResource( + IDR_EXTENSION_API_JSON_EXPERIMENTAL_RECORD)); RegisterSchema("experimental.rlz", ReadFromResource( IDR_EXTENSION_API_JSON_EXPERIMENTAL_RLZ)); RegisterSchema("runtime", ReadFromResource( @@ -380,8 +398,16 @@ void ExtensionAPI::InitDefaultConfiguration() { IDR_EXTENSION_API_JSON_EXTENSION)); RegisterSchema("fileBrowserHandler", ReadFromResource( IDR_EXTENSION_API_JSON_FILEBROWSERHANDLER)); + RegisterSchema("fileBrowserHandlerInternal", ReadFromResource( + IDR_EXTENSION_API_JSON_FILEBROWSERHANDLERINTERNAL)); RegisterSchema("fileBrowserPrivate", ReadFromResource( IDR_EXTENSION_API_JSON_FILEBROWSERPRIVATE)); + RegisterSchema("fontSettings", ReadFromResource( + IDR_EXTENSION_API_JSON_FONTSSETTINGS)); + RegisterSchema("history", ReadFromResource( + IDR_EXTENSION_API_JSON_HISTORY)); + RegisterSchema("i18n", ReadFromResource( + IDR_EXTENSION_API_JSON_I18N)); RegisterSchema("idle", ReadFromResource( IDR_EXTENSION_API_JSON_IDLE)); RegisterSchema("input.ime", ReadFromResource( @@ -390,6 +416,8 @@ void ExtensionAPI::InitDefaultConfiguration() { IDR_EXTENSION_API_JSON_INPUTMETHODPRIVATE)); RegisterSchema("managedModePrivate", ReadFromResource( IDR_EXTENSION_API_JSON_MANAGEDMODEPRIVATE)); + RegisterSchema("management", ReadFromResource( + IDR_EXTENSION_API_JSON_MANAGEMENT)); RegisterSchema("mediaPlayerPrivate", ReadFromResource( IDR_EXTENSION_API_JSON_MEDIAPLAYERPRIVATE)); RegisterSchema("metricsPrivate", ReadFromResource( @@ -402,6 +430,8 @@ void ExtensionAPI::InitDefaultConfiguration() { IDR_EXTENSION_API_JSON_PAGEACTION)); RegisterSchema("pageActions", ReadFromResource( IDR_EXTENSION_API_JSON_PAGEACTIONS)); + RegisterSchema("pageCapture", ReadFromResource( + IDR_EXTENSION_API_JSON_PAGECAPTURE)); RegisterSchema("permissions", ReadFromResource( IDR_EXTENSION_API_JSON_PERMISSIONS)); RegisterSchema("privacy", ReadFromResource( @@ -410,8 +440,12 @@ void ExtensionAPI::InitDefaultConfiguration() { IDR_EXTENSION_API_JSON_PROXY)); RegisterSchema("scriptBadge", ReadFromResource( IDR_EXTENSION_API_JSON_SCRIPTBADGE)); + RegisterSchema("storage", ReadFromResource( + IDR_EXTENSION_API_JSON_STORAGE)); RegisterSchema("systemPrivate", ReadFromResource( IDR_EXTENSION_API_JSON_SYSTEMPRIVATE)); + RegisterSchema("tabs", ReadFromResource( + IDR_EXTENSION_API_JSON_TABS)); RegisterSchema("terminalPrivate", ReadFromResource( IDR_EXTENSION_API_JSON_TERMINALPRIVATE)); RegisterSchema("test", ReadFromResource( @@ -426,6 +460,10 @@ void ExtensionAPI::InitDefaultConfiguration() { IDR_EXTENSION_API_JSON_TYPES)); RegisterSchema("wallpaperPrivate", ReadFromResource( IDR_EXTENSION_API_JSON_WALLPAPERPRIVATE)); + RegisterSchema("webNavigation", ReadFromResource( + IDR_EXTENSION_API_JSON_WEBNAVIGATION)); + RegisterSchema("webRequest", ReadFromResource( + IDR_EXTENSION_API_JSON_WEBREQUEST)); RegisterSchema("webRequestInternal", ReadFromResource( IDR_EXTENSION_API_JSON_WEBREQUESTINTERNAL)); RegisterSchema("webSocketProxyPrivate", ReadFromResource( @@ -434,6 +472,8 @@ void ExtensionAPI::InitDefaultConfiguration() { IDR_EXTENSION_API_JSON_WEBSTORE)); RegisterSchema("webstorePrivate", ReadFromResource( IDR_EXTENSION_API_JSON_WEBSTOREPRIVATE)); + RegisterSchema("windows", ReadFromResource( + IDR_EXTENSION_API_JSON_WINDOWS)); // Schemas to be loaded via JSON generated from IDL files. GeneratedSchemas::Get(&unloaded_schemas_); diff --git a/chrome/common/extensions/api/file_browser_handler_internal.json b/chrome/common/extensions/api/file_browser_handler_internal.json index a50a0a8..38fa8d3 100644 --- a/chrome/common/extensions/api/file_browser_handler_internal.json +++ b/chrome/common/extensions/api/file_browser_handler_internal.json @@ -5,9 +5,6 @@ [ { "namespace": "fileBrowserHandlerInternal", - "compiler_options": { - "implemented_in": "chrome/browser/chromeos/extensions/file_browser_handler_api.h" - }, "nodoc": true, "internal": true, "platforms": ["chromeos"], diff --git a/chrome/common/extensions/api/i18n.json b/chrome/common/extensions/api/i18n.json index 765760d..db3c3f6 100644 --- a/chrome/common/extensions/api/i18n.json +++ b/chrome/common/extensions/api/i18n.json @@ -23,7 +23,6 @@ }, { "name": "getMessage", - "nocompile": true, "type": "function", "unprivileged": true, "description": "Gets the localized string for the specified message. If the message is missing, this method returns an empty string (''). If the format of the getMessage() call is wrong — for example, messageName is not a string or the substitutions array has more than 9 elements — this method returns undefined.", diff --git a/chrome/common/extensions/api/storage.json b/chrome/common/extensions/api/storage.json index a75de09..3e02335 100644 --- a/chrome/common/extensions/api/storage.json +++ b/chrome/common/extensions/api/storage.json @@ -6,9 +6,6 @@ { "namespace": "storage", "unprivileged": true, - "compiler_options": { - "generate_type_functions": true - }, "types": [ { "id": "StorageChange", diff --git a/chrome/common/extensions/api/windows.json b/chrome/common/extensions/api/windows.json index ee0702b..dbfe080 100644 --- a/chrome/common/extensions/api/windows.json +++ b/chrome/common/extensions/api/windows.json @@ -5,9 +5,6 @@ [ { "namespace": "windows", - "compiler_options": { - "implemented_in": "chrome/browser/extensions/api/tabs/tabs_api.h" - }, "dependencies": [ "tabs" ], "types": [ { diff --git a/chrome/common/extensions/docs/server2/api_data_source.py b/chrome/common/extensions/docs/server2/api_data_source.py index 5cb8824..8826768 100644 --- a/chrome/common/extensions/docs/server2/api_data_source.py +++ b/chrome/common/extensions/docs/server2/api_data_source.py @@ -14,10 +14,9 @@ import third_party.json_schema_compiler.idl_schema as idl_schema import third_party.json_schema_compiler.idl_parser as idl_parser # Increment this version when there are changes to the data stored in any of -# the caches used by APIDataSource. This would include changes to model.py in -# JSON schema compiler! This allows the cache to be invalidated without having -# to flush memcache on the production server. -_VERSION = 10 +# the caches used by APIDataSource. This allows the cache to be invalidated +# without having to flush memcache on the production server. +_VERSION = 9 def _RemoveNoDocs(item): if json_parse.IsDict(item): diff --git a/chrome/common/extensions_api_resources.grd b/chrome/common/extensions_api_resources.grd index 915a1c8..bbf2c0d 100644 --- a/chrome/common/extensions_api_resources.grd +++ b/chrome/common/extensions_api_resources.grd @@ -10,40 +10,57 @@ + + + + + + + + + + + + + + + + + @@ -51,10 +68,13 @@ + + + diff --git a/tools/json_schema_compiler/code.py b/tools/json_schema_compiler/code.py index e4326d4..07f6574 100644 --- a/tools/json_schema_compiler/code.py +++ b/tools/json_schema_compiler/code.py @@ -14,17 +14,15 @@ class Code(object): self._indent_size = indent_size self._comment_length = comment_length - def Append(self, line='', substitute=True, indent_level=None): + def Append(self, line='', substitute=True): """Appends a line of code at the current indent level or just a newline if line is not specified. Trailing whitespace is stripped. substitute: indicated whether this line should be affected by code.Substitute(). """ - if indent_level is None: - indent_level = self._indent_level - self._code.append(Line(((' ' * indent_level) + line).rstrip(), - substitute=substitute)) + self._code.append(Line(((' ' * self._indent_level) + line).rstrip(), + substitute=substitute)) return self def IsEmpty(self): diff --git a/tools/json_schema_compiler/compiler.py b/tools/json_schema_compiler/compiler.py index a347541..ae92373 100755 --- a/tools/json_schema_compiler/compiler.py +++ b/tools/json_schema_compiler/compiler.py @@ -69,16 +69,13 @@ def handle_single_schema(filename, dest_dir, root, root_namespace): referenced_api_defs = json_schema.Load(referenced_schema_path) for namespace in referenced_api_defs: - api_model.AddNamespace( - namespace, + api_model.AddNamespace(namespace, os.path.relpath(referenced_schema_path, opts.root)) # Gets the relative path from opts.root to the schema to correctly determine # the include path. relpath = os.path.relpath(schema, opts.root) - namespace = api_model.AddNamespace(target_namespace, - relpath, - include_compiler_options=True) + namespace = api_model.AddNamespace(target_namespace, relpath) if not namespace: continue @@ -133,9 +130,7 @@ def handle_bundle_schema(filenames, dest_dir, root, root_namespace): relpath = os.path.relpath(os.path.normpath(filenames[0]), root) for target_namespace, schema_filename in zip(api_defs, filenames): - namespace = api_model.AddNamespace(target_namespace, - relpath, - include_compiler_options=True) + namespace = api_model.AddNamespace(target_namespace, relpath) path, filename = os.path.split(schema_filename) short_filename, extension = os.path.splitext(filename) diff --git a/tools/json_schema_compiler/model.py b/tools/json_schema_compiler/model.py index 36be5a3..b583712 100644 --- a/tools/json_schema_compiler/model.py +++ b/tools/json_schema_compiler/model.py @@ -26,12 +26,10 @@ class Model(object): def __init__(self): self.namespaces = {} - def AddNamespace(self, json, source_file, include_compiler_options=False): + def AddNamespace(self, json, source_file): """Add a namespace's json to the model and returns the namespace. """ - namespace = Namespace(json, - source_file, - include_compiler_options=include_compiler_options) + namespace = Namespace(json, source_file) self.namespaces[namespace.name] = namespace return namespace @@ -44,28 +42,21 @@ class Namespace(object): - |source_file| the file that contained the namespace definition - |source_file_dir| the directory component of |source_file| - |source_file_filename| the filename component of |source_file| - - |platforms| if not None, the list of platforms that the namespace is - available to - |types| a map of type names to their model.Type - |functions| a map of function names to their model.Function - |events| a map of event names to their model.Function - |properties| a map of property names to their model.Property - - |compiler_options| the compiler_options dict, only present if - |include_compiler_options| is True """ - def __init__(self, json, source_file, include_compiler_options=False): + def __init__(self, json, source_file): self.name = json['namespace'] self.unix_name = UnixName(self.name) self.source_file = source_file self.source_file_dir, self.source_file_filename = os.path.split(source_file) self.parent = None - self.platforms = _GetPlatforms(json) _AddTypes(self, json, self) _AddFunctions(self, json, self) _AddEvents(self, json, self) _AddProperties(self, json, self) - if include_compiler_options: - self.compiler_options = json.get('compiler_options', {}) class Type(object): """A Type defined in the json. @@ -136,15 +127,13 @@ class Function(object): Properties: - |name| the function name - - |platforms| if not None, the list of platforms that the function is - available to - |params| a list of parameters to the function (order matters). A separate - parameter is used for each choice of a 'choices' parameter + parameter is used for each choice of a 'choices' parameter. - |description| a description of the function (if provided) - |callback| the callback parameter to the function. There should be exactly - one + one - |optional| whether the Function is "optional"; this only makes sense to be - present when the Function is representing a callback property + present when the Function is representing a callback property. - |simple_name| the name of this Function without a namespace """ def __init__(self, @@ -155,7 +144,6 @@ class Function(object): from_client=False): self.name = json['name'] self.simple_name = _StripNamespace(self.name, namespace) - self.platforms = _GetPlatforms(json) self.params = [] self.description = json.get('description') self.callback = None @@ -369,37 +357,23 @@ class Property(object): unix_name = property(GetUnixName, SetUnixName) -class _Enum(object): - """Superclass for enum types with a "name" field, setting up repr/eq/ne. - Enums need to do this so that equality/non-equality work over pickling. +class _PropertyTypeInfo(object): + """This class is not an inner class of |PropertyType| so it can be pickled. """ - - @staticmethod - def GetAll(cls): - """Yields all _Enum objects declared in |cls|. - """ - for prop_key in dir(cls): - prop_value = getattr(cls, prop_key) - if isinstance(prop_value, _Enum): - yield prop_value - - def __init__(self, name): + def __init__(self, is_fundamental, name): + self.is_fundamental = is_fundamental self.name = name - def __repr(self): + def __repr__(self): return self.name def __eq__(self, other): - return type(other) == type(self) and other.name == self.name + return isinstance(other, _PropertyTypeInfo) and self.name == other.name def __ne__(self, other): + # Yes. You seriously do need this. return not (self == other) -class _PropertyTypeInfo(_Enum): - def __init__(self, is_fundamental, name): - _Enum.__init__(self, name) - self.is_fundamental = is_fundamental - class PropertyType(object): """Enum of different types of properties/parameters. """ @@ -487,27 +461,3 @@ def _AddProperties(model, namespace, from_json=from_json, from_client=from_client) - -class _PlatformInfo(_Enum): - def __init__(self, name): - _Enum.__init__(self, name) - -class Platforms(object): - """Enum of the possible platforms. - """ - CHROMEOS = _PlatformInfo("chromeos") - CHROMEOS_TOUCH = _PlatformInfo("chromeos_touch") - LINUX = _PlatformInfo("linux") - MAC = _PlatformInfo("mac") - WIN = _PlatformInfo("win") - -def _GetPlatforms(json): - if 'platforms' not in json: - return None - platforms = [] - for platform_name in json['platforms']: - for platform_enum in _Enum.GetAll(Platforms): - if platform_name == platform_enum.name: - platforms.append(platform_enum) - break - return platforms diff --git a/tools/json_schema_compiler/schema_bundle_generator.py b/tools/json_schema_compiler/schema_bundle_generator.py index 3272be1..d391ea7 100644 --- a/tools/json_schema_compiler/schema_bundle_generator.py +++ b/tools/json_schema_compiler/schema_bundle_generator.py @@ -4,7 +4,6 @@ import code import cpp_util -from model import Platforms from schema_util import CapitalizeFirstLetter from schema_util import JsFunctionNameToClassName @@ -45,20 +44,6 @@ class SchemaBundleGenerator(object): c.Append() return c - def _GetPlatformIfdefs(self, model_object): - """Generates the "defined" conditional for an #if check if |model_object| - has platform restrictions. Returns None if there are no restrictions. - """ - if model_object.platforms is None: - return None - ifdefs = [] - for platform in model_object.platforms: - if platform == Platforms.CHROMEOS: - ifdefs.append('defined(OS_CHROMEOS)') - else: - raise ValueError("Unsupported platform ifdef: %s" % platform.name) - return ' and '.join(ifdefs) - def GenerateAPIHeader(self): """Generates the header for API registration / declaration""" c = code.Code() @@ -68,19 +53,9 @@ class SchemaBundleGenerator(object): c.Append('#include "base/basictypes.h"') for namespace in self._model.namespaces.values(): - ifdefs = self._GetPlatformIfdefs(namespace) - if ifdefs is not None: - c.Append("#if %s" % ifdefs, indent_level=0) - namespace_name = namespace.unix_name.replace("experimental_", "") - implementation_header = namespace.compiler_options.get( - "implemented_in", - "chrome/browser/extensions/api/%s/%s_api.h" % (namespace_name, - namespace_name)) - c.Append('#include "%s"' % implementation_header) - - if ifdefs is not None: - c.Append("#endif // %s" % ifdefs, indent_level=0) + c.Append('#include "chrome/browser/extensions/api/%s/%s_api.h"' % ( + namespace_name, namespace_name)) c.Append() c.Append("class ExtensionFunctionRegistry;") @@ -95,41 +70,20 @@ class SchemaBundleGenerator(object): c.Append() return self.GenerateHeader('generated_api', c) - def _GetNamespaceFunctions(self, namespace): - functions = list(namespace.functions.values()) - if namespace.compiler_options.get("generate_type_functions", False): - for type_ in namespace.types.values(): - functions += list(type_.functions.values()) - return functions - def GenerateFunctionRegistry(self): c = code.Code() c.Sblock("class GeneratedFunctionRegistry {") - c.Append(" public:") + c.Append("public:") c.Sblock("static void RegisterAll(ExtensionFunctionRegistry* registry) {") for namespace in self._model.namespaces.values(): - namespace_ifdefs = self._GetPlatformIfdefs(namespace) - if namespace_ifdefs is not None: - c.Append("#if %s" % namespace_ifdefs, indent_level=0) - namespace_name = CapitalizeFirstLetter(namespace.name.replace( "experimental.", "")) - for function in self._GetNamespaceFunctions(namespace): + for function in namespace.functions.values(): if function.nocompile: continue - function_ifdefs = self._GetPlatformIfdefs(function) - if function_ifdefs is not None: - c.Append("#if %s" % function_ifdefs, indent_level=0) - function_name = JsFunctionNameToClassName(namespace.name, function.name) c.Append("registry->RegisterFunction<%sFunction>();" % ( function_name)) - - if function_ifdefs is not None: - c.Append("#endif // %s" % function_ifdefs, indent_level=0) - - if namespace_ifdefs is not None: - c.Append("#endif // %s" % namespace_ifdefs, indent_level=0) c.Eblock("}") c.Eblock("};") c.Append() @@ -146,7 +100,7 @@ class SchemaBundleGenerator(object): c.Concat(self._cpp_type_generator.GetRootNamespaceStart()) c.Append() c.Sblock('class GeneratedSchemas {') - c.Append(' public:') + c.Append('public:') c.Append('// Puts all API schemas in |schemas|.') c.Append('static void Get(' 'std::map* schemas);') diff --git a/tools/json_schema_compiler/test/json_schema_compiler_tests.gyp b/tools/json_schema_compiler/test/json_schema_compiler_tests.gyp index 43ec909..f8e5dc3 100644 --- a/tools/json_schema_compiler/test/json_schema_compiler_tests.gyp +++ b/tools/json_schema_compiler/test/json_schema_compiler_tests.gyp @@ -9,9 +9,9 @@ 'type': 'static_library', 'variables': { 'chromium_code': 1, - 'schema_files': [ - 'additional_properties.json', + 'json_schema_files': [ 'any.json', + 'additional_properties.json', 'arrays.json', 'callbacks.json', 'choices.json', @@ -19,19 +19,22 @@ 'enums.json', 'functions_as_parameters.json', 'functions_on_types.json', - 'idl_basics.idl', - 'idl_object_types.idl', 'objects.json', 'simple_api.json', ], + 'idl_schema_files': [ + 'idl_basics.idl', + 'idl_object_types.idl' + ], 'cc_dir': 'tools/json_schema_compiler/test', 'root_namespace': 'test::api', }, 'inputs': [ - '<@(schema_files)', + '<@(idl_schema_files)', ], 'sources': [ - '<@(schema_files)', + '<@(json_schema_files)', + '<@(idl_schema_files)', ], 'includes': ['../../../build/json_schema_compile.gypi'], }, -- cgit v1.1