1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
|
# Copyright 2013 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 logging
import os
import traceback
from chroot_file_system import ChrootFileSystem
from content_provider import ContentProvider
import environment
from extensions_paths import CONTENT_PROVIDERS, LOCAL_DEBUG_DIR
from future import Future
from local_file_system import LocalFileSystem
from third_party.json_schema_compiler.memoize import memoize
_IGNORE_MISSING_CONTENT_PROVIDERS = [False]
def IgnoreMissingContentProviders(fn):
'''Decorates |fn| to ignore missing content providers during its run.
'''
def run(*args, **optargs):
saved = _IGNORE_MISSING_CONTENT_PROVIDERS[0]
_IGNORE_MISSING_CONTENT_PROVIDERS[0] = True
try:
return fn(*args, **optargs)
finally:
_IGNORE_MISSING_CONTENT_PROVIDERS[0] = saved
return run
class ContentProviders(object):
'''Implements the content_providers.json configuration; see
chrome/common/extensions/docs/templates/json/content_providers.json for its
current state and a description of the format.
Returns ContentProvider instances based on how they're configured there.
'''
def __init__(self,
object_store_creator,
compiled_fs_factory,
host_file_system,
github_file_system_provider,
gcs_file_system_provider):
self._object_store_creator = object_store_creator
self._compiled_fs_factory = compiled_fs_factory
self._host_file_system = host_file_system
self._github_file_system_provider = github_file_system_provider
self._gcs_file_system_provider = gcs_file_system_provider
self._cache = None
# If running the devserver and there is a LOCAL_DEBUG_DIR, we
# will read the content_provider configuration from there instead
# of fetching it from SVN trunk or patch.
if environment.IsDevServer() and os.path.exists(LOCAL_DEBUG_DIR):
local_fs = LocalFileSystem(LOCAL_DEBUG_DIR)
conf_stat = None
try:
conf_stat = local_fs.Stat(CONTENT_PROVIDERS)
except:
pass
if conf_stat:
logging.warn(("Using local debug folder (%s) for "
"content_provider.json configuration") % LOCAL_DEBUG_DIR)
self._cache = compiled_fs_factory.ForJson(local_fs)
if not self._cache:
self._cache = compiled_fs_factory.ForJson(host_file_system)
@memoize
def GetByName(self, name):
'''Gets the ContentProvider keyed by |name| in content_providers.json, or
None of there is no such content provider.
'''
config = self._GetConfig().get(name)
if config is None:
logging.error('No content provider found with name "%s"' % name)
return None
return self._CreateContentProvider(name, config)
@memoize
def GetByServeFrom(self, path):
'''Gets a (content_provider, serve_from, path_in_content_provider) tuple,
where content_provider is the ContentProvider with the longest "serveFrom"
property that is a subpath of |path|, serve_from is that property, and
path_in_content_provider is the remainder of |path|.
For example, if content provider A serves from "foo" and content provider B
serves from "foo/bar", GetByServeFrom("foo/bar/baz") will return (B,
"foo/bar", "baz").
Returns (None, '', |path|) if no ContentProvider serves from |path|.
'''
serve_from_to_config = dict(
(config['serveFrom'], (name, config))
for name, config in self._GetConfig().iteritems())
path_parts = path.split('/')
for i in xrange(len(path_parts), -1, -1):
name_and_config = serve_from_to_config.get('/'.join(path_parts[:i]))
if name_and_config is not None:
return (self._CreateContentProvider(name_and_config[0],
name_and_config[1]),
'/'.join(path_parts[:i]),
'/'.join(path_parts[i:]))
return None, '', path
def _GetConfig(self):
return self._cache.GetFromFile(CONTENT_PROVIDERS).Get()
def _CreateContentProvider(self, name, config):
default_extensions = config.get('defaultExtensions', ())
supports_templates = config.get('supportsTemplates', False)
supports_zip = config.get('supportsZip', False)
if 'chromium' in config:
chromium_config = config['chromium']
if 'dir' not in chromium_config:
logging.error('%s: "chromium" must have a "dir" property' % name)
return None
file_system = ChrootFileSystem(self._host_file_system,
chromium_config['dir'])
elif 'gcs' in config:
gcs_config = config['gcs']
if 'bucket' not in gcs_config:
logging.error('%s: "gcs" must have a "bucket" property' % name)
return None
bucket = gcs_config['bucket']
if not bucket.startswith('gs://'):
logging.error('%s: bucket %s should start with gs://' % (name, bucket))
return None
bucket = bucket[len('gs://'):]
file_system = self._gcs_file_system_provider.Create(bucket)
if 'dir' in gcs_config:
file_system = ChrootFileSystem(file_system, gcs_config['dir'])
elif 'github' in config:
github_config = config['github']
if 'owner' not in github_config or 'repo' not in github_config:
logging.error('%s: "github" must provide an "owner" and "repo"' % name)
return None
file_system = self._github_file_system_provider.Create(
github_config['owner'], github_config['repo'])
if 'dir' in github_config:
file_system = ChrootFileSystem(file_system, github_config['dir'])
else:
logging.error('%s: content provider type not supported' % name)
return None
return ContentProvider(name,
self._compiled_fs_factory,
file_system,
self._object_store_creator,
default_extensions=default_extensions,
supports_templates=supports_templates,
supports_zip=supports_zip)
def Cron(self):
def safe(name, action, callback):
'''Safely runs |callback| for a ContentProvider called |name| by
swallowing exceptions and turning them into a None return value. It's
important to run all ContentProvider Crons even if some of them fail.
'''
try:
return callback()
except:
if not _IGNORE_MISSING_CONTENT_PROVIDERS[0]:
logging.error('Error %s Cron for ContentProvider "%s":\n%s' %
(action, name, traceback.format_exc()))
return None
futures = [(name, safe(name,
'initializing',
self._CreateContentProvider(name, config).Cron))
for name, config in self._GetConfig().iteritems()]
return Future(callback=
lambda: [safe(name, 'resolving', f.Get) for name, f in futures if f])
|