content_providers.py revision f2477e01787aa58f445919b809d89e252beef54f
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.
4
5import logging
6from operator import itemgetter
7import posixpath
8
9from chroot_file_system import ChrootFileSystem
10from content_provider import ContentProvider
11from extensions_paths import CONTENT_PROVIDERS
12from future import Gettable, Future
13from third_party.json_schema_compiler.memoize import memoize
14
15
16class ContentProviders(object):
17  '''Implements the content_providers.json configuration; see
18  chrome/common/extensions/docs/templates/json/content_providers.json for its
19  current state and a description of the format.
20
21  Returns ContentProvider instances based on how they're configured there.
22  '''
23
24  def __init__(self,
25               compiled_fs_factory,
26               host_file_system,
27               github_file_system_provider):
28    self._compiled_fs_factory = compiled_fs_factory
29    self._host_file_system = host_file_system
30    self._github_file_system_provider = github_file_system_provider
31    self._cache = compiled_fs_factory.ForJson(host_file_system)
32
33  @memoize
34  def GetByName(self, name):
35    '''Gets the ContentProvider keyed by |name| in content_providers.json, or
36    None of there is no such content provider.
37    '''
38    config = self._GetConfig().get(name)
39    if config is None:
40      logging.error('No content provider found with name "%s"' % name)
41      return None
42    return self._CreateContentProvider(name, config)
43
44  @memoize
45  def GetByServeFrom(self, path):
46    '''Gets a (content_provider, path_in_content_provider) tuple, where
47    content_provider is the ContentProvider with the longest "serveFrom"
48    property that is a subpath of |path|, and path_in_content_provider is the
49    remainder of |path|.
50
51    For example, if content provider A serves from "foo" and content provider B
52    serves from "foo/bar", GetByServeFrom("foo/bar/baz") will return (B, "baz").
53
54    Returns (None, |path|) if no ContentProvider serves from |path|.
55    '''
56    serve_from_to_config = dict(
57        (config['serveFrom'], (name, config))
58        for name, config in self._GetConfig().iteritems())
59    path_parts = path.split('/')
60    for i in xrange(len(path_parts), -1, -1):
61      name_and_config = serve_from_to_config.get('/'.join(path_parts[:i]))
62      if name_and_config is not None:
63        return (self._CreateContentProvider(name_and_config[0],
64                                            name_and_config[1]),
65                '/'.join(path_parts[i:]))
66    return None, path
67
68  def _GetConfig(self):
69    return self._cache.GetFromFile(CONTENT_PROVIDERS).Get()
70
71  def _CreateContentProvider(self, name, config):
72    supports_templates = config.get('supportsTemplates', False)
73    supports_zip = config.get('supportsZip', False)
74
75    if 'chromium' in config:
76      chromium_config = config['chromium']
77      if 'dir' not in chromium_config:
78        logging.error('%s: "chromium" must have a "dir" property' % name)
79        return None
80      file_system = ChrootFileSystem(self._host_file_system,
81                                     chromium_config['dir'])
82    elif 'github' in config:
83      github_config = config['github']
84      if 'owner' not in github_config or 'repo' not in github_config:
85        logging.error('%s: "github" must provide an "owner" and "repo"' % name)
86        return None
87      file_system = self._github_file_system_provider.Create(
88          github_config['owner'], github_config['repo'])
89      if 'dir' in github_config:
90        file_system = ChrootFileSystem(file_system, github_config['dir'])
91    else:
92      logging.error(
93          '%s: content provider type "%s" not supported' % (name, type_))
94      return None
95
96    return ContentProvider(name,
97                           self._compiled_fs_factory,
98                           file_system,
99                           supports_templates=supports_templates,
100                           supports_zip=supports_zip)
101
102  def Cron(self):
103    futures = [self._CreateContentProvider(name, config).Cron()
104               for name, config in self._GetConfig().iteritems()]
105    return Future(delegate=Gettable(lambda: [f.Get() for f in futures]))
106