1 # Copyright 2014 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.
10 from compiled_file_system
import Cache
11 from extensions_paths
import EXAMPLES
12 from samples_data_source
import SamplesDataSource
13 import third_party
.json_schema_compiler
.json_comment_eater
as json_comment_eater
17 _DEFAULT_ICON_PATH
= 'images/sample-default-icon.png'
20 def _GetAPIItems(js_file
):
21 chrome_pattern
= r
'chrome[\w.]+'
22 # Add API calls that appear normally, like "chrome.runtime.connect".
23 calls
= set(re
.findall(chrome_pattern
, js_file
))
24 # Add API calls that have been assigned into variables, like
25 # "var storageArea = chrome.storage.sync; storageArea.get", which should
26 # be expanded like "chrome.storage.sync.get".
27 for match
in re
.finditer(r
'var\s+(\w+)\s*=\s*(%s);' % chrome_pattern
,
29 var_name
, api_prefix
= match
.groups()
30 for var_match
in re
.finditer(r
'\b%s\.([\w.]+)\b' % re
.escape(var_name
),
32 api_suffix
, = var_match
.groups()
33 calls
.add('%s.%s' % (api_prefix
, api_suffix
))
37 class SamplesModel(object):
39 extension_samples_file_system
,
40 app_samples_file_system
,
45 self
._samples
_fs
= (extension_samples_file_system
if
46 platform
== 'extensions' else app_samples_file_system
)
47 self
._samples
_cache
= compiled_fs_factory
.Create(
49 self
._MakeSamplesList
,
52 self
._text
_cache
= compiled_fs_factory
.ForUnicode(self
._samples
_fs
)
53 self
._reference
_resolver
= reference_resolver
54 self
._base
_path
= base_path
55 self
._platform
= platform
58 return self
._samples
_cache
60 def FilterSamples(self
, api_name
):
61 '''Fetches and filters the list of samples for this platform, returning
62 a Future to the only the samples that use the API |api_name|.
64 def filter_samples(samples_list
):
65 return [sample
for sample
in samples_list
66 if any(call
['name'].startswith(api_name
+ '.')
67 for call
in sample
['api_calls'])]
69 # TODO(rockot): This cache is probably not working as intended, since
70 # it can still lead to underlying filesystem (e.g. gitiles) access
71 # while processing live requests. Because this can fail, we at least
72 # trap and log exceptions to prevent 500s from being thrown.
73 logging
.warning('Unable to get samples listing. Skipping.')
75 platform_for_samples
= '' if self
._platform
== 'apps' else EXAMPLES
76 return (self
._samples
_cache
.GetFromFileListing(platform_for_samples
)
77 .Then(filter_samples
, error_handler
=handle_error
))
79 def _GetDataFromManifest(self
, path
, file_system
):
80 manifest
= self
._text
_cache
.GetFromFile(path
+ '/manifest.json').Get()
82 manifest_json
= json
.loads(json_comment_eater
.Nom(manifest
))
83 except ValueError as e
:
84 logging
.error('Error parsing manifest.json for %s: %s' % (path
, e
))
87 'name': manifest_json
.get('name', ''),
88 'description': manifest_json
.get('description', None),
89 'icon': manifest_json
.get('icons', {}).get('128', None),
90 'default_locale': manifest_json
.get('default_locale', None),
93 if not l10n_data
['default_locale']:
95 locales_path
= path
+ '/_locales/'
96 locales_dir
= file_system
.ReadSingle(locales_path
).Get()
98 def load_locale_json(path
):
99 return (path
, json
.loads(self
._text
_cache
.GetFromFile(path
).Get()))
102 locales_json
= [load_locale_json(locales_path
+ f
+ 'messages.json')
103 for f
in locales_dir
]
104 except ValueError as e
:
105 logging
.error('Error parsing locales files for %s: %s' % (path
, e
))
107 for path
, json_
in locales_json
:
108 l10n_data
['locales'][path
[len(locales_path
):].split('/')[0]] = json_
112 def _MakeSamplesList(self
, base_path
, files
):
114 for filename
in sorted(files
):
115 if filename
.rsplit('/')[-1] != 'manifest.json':
118 # This is a little hacky, but it makes a sample page.
119 sample_path
= filename
.rsplit('/', 1)[-2]
120 sample_files
= [path
for path
in files
121 if path
.startswith(sample_path
+ '/')]
122 js_files
= [path
for path
in sample_files
if path
.endswith('.js')]
123 js_contents
= [self
._text
_cache
.GetFromFile(
124 posixpath
.join(base_path
, js_file
)).Get()
125 for js_file
in js_files
]
127 for js
in js_contents
:
128 api_items
.update(_GetAPIItems(js
))
131 for item
in sorted(api_items
):
132 if len(item
.split('.')) < 3:
134 if item
.endswith('.removeListener') or item
.endswith('.hasListener'):
136 if item
.endswith('.addListener'):
137 item
= item
[:-len('.addListener')]
138 if item
.startswith('chrome.'):
139 item
= item
[len('chrome.'):]
140 ref_data
= self
._reference
_resolver
.GetLink(item
)
141 # TODO(kalman): What about references like chrome.storage.sync.get?
142 # That should link to either chrome.storage.sync or
143 # chrome.storage.StorageArea.get (or probably both).
144 # TODO(kalman): Filter out API-only references? This can happen when
145 # the API namespace is assigned to a variable, but it's very hard to
150 'name': ref_data
['text'],
151 'link': ref_data
['href']
154 if self
._platform
== 'apps':
155 url
= url_constants
.GITHUB_BASE
+ '/' + sample_path
156 icon_base
= url_constants
.RAW_GITHUB_BASE
+ '/' + sample_path
159 extension_sample_path
= posixpath
.join('examples', sample_path
)
160 url
= extension_sample_path
161 icon_base
= extension_sample_path
162 download_url
= extension_sample_path
+ '.zip'
164 manifest_data
= self
._GetDataFromManifest
(
165 posixpath
.join(base_path
, sample_path
),
167 if manifest_data
['icon'] is None:
168 icon_path
= posixpath
.join(
169 self
._base
_path
, 'static', _DEFAULT_ICON_PATH
)
171 icon_path
= '%s/%s' % (icon_base
, manifest_data
['icon'])
172 manifest_data
.update({
174 'download_url': download_url
,
176 'files': [f
.replace(sample_path
+ '/', '') for f
in sample_files
],
177 'api_calls': api_calls
179 samples_list
.append(manifest_data
)