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