Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / common / extensions / docs / server2 / path_canonicalizer.py
blob5bb60499a9c3e2264d7a8c116199bb5e6bc51a8a
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 import logging
6 import posixpath
7 import traceback
9 from branch_utility import BranchUtility
10 from compiled_file_system import SingleFile
11 from extensions_paths import PUBLIC_TEMPLATES
12 from file_system import FileNotFoundError
15 def _SimplifyFileName(file_name):
16 return (posixpath.splitext(file_name)[0]
17 .lower()
18 .replace('.', '')
19 .replace('-', '')
20 .replace('_', ''))
23 class PathCanonicalizer(object):
24 '''Transforms paths into their canonical forms. Since the dev server has had
25 many incarnations - e.g. there didn't use to be apps/ - there may be old
26 paths lying around the webs. We try to redirect those to where they are now.
27 '''
28 def __init__(self, compiled_fs_factory, file_system):
29 # Map of simplified API names (for typo detection) to their real paths.
30 @SingleFile
31 def make_public_apis(_, file_names):
32 return dict((_SimplifyFileName(name), name) for name in file_names)
33 self._public_apis = compiled_fs_factory.Create(file_system,
34 make_public_apis,
35 PathCanonicalizer)
37 def Canonicalize(self, path):
38 '''Returns the canonical path for |path|, and whether that path is a
39 permanent canonicalisation (e.g. when we redirect from a channel to a
40 channel-less URL) or temporary (e.g. when we redirect from an apps-only API
41 to an extensions one - we may at some point enable it for extensions).
42 '''
43 class ReturnType(object):
44 def __init__(self, path, permanent):
45 self.path = path
46 self.permanent = permanent
48 # Catch incorrect comparisons by disabling ==/!=.
49 def __eq__(self, _): raise NotImplementedError()
50 def __ne__(self, _): raise NotImplementedError()
52 # Strip any channel info off it. There are no channels anymore.
53 for channel_name in BranchUtility.GetAllChannelNames():
54 channel_prefix = channel_name + '/'
55 if path.startswith(channel_prefix):
56 # Redirect now so that we can set the permanent-redirect bit. Channel
57 # redirects are the only things that should be permanent redirects;
58 # anything else *could* change, so is temporary.
59 return ReturnType(path[len(channel_prefix):], True)
61 # No further work needed for static.
62 if path.startswith('static/'):
63 return ReturnType(path, False)
65 # People go to just "extensions" or "apps". Redirect to the directory.
66 if path in ('extensions', 'apps'):
67 return ReturnType(path + '/', False)
69 # The rest of this function deals with trying to figure out what API page
70 # for extensions/apps to redirect to, if any. We see a few different cases
71 # here:
72 # - Unqualified names ("browserAction.html"). These are easy to resolve;
73 # figure out whether it's an extension or app API and redirect.
74 # - but what if it's both? Well, assume extensions. Maybe later we can
75 # check analytics and see which is more popular.
76 # - Wrong names ("apps/browserAction.html"). This really does happen,
77 # damn it, so do the above logic but record which is the default.
78 if path.startswith(('extensions/', 'apps/')):
79 default_platform, reference_path = path.split('/', 1)
80 else:
81 default_platform, reference_path = ('extensions', path)
83 try:
84 apps_public = self._public_apis.GetFromFileListing(
85 '%s/apps' % PUBLIC_TEMPLATES).Get()
86 extensions_public = self._public_apis.GetFromFileListing(
87 '%s/extensions' % PUBLIC_TEMPLATES).Get()
88 except FileNotFoundError:
89 # Probably offline.
90 logging.warning(traceback.format_exc())
91 return ReturnType(path, False)
93 simple_reference_path = _SimplifyFileName(reference_path)
94 apps_path = apps_public.get(simple_reference_path)
95 extensions_path = extensions_public.get(simple_reference_path)
97 if apps_path is None:
98 if extensions_path is None:
99 # No idea. Just return the original path. It'll probably 404.
100 pass
101 else:
102 path = 'extensions/%s' % extensions_path
103 else:
104 if extensions_path is None:
105 path = 'apps/%s' % apps_path
106 else:
107 assert apps_path == extensions_path
108 path = '%s/%s' % (default_platform, apps_path)
110 return ReturnType(path, False)