OLD | NEW |
---|---|
(Empty) | |
1 # Copyright 2016 The Chromium Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 import json | |
6 import os | |
7 import platform | |
8 import zipfile | |
9 | |
10 from catapult_base import cloud_storage | |
11 from profile_creators import fast_navigation_profile_extender | |
12 import page_sets | |
13 | |
14 # Remote directory to download extensions from in cloud storage. | |
15 REMOTE_DIR = "extension_set" | |
16 # Name of zip archive to download. | |
17 ZIP_NAME = "extensions.zip" | |
18 | |
19 | |
20 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.
| |
21 """ Exception thrown when remote archive is invalid or malformed. | |
22 | |
23 Remote archive should be located at REMOTE_DIR/ZIP_NAME. Upon failure, | |
24 Prompts user to update remote archive using update_remote_extensions | |
25 script. """ | |
26 | |
27 def __init__(self, msg=''): | |
28 msg += "\nTry running" + \ | |
29 "\n\tpython update_remote_extensions.py -e extension_set.csv\n" + \ | |
30 "in src/tools/perf/profile_creator subdirectory." | |
31 super(InvalidExtensionArchiveError, self).__init__(msg) | |
32 | |
33 | |
34 class ExtensionProfileExtender( | |
35 fast_navigation_profile_extender.FastNavigationProfileExtender): | |
36 """Creates a profile with many extensions. """ | |
37 | |
38 def __init__(self, finder_options): | |
39 maximum_batch_size = 1 | |
40 super(ExtensionProfileExtender, self).__init__( | |
41 finder_options, maximum_batch_size) | |
42 self._page_set = page_sets.BlankPageSet() | |
43 urls = [] | |
44 for story in self._page_set.stories: | |
45 urls.append(story.url) | |
46 self._navigation_urls = urls | |
47 finder_options.browser_options.disable_default_apps = False | |
48 | |
49 def _DownloadRemoteExtensions(self, remote_bucket, local_extensions_dir): | |
50 """ Downloads archive of common extensions to disk and unzips. | |
51 | |
52 Args: | |
53 remote_bucket: bucket to download remote archive from. | |
54 local_extensions_dir: directory to unzip archive into. | |
55 | |
56 Raises: | |
57 InvalidExtensionArchiveError if cannot find remote archive. | |
58 """ | |
59 remote_zip_path = os.path.join(REMOTE_DIR, ZIP_NAME) | |
60 local_zip_path = os.path.join(local_extensions_dir, ZIP_NAME) | |
61 try: | |
62 cloud_storage.Get(remote_bucket, remote_zip_path, | |
63 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
| |
64 except: | |
65 raise InvalidExtensionArchiveError("Can't find archive at gs://%s/%s." | |
66 % (remote_bucket, remote_zip_path)) | |
erikchen
2015/07/16 00:33:28
formatting
sydli
2015/07/16 00:53:13
Done.
| |
67 with zipfile.ZipFile(local_zip_path, "r") as extensions_zip: | |
68 extensions_zip.extractall(local_extensions_dir) | |
69 os.remove(local_zip_path) | |
70 | |
71 def _GetExtensionInfoFromCRX(self, crxfile): | |
72 """ Retrieves version + name of extension from CRX archive. """ | |
73 crx_zip = zipfile.ZipFile(crxfile) | |
74 manifest_contents = crx_zip.read('manifest.json') | |
75 decoded_manifest = json.loads(manifest_contents) | |
76 crx_version = decoded_manifest['version'] | |
77 extension_name = decoded_manifest['name'] | |
78 return (crx_version, extension_name) | |
79 | |
80 def _LoadExtensions(self, local_extensions_dir, profile_dir): | |
81 """ Loads extensions in _local_extensions_dir into user profile. | |
82 | |
83 Extensions are loaded according to platform specifications at | |
84 https://developer.chrome.com/extensions/external_extensions.html . | |
85 | |
86 Args: | |
87 local_extensions_dir: directory containing *CRX files. | |
88 profile_dir: user profile directory to load extensions into. | |
89 | |
90 Raises: | |
91 InvalidExtensionArchiveError if archive contains a non-CRX file. | |
92 """ | |
93 ext_files = os.listdir(local_extensions_dir) | |
94 external_ext_dir = os.path.join(profile_dir, "External Extensions") | |
95 os.makedirs(external_ext_dir) | |
96 for ext_file in ext_files: | |
97 ext_path = os.path.join(local_extensions_dir, ext_file) | |
98 if not ext_file.endswith(".crx"): | |
99 raise InvalidExtensionArchiveError("Archive contains non-crx file %s." | |
100 % ext_file) | |
101 (version, name) = self._GetExtensionInfoFromCRX(ext_path) | |
102 extension_info = { | |
103 'external_crx': ext_path, | |
104 'external_version': version, | |
105 '_comment': name | |
106 } | |
107 ext_id = os.path.splitext(os.path.basename(ext_path))[0] | |
108 extension_json_path = os.path.join(external_ext_dir, "%s.json" % ext_id) | |
109 with open(extension_json_path, 'w') as f: | |
110 f.write(json.dumps(extension_info)) | |
111 | |
112 def GetUrlIterator(self): | |
113 """Superclass override.""" | |
114 return iter(self._navigation_urls) | |
115 | |
116 def ShouldExitAfterBatchNavigation(self): | |
117 """Superclass override.""" | |
118 return False | |
119 | |
120 def Run(self): | |
121 # DL extensions from cloud & force-install extensions into profile. | |
122 if platform.system() != 'Darwin': | |
123 raise NotImplementedError('Extension profile generator on %s is not yet ' | |
124 'supported' % platform.system()) | |
125 local_extensions_dir = os.path.join(self.profile_path, | |
126 "external_extensions_crx") | |
127 self._DownloadRemoteExtensions(cloud_storage.PARTNER_BUCKET, | |
128 local_extensions_dir) | |
129 self._LoadExtensions(local_extensions_dir, self.profile_path) | |
130 super(ExtensionProfileExtender, self).Run() | |
131 | |
OLD | NEW |