10f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)# Copyright 2013 The Chromium Authors. All rights reserved.
20f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
30f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)# found in the LICENSE file.
40f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
50f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)import logging
65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import os
75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import traceback
80f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
90f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)from chroot_file_system import ChrootFileSystem
100f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)from content_provider import ContentProvider
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import environment
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from extensions_paths import CONTENT_PROVIDERS, LOCAL_DEBUG_DIR
13effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochfrom future import Future
1403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)from gitiles_file_system import GitilesFileSystem
155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from local_file_system import LocalFileSystem
160f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)from third_party.json_schema_compiler.memoize import memoize
170f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
180f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)_IGNORE_MISSING_CONTENT_PROVIDERS = [False]
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def IgnoreMissingContentProviders(fn):
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  '''Decorates |fn| to ignore missing content providers during its run.
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  '''
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def run(*args, **optargs):
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    saved = _IGNORE_MISSING_CONTENT_PROVIDERS[0]
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    _IGNORE_MISSING_CONTENT_PROVIDERS[0] = True
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    try:
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return fn(*args, **optargs)
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    finally:
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      _IGNORE_MISSING_CONTENT_PROVIDERS[0] = saved
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return run
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
350f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)class ContentProviders(object):
360f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  '''Implements the content_providers.json configuration; see
370f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  chrome/common/extensions/docs/templates/json/content_providers.json for its
380f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  current state and a description of the format.
390f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
400f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  Returns ContentProvider instances based on how they're configured there.
410f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  '''
420f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
43f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def __init__(self,
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)               object_store_creator,
45f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)               compiled_fs_factory,
46f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)               host_file_system,
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)               github_file_system_provider,
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)               gcs_file_system_provider):
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self._object_store_creator = object_store_creator
500f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    self._compiled_fs_factory = compiled_fs_factory
510f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    self._host_file_system = host_file_system
52f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self._github_file_system_provider = github_file_system_provider
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self._gcs_file_system_provider = gcs_file_system_provider
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self._cache = None
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # If running the devserver and there is a LOCAL_DEBUG_DIR, we
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # will read the content_provider configuration from there instead
581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    # of fetching it from Gitiles or patch.
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if environment.IsDevServer() and os.path.exists(LOCAL_DEBUG_DIR):
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      local_fs = LocalFileSystem(LOCAL_DEBUG_DIR)
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      conf_stat = None
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      try:
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        conf_stat = local_fs.Stat(CONTENT_PROVIDERS)
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      except:
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        pass
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if conf_stat:
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        logging.warn(("Using local debug folder (%s) for "
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                      "content_provider.json configuration") % LOCAL_DEBUG_DIR)
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self._cache = compiled_fs_factory.ForJson(local_fs)
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if not self._cache:
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      self._cache = compiled_fs_factory.ForJson(host_file_system)
740f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
750f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  @memoize
760f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  def GetByName(self, name):
770f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    '''Gets the ContentProvider keyed by |name| in content_providers.json, or
780f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    None of there is no such content provider.
790f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    '''
800f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    config = self._GetConfig().get(name)
810f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    if config is None:
820f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      logging.error('No content provider found with name "%s"' % name)
830f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      return None
840f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    return self._CreateContentProvider(name, config)
850f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
860f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  @memoize
870f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  def GetByServeFrom(self, path):
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    '''Gets a (content_provider, serve_from, path_in_content_provider) tuple,
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    where content_provider is the ContentProvider with the longest "serveFrom"
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    property that is a subpath of |path|, serve_from is that property, and
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    path_in_content_provider is the remainder of |path|.
920f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
930f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    For example, if content provider A serves from "foo" and content provider B
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    serves from "foo/bar", GetByServeFrom("foo/bar/baz") will return (B,
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    "foo/bar", "baz").
960f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    Returns (None, '', |path|) if no ContentProvider serves from |path|.
980f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    '''
990f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    serve_from_to_config = dict(
1000f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)        (config['serveFrom'], (name, config))
1010f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)        for name, config in self._GetConfig().iteritems())
1020f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    path_parts = path.split('/')
1030f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    for i in xrange(len(path_parts), -1, -1):
1040f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      name_and_config = serve_from_to_config.get('/'.join(path_parts[:i]))
1050f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      if name_and_config is not None:
1060f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)        return (self._CreateContentProvider(name_and_config[0],
1070f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)                                            name_and_config[1]),
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                '/'.join(path_parts[:i]),
1090f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)                '/'.join(path_parts[i:]))
1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return None, '', path
1110f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
1120f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  def _GetConfig(self):
113f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return self._cache.GetFromFile(CONTENT_PROVIDERS).Get()
1140f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
1150f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  def _CreateContentProvider(self, name, config):
1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    default_extensions = config.get('defaultExtensions', ())
1170f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    supports_templates = config.get('supportsTemplates', False)
1180f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    supports_zip = config.get('supportsZip', False)
1190f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
1200f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    if 'chromium' in config:
1210f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      chromium_config = config['chromium']
1220f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      if 'dir' not in chromium_config:
123f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        logging.error('%s: "chromium" must have a "dir" property' % name)
1240f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)        return None
1250f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      file_system = ChrootFileSystem(self._host_file_system,
1261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1270f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)                                     chromium_config['dir'])
1281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    # TODO(rockot): Remove this in a future patch. It should not be needed once
1291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    # the new content_providers.json is committed.
13003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    elif 'gitiles' in config:
1311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      chromium_config = config['gitiles']
1321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      if 'dir' not in chromium_config:
1331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        logging.error('%s: "chromium" must have a "dir" property' % name)
13403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        return None
1351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      file_system = ChrootFileSystem(self._host_file_system,
1361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                     chromium_config['dir'])
1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    elif 'gcs' in config:
1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      gcs_config = config['gcs']
1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if 'bucket' not in gcs_config:
1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        logging.error('%s: "gcs" must have a "bucket" property' % name)
1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return None
1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      bucket = gcs_config['bucket']
1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if not bucket.startswith('gs://'):
1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        logging.error('%s: bucket %s should start with gs://' % (name, bucket))
1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return None
1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      bucket = bucket[len('gs://'):]
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      file_system = self._gcs_file_system_provider.Create(bucket)
1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if 'dir' in gcs_config:
1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        file_system = ChrootFileSystem(file_system, gcs_config['dir'])
1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
151f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    elif 'github' in config:
152f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      github_config = config['github']
153f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      if 'owner' not in github_config or 'repo' not in github_config:
154f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        logging.error('%s: "github" must provide an "owner" and "repo"' % name)
155f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        return None
156f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      file_system = self._github_file_system_provider.Create(
157f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          github_config['owner'], github_config['repo'])
158f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      if 'dir' in github_config:
159f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        file_system = ChrootFileSystem(file_system, github_config['dir'])
1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1610f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    else:
1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      logging.error('%s: content provider type not supported' % name)
1630f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      return None
1640f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
1650f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    return ContentProvider(name,
1660f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)                           self._compiled_fs_factory,
1670f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)                           file_system,
1685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                           self._object_store_creator,
1695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                           default_extensions=default_extensions,
1700f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)                           supports_templates=supports_templates,
1710f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)                           supports_zip=supports_zip)
1720f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
1731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  def GetRefreshPaths(self):
1741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return self._GetConfig().keys()
1751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  def Refresh(self, path):
1775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def safe(name, action, callback):
1785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      '''Safely runs |callback| for a ContentProvider called |name| by
1795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      swallowing exceptions and turning them into a None return value. It's
1801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      important to run all ContentProvider Refreshes even if some of them fail.
1815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      '''
1825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      try:
1835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return callback()
1845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      except:
1855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if not _IGNORE_MISSING_CONTENT_PROVIDERS[0]:
1861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          logging.error('Error %s Refresh for ContentProvider "%s":\n%s' %
1875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        (action, name, traceback.format_exc()))
1885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return None
1895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    config = self._GetConfig()[path]
1911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    provider = self._CreateContentProvider(path, config)
1921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    future = safe(path,
1931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                  'initializing',
1941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                  self._CreateContentProvider(path, config).Refresh)
1951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if future is None:
1961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return Future(callback=lambda: True)
1971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return Future(callback=lambda: safe(path, 'resolving', future.Get))
198