Index: tools/perf/profile_creators/extension_profile_extender.py |
diff --git a/tools/perf/profile_creators/extension_profile_extender.py b/tools/perf/profile_creators/extension_profile_extender.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b94b241289c53d2b860664a53e494ed8dd2f3ec3 |
--- /dev/null |
+++ b/tools/perf/profile_creators/extension_profile_extender.py |
@@ -0,0 +1,131 @@ |
+# Copyright 2016 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. |
+ |
+import json |
+import os |
+import platform |
+import zipfile |
+ |
+from catapult_base import cloud_storage |
+from profile_creators import fast_navigation_profile_extender |
+import page_sets |
+ |
+# Remote directory to download extensions from in cloud storage. |
+REMOTE_DIR = "extension_set" |
+# Name of zip archive to download. |
+ZIP_NAME = "extensions.zip" |
+ |
+ |
+class InvalidExtensionArchiveError(Exception): |
erikchen
2015/07/16 00:33:28
this should inherit from exceptions.error, a telem
sydli
2015/07/16 00:53:14
Done.
|
+ """ Exception thrown when remote archive is invalid or malformed. |
+ |
+ Remote archive should be located at REMOTE_DIR/ZIP_NAME. Upon failure, |
+ Prompts user to update remote archive using update_remote_extensions |
+ script. """ |
+ |
+ def __init__(self, msg=''): |
+ msg += "\nTry running" + \ |
+ "\n\tpython update_remote_extensions.py -e extension_set.csv\n" + \ |
+ "in src/tools/perf/profile_creator subdirectory." |
+ super(InvalidExtensionArchiveError, self).__init__(msg) |
+ |
+ |
+class ExtensionProfileExtender( |
+ fast_navigation_profile_extender.FastNavigationProfileExtender): |
+ """Creates a profile with many extensions. """ |
+ |
+ def __init__(self, finder_options): |
+ maximum_batch_size = 1 |
+ super(ExtensionProfileExtender, self).__init__( |
+ finder_options, maximum_batch_size) |
+ self._page_set = page_sets.BlankPageSet() |
+ urls = [] |
+ for story in self._page_set.stories: |
+ urls.append(story.url) |
+ self._navigation_urls = urls |
+ finder_options.browser_options.disable_default_apps = False |
+ |
+ def _DownloadRemoteExtensions(self, remote_bucket, local_extensions_dir): |
+ """ Downloads archive of common extensions to disk and unzips. |
+ |
+ Args: |
+ remote_bucket: bucket to download remote archive from. |
+ local_extensions_dir: directory to unzip archive into. |
+ |
+ Raises: |
+ InvalidExtensionArchiveError if cannot find remote archive. |
+ """ |
+ remote_zip_path = os.path.join(REMOTE_DIR, ZIP_NAME) |
+ local_zip_path = os.path.join(local_extensions_dir, ZIP_NAME) |
+ try: |
+ cloud_storage.Get(remote_bucket, remote_zip_path, |
+ local_zip_path) |
erikchen
2015/07/16 00:33:28
I think this is incorrectly formatted.
sydli
2015/07/16 00:53:13
Done. Probably did this wrong everywhere else, so
|
+ except: |
+ raise InvalidExtensionArchiveError("Can't find archive at gs://%s/%s." |
+ % (remote_bucket, remote_zip_path)) |
erikchen
2015/07/16 00:33:28
formatting
sydli
2015/07/16 00:53:13
Done.
|
+ with zipfile.ZipFile(local_zip_path, "r") as extensions_zip: |
+ extensions_zip.extractall(local_extensions_dir) |
+ os.remove(local_zip_path) |
+ |
+ def _GetExtensionInfoFromCRX(self, crxfile): |
+ """ Retrieves version + name of extension from CRX archive. """ |
+ crx_zip = zipfile.ZipFile(crxfile) |
+ manifest_contents = crx_zip.read('manifest.json') |
+ decoded_manifest = json.loads(manifest_contents) |
+ crx_version = decoded_manifest['version'] |
+ extension_name = decoded_manifest['name'] |
+ return (crx_version, extension_name) |
+ |
+ def _LoadExtensions(self, local_extensions_dir, profile_dir): |
+ """ Loads extensions in _local_extensions_dir into user profile. |
+ |
+ Extensions are loaded according to platform specifications at |
+ https://developer.chrome.com/extensions/external_extensions.html . |
+ |
+ Args: |
+ local_extensions_dir: directory containing *CRX files. |
+ profile_dir: user profile directory to load extensions into. |
+ |
+ Raises: |
+ InvalidExtensionArchiveError if archive contains a non-CRX file. |
+ """ |
+ ext_files = os.listdir(local_extensions_dir) |
+ external_ext_dir = os.path.join(profile_dir, "External Extensions") |
+ os.makedirs(external_ext_dir) |
+ for ext_file in ext_files: |
+ ext_path = os.path.join(local_extensions_dir, ext_file) |
+ if not ext_file.endswith(".crx"): |
+ raise InvalidExtensionArchiveError("Archive contains non-crx file %s." |
+ % ext_file) |
+ (version, name) = self._GetExtensionInfoFromCRX(ext_path) |
+ extension_info = { |
+ 'external_crx': ext_path, |
+ 'external_version': version, |
+ '_comment': name |
+ } |
+ ext_id = os.path.splitext(os.path.basename(ext_path))[0] |
+ extension_json_path = os.path.join(external_ext_dir, "%s.json" % ext_id) |
+ with open(extension_json_path, 'w') as f: |
+ f.write(json.dumps(extension_info)) |
+ |
+ def GetUrlIterator(self): |
+ """Superclass override.""" |
+ return iter(self._navigation_urls) |
+ |
+ def ShouldExitAfterBatchNavigation(self): |
+ """Superclass override.""" |
+ return False |
+ |
+ def Run(self): |
+ # DL extensions from cloud & force-install extensions into profile. |
+ if platform.system() != 'Darwin': |
+ raise NotImplementedError('Extension profile generator on %s is not yet ' |
+ 'supported' % platform.system()) |
+ local_extensions_dir = os.path.join(self.profile_path, |
+ "external_extensions_crx") |
+ self._DownloadRemoteExtensions(cloud_storage.PARTNER_BUCKET, |
+ local_extensions_dir) |
+ self._LoadExtensions(local_extensions_dir, self.profile_path) |
+ super(ExtensionProfileExtender, self).Run() |
+ |