1# Copyright 2012 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 inspect
6import os
7import re
8import urlparse
9
10
11class Page(object):
12  def __init__(self, url, page_set=None, base_dir=None, name=''):
13    self._url = url
14    self._page_set = page_set
15    # Default value of base_dir is the directory of the file that defines the
16    # class of this page instace.
17    if base_dir is None:
18      base_dir = os.path.dirname(inspect.getfile(self.__class__))
19    self._base_dir = base_dir
20    self._name = name
21
22    # These attributes can be set dynamically by the page.
23    self.synthetic_delays = dict()
24    self.startup_url = page_set.startup_url if page_set else ''
25    self.credentials = None
26    self.disabled = False
27    self.script_to_evaluate_on_commit = None
28    self._SchemeErrorCheck()
29
30  def _SchemeErrorCheck(self):
31    if not self._scheme:
32      raise ValueError('Must prepend the URL with scheme (e.g. file://)')
33
34    if self.startup_url:
35      startup_url_scheme = urlparse.urlparse(self.startup_url).scheme
36      if not startup_url_scheme:
37        raise ValueError('Must prepend the URL with scheme (e.g. http://)')
38      if startup_url_scheme == 'file':
39        raise ValueError('startup_url with local file scheme is not supported')
40
41  def RunNavigateSteps(self, action_runner):
42    action_runner.NavigateToPage(self)
43
44  def CanRunOnBrowser(self, browser_info):
45    """Override this to returns whether this page can be run on specific
46    browser.
47
48    Args:
49      browser_info: an instance of telemetry.core.browser_info.BrowserInfo
50    """
51    assert browser_info
52    return True
53
54  @property
55  def page_set(self):
56    return self._page_set
57
58  @property
59  def name(self):
60    return self._name
61
62  @property
63  def url(self):
64    return self._url
65
66  def GetSyntheticDelayCategories(self):
67    result = []
68    for delay, options in self.synthetic_delays.items():
69      options = '%f;%s' % (options.get('target_duration', 0),
70                           options.get('mode', 'static'))
71      result.append('DELAY(%s;%s)' % (delay, options))
72    return result
73
74  def __lt__(self, other):
75    return self.url < other.url
76
77  def __cmp__(self, other):
78    x = cmp(self.name, other.name)
79    if x != 0:
80      return x
81    return cmp(self.url, other.url)
82
83  def __str__(self):
84    return self.url
85
86  def AddCustomizeBrowserOptions(self, options):
87    """ Inherit page overrides this to add customized browser options."""
88    pass
89
90  @property
91  def _scheme(self):
92    return urlparse.urlparse(self.url).scheme
93
94  @property
95  def is_file(self):
96    """Returns True iff this URL points to a file."""
97    return self._scheme == 'file'
98
99  @property
100  def is_local(self):
101    """Returns True iff this URL is local. This includes chrome:// URLs."""
102    return self._scheme in ['file', 'chrome', 'about']
103
104  @property
105  def file_path(self):
106    """Returns the path of the file, stripping the scheme and query string."""
107    assert self.is_file
108    # Because ? is a valid character in a filename,
109    # we have to treat the url as a non-file by removing the scheme.
110    parsed_url = urlparse.urlparse(self.url[7:])
111    return os.path.normpath(os.path.join(
112        self._base_dir, parsed_url.netloc + parsed_url.path))
113
114  @property
115  def file_path_url(self):
116    """Returns the file path, including the params, query, and fragment."""
117    assert self.is_file
118    file_path_url = os.path.normpath(os.path.join(self._base_dir, self.url[7:]))
119    # Preserve trailing slash or backslash.
120    # It doesn't matter in a file path, but it does matter in a URL.
121    if self.url.endswith('/'):
122      file_path_url += os.sep
123    return file_path_url
124
125  @property
126  def serving_dir(self):
127    file_path = os.path.realpath(self.file_path)
128    if os.path.isdir(file_path):
129      return file_path
130    else:
131      return os.path.dirname(file_path)
132
133  @property
134  def file_safe_name(self):
135    """A version of display_name that's safe to use as a filename."""
136    # Just replace all special characters in the url with underscore.
137    return re.sub('[^a-zA-Z0-9]', '_', self.display_name)
138
139  @property
140  def display_name(self):
141    if self.name:
142      return self.name
143    if not self.is_file:
144      return self.url
145    all_urls = [p.url.rstrip('/') for p in self.page_set if p.is_file]
146    common_prefix = os.path.dirname(os.path.commonprefix(all_urls))
147    return self.url[len(common_prefix):].strip('/')
148
149  @property
150  def archive_path(self):
151    return self.page_set.WprFilePathForPage(self)
152