Home | History | Annotate | Download | only in server2
      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 
      5 import logging
      6 import traceback
      7 
      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
     13 
     14 
     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.
     19 
     20   Returns ContentProvider instances based on how they're configured there.
     21   '''
     22 
     23   def __init__(self,
     24                compiled_fs_factory,
     25                host_file_system,
     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)
     31 
     32   @memoize
     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.
     36     '''
     37     config = self._GetConfig().get(name)
     38     if config is None:
     39       logging.error('No content provider found with name "%s"' % name)
     40       return None
     41     return self._CreateContentProvider(name, config)
     42 
     43   @memoize
     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
     48     remainder of |path|.
     49 
     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").
     52 
     53     Returns (None, |path|) if no ContentProvider serves from |path|.
     54     '''
     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],
     63                                             name_and_config[1]),
     64                 '/'.join(path_parts[i:]))
     65     return None, path
     66 
     67   def _GetConfig(self):
     68     return self._cache.GetFromFile(CONTENT_PROVIDERS).Get()
     69 
     70   def _CreateContentProvider(self, name, config):
     71     supports_templates = config.get('supportsTemplates', False)
     72     supports_zip = config.get('supportsZip', False)
     73 
     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)
     78         return None
     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)
     85         return None
     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'])
     90     else:
     91       logging.error(
     92           '%s: content provider type "%s" not supported' % (name, type_))
     93       return None
     94 
     95     return ContentProvider(name,
     96                            self._compiled_fs_factory,
     97                            file_system,
     98                            supports_templates=supports_templates,
     99                            supports_zip=supports_zip)
    100 
    101   def Cron(self):
    102     def safe(name, action, callback):
    103       '''Safely runs |callback| for a ContentProvider called |name|. It's
    104       important to run all ContentProvider Cron's even if some of them fail.
    105       '''
    106       try:
    107         return callback()
    108       except:
    109         logging.error('Error %s Cron for ContentProvider "%s": %s' %
    110                       (action, name, traceback.format_exc()))
    111         return None
    112 
    113     futures = [(name, safe(name,
    114                            'initializing',
    115                            self._CreateContentProvider(name, config).Cron))
    116                for name, config in self._GetConfig().iteritems()]
    117     return Future(delegate=Gettable(
    118         lambda: [safe(name, 'resolving', f.Get) for name, f in futures if f]))
    119