Suppress tabs permission warning if there is already a browsingHistory warning.
[chromium-blink-merge.git] / chrome / common / extensions / docs / server2 / path_canonicalizer.py
blob03a22f5980307f3595c98db76e311a71b07407e4
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.
5 from collections import defaultdict
6 import posixpath
8 from future import Future
9 from path_util import SplitParent
10 from special_paths import SITE_VERIFICATION_FILE
12 def _Normalize(file_name, splittext=False):
13 normalized = file_name
14 if splittext:
15 normalized = posixpath.splitext(file_name)[0]
16 normalized = normalized.replace('.', '').replace('-', '').replace('_', '')
17 return normalized.lower()
20 class PathCanonicalizer(object):
21 '''Transforms paths into their canonical forms. Since the docserver has had
22 many incarnations - e.g. there didn't use to be apps/ - there may be old
23 paths lying around the webs. We try to redirect those to where they are now.
24 '''
25 def __init__(self,
26 file_system,
27 object_store_creator,
28 strip_extensions):
29 # |strip_extensions| is a list of file extensions (e.g. .html) that should
30 # be stripped for a path's canonical form.
31 self._cache = object_store_creator.Create(
32 PathCanonicalizer, category=file_system.GetIdentity())
33 self._file_system = file_system
34 self._strip_extensions = strip_extensions
36 def _LoadCache(self):
37 cached_future = self._cache.GetMulti(('canonical_paths',
38 'simplified_paths_map'))
40 def resolve():
41 # |canonical_paths| is the pre-calculated set of canonical paths.
42 # |simplified_paths_map| is a lazily populated mapping of simplified file
43 # names to a list of full paths that contain them. For example,
44 # - browseraction: [extensions/browserAction.html]
45 # - storage: [apps/storage.html, extensions/storage.html]
46 cached = cached_future.Get()
47 canonical_paths, simplified_paths_map = (
48 cached.get('canonical_paths'), cached.get('simplified_paths_map'))
50 if canonical_paths is None:
51 assert simplified_paths_map is None
52 canonical_paths = set()
53 simplified_paths_map = defaultdict(list)
54 for base, dirs, files in self._file_system.Walk(''):
55 for path in dirs + files:
56 path_without_ext, ext = posixpath.splitext(path)
57 canonical_path = posixpath.join(base, path_without_ext)
58 if (ext not in self._strip_extensions or
59 path == SITE_VERIFICATION_FILE):
60 canonical_path += ext
61 canonical_paths.add(canonical_path)
62 simplified_paths_map[_Normalize(path, splittext=True)].append(
63 canonical_path)
64 # Store |simplified_paths_map| sorted. Ties in length are broken by
65 # taking the shortest, lexicographically smallest path.
66 for path_list in simplified_paths_map.itervalues():
67 path_list.sort(key=lambda p: (len(p), p))
68 self._cache.SetMulti({
69 'canonical_paths': canonical_paths,
70 'simplified_paths_map': simplified_paths_map,
72 else:
73 assert simplified_paths_map is not None
75 return canonical_paths, simplified_paths_map
77 return Future(callback=resolve)
79 def Canonicalize(self, path):
80 '''Returns the canonical path for |path|.
81 '''
82 canonical_paths, simplified_paths_map = self._LoadCache().Get()
84 # Path may already be the canonical path.
85 if path in canonical_paths:
86 return path
88 # Path not found. Our single heuristic: find |base| in the directory
89 # structure with the longest common prefix of |path|.
90 _, base = SplitParent(path)
92 # Paths with a non-extension dot separator lose information in
93 # _SimplifyFileName, so we try paths both with and without the dot to
94 # maximize the possibility of finding the right path.
95 potential_paths = (
96 simplified_paths_map.get(_Normalize(base), []) +
97 simplified_paths_map.get(_Normalize(base, splittext=True), []))
99 if potential_paths == []:
100 # There is no file with anything close to that name.
101 return path
103 # The most likely canonical file is the one with the longest common prefix
104 # with |path|. This is slightly weaker than it could be; |path| is
105 # compared without symbols, not the simplified form of |path|,
106 # which may matter.
107 max_prefix = potential_paths[0]
108 max_prefix_length = len(posixpath.commonprefix((max_prefix, path)))
109 for path_for_file in potential_paths[1:]:
110 prefix_length = len(posixpath.commonprefix((path_for_file,
111 _Normalize(path))))
112 if prefix_length > max_prefix_length:
113 max_prefix, max_prefix_length = path_for_file, prefix_length
115 return max_prefix
117 def Cron(self):
118 return self._LoadCache()