1 # Copyright 2013 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.
8 from chroot_file_system
import ChrootFileSystem
9 from content_provider
import ContentProvider
10 from extensions_paths
import CONTENT_PROVIDERS
11 from future
import Gettable
, Future
12 from third_party
.json_schema_compiler
.memoize
import memoize
15 class ContentProviders(object):
16 '''Implements the content_providers.json configuration; see
17 chrome/common/extensions/docs/templates/json/content_providers.json for its
18 current state and a description of the format.
20 Returns ContentProvider instances based on how they're configured there.
26 github_file_system_provider
):
27 self
._compiled
_fs
_factory
= compiled_fs_factory
28 self
._host
_file
_system
= host_file_system
29 self
._github
_file
_system
_provider
= github_file_system_provider
30 self
._cache
= compiled_fs_factory
.ForJson(host_file_system
)
33 def GetByName(self
, name
):
34 '''Gets the ContentProvider keyed by |name| in content_providers.json, or
35 None of there is no such content provider.
37 config
= self
._GetConfig
().get(name
)
39 logging
.error('No content provider found with name "%s"' % name
)
41 return self
._CreateContentProvider
(name
, config
)
44 def GetByServeFrom(self
, path
):
45 '''Gets a (content_provider, path_in_content_provider) tuple, where
46 content_provider is the ContentProvider with the longest "serveFrom"
47 property that is a subpath of |path|, and path_in_content_provider is the
50 For example, if content provider A serves from "foo" and content provider B
51 serves from "foo/bar", GetByServeFrom("foo/bar/baz") will return (B, "baz").
53 Returns (None, |path|) if no ContentProvider serves from |path|.
55 serve_from_to_config
= dict(
56 (config
['serveFrom'], (name
, config
))
57 for name
, config
in self
._GetConfig
().iteritems())
58 path_parts
= path
.split('/')
59 for i
in xrange(len(path_parts
), -1, -1):
60 name_and_config
= serve_from_to_config
.get('/'.join(path_parts
[:i
]))
61 if name_and_config
is not None:
62 return (self
._CreateContentProvider
(name_and_config
[0],
64 '/'.join(path_parts
[i
:]))
68 return self
._cache
.GetFromFile(CONTENT_PROVIDERS
).Get()
70 def _CreateContentProvider(self
, name
, config
):
71 supports_templates
= config
.get('supportsTemplates', False)
72 supports_zip
= config
.get('supportsZip', False)
74 if 'chromium' in config
:
75 chromium_config
= config
['chromium']
76 if 'dir' not in chromium_config
:
77 logging
.error('%s: "chromium" must have a "dir" property' % name
)
79 file_system
= ChrootFileSystem(self
._host
_file
_system
,
80 chromium_config
['dir'])
81 elif 'github' in config
:
82 github_config
= config
['github']
83 if 'owner' not in github_config
or 'repo' not in github_config
:
84 logging
.error('%s: "github" must provide an "owner" and "repo"' % name
)
86 file_system
= self
._github
_file
_system
_provider
.Create(
87 github_config
['owner'], github_config
['repo'])
88 if 'dir' in github_config
:
89 file_system
= ChrootFileSystem(file_system
, github_config
['dir'])
92 '%s: content provider type "%s" not supported' % (name
, type_
))
95 return ContentProvider(name
,
96 self
._compiled
_fs
_factory
,
98 supports_templates
=supports_templates
,
99 supports_zip
=supports_zip
)
102 def safe(name
, action
, callback
):
103 '''Safely runs |callback| for a ContentProvider called |name| by
104 swallowing exceptions and turning them into a None return value. It's
105 important to run all ContentProvider Crons even if some of them fail.
110 logging
.error('Error %s Cron for ContentProvider "%s":\n%s' %
111 (action
, name
, traceback
.format_exc()))
114 futures
= [(name
, safe(name
,
116 self
._CreateContentProvider
(name
, config
).Cron
))
117 for name
, config
in self
._GetConfig
().iteritems()]
118 return Future(delegate
=Gettable(
119 lambda: [safe(name
, 'resolving', f
.Get
) for name
, f
in futures
if f
]))