146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)# Copyright 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file. 41320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciimport inspect 51320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciimport os 61320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciimport re 71320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciimport urlparse 81320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 91320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci_next_page_id = 0 101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciclass Page(object): 121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def __init__(self, url, page_set=None, base_dir=None, name=''): 141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self._url = url 151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self._page_set = page_set 161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci # Default value of base_dir is the directory of the file that defines the 171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci # class of this page instance. 181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if base_dir is None: 191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci base_dir = os.path.dirname(inspect.getfile(self.__class__)) 201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self._base_dir = base_dir 211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self._name = name 221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci global _next_page_id 241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self._id = _next_page_id 251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci _next_page_id += 1 261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci # These attributes can be set dynamically by the page. 281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self.synthetic_delays = dict() 291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self.startup_url = page_set.startup_url if page_set else '' 301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self.credentials = None 311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self.disabled = False 321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self.skip_waits = False 331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self.script_to_evaluate_on_commit = None 341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self._SchemeErrorCheck() 351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def _SchemeErrorCheck(self): 371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if not self._scheme: 381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci raise ValueError('Must prepend the URL with scheme (e.g. file://)') 391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if self.startup_url: 411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci startup_url_scheme = urlparse.urlparse(self.startup_url).scheme 421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if not startup_url_scheme: 431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci raise ValueError('Must prepend the URL with scheme (e.g. http://)') 441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if startup_url_scheme == 'file': 451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci raise ValueError('startup_url with local file scheme is not supported') 461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def TransferToPageSet(self, another_page_set): 481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci """ Transfer this page to another page set. 491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci Args: 501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci another_page_set: an instance of telemetry.page.PageSet to transfer this 511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci page to. 521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci Note: 531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci This method removes this page instance from the pages list of its current 541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci page_set, so one should be careful not to iterate through the list of 551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci pages of a page_set and calling this method. 561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci For example, the below loop is erroneous: 571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci for p in page_set_A.pages: 581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci p.TransferToPageSet(page_set_B.pages) 591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci """ 601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci assert self._page_set 611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if another_page_set is self._page_set: 621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return 631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self._page_set.pages.remove(self) 641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self._page_set = another_page_set 651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self._page_set.AddPage(self) 661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def RunNavigateSteps(self, action_runner): 681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci action_runner.NavigateToPage(self) 691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def CanRunOnBrowser(self, browser_info): 711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci """Override this to returns whether this page can be run on specific 721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci browser. 731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci Args: 751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci browser_info: an instance of telemetry.core.browser_info.BrowserInfo 761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci """ 771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci assert browser_info 781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return True 791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def AsDict(self): 811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci """Converts a page object to a dict suitable for JSON output.""" 821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci d = { 831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 'id': self._id, 841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 'url': self._url, 851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if self._name: 871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci d['name'] = self._name 881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return d 891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci @property 911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def page_set(self): 921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return self._page_set 931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci @property 951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def name(self): 961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return self._name 971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci @property 991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def url(self): 1001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return self._url 1011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci @property 1031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def id(self): 1041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return self._id 1051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def GetSyntheticDelayCategories(self): 1071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci result = [] 1081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci for delay, options in self.synthetic_delays.items(): 1091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci options = '%f;%s' % (options.get('target_duration', 0), 1101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci options.get('mode', 'static')) 1111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci result.append('DELAY(%s;%s)' % (delay, options)) 1121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return result 1131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def __lt__(self, other): 1151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return self.url < other.url 1161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def __cmp__(self, other): 1181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci x = cmp(self.name, other.name) 1191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if x != 0: 1201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return x 1211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return cmp(self.url, other.url) 1221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def __str__(self): 1241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return self.url 1251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def AddCustomizeBrowserOptions(self, options): 1271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci """ Inherit page overrides this to add customized browser options.""" 1281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci pass 1291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci @property 1311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def _scheme(self): 1321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return urlparse.urlparse(self.url).scheme 1331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci @property 1351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def is_file(self): 1361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci """Returns True iff this URL points to a file.""" 1371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return self._scheme == 'file' 1381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci @property 1401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def is_local(self): 1411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci """Returns True iff this URL is local. This includes chrome:// URLs.""" 1421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return self._scheme in ['file', 'chrome', 'about'] 1431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci @property 1451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def file_path(self): 1461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci """Returns the path of the file, stripping the scheme and query string.""" 1471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci assert self.is_file 1481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci # Because ? is a valid character in a filename, 1491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci # we have to treat the url as a non-file by removing the scheme. 1501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci parsed_url = urlparse.urlparse(self.url[7:]) 1511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return os.path.normpath(os.path.join( 1521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self._base_dir, parsed_url.netloc + parsed_url.path)) 1531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci @property 1551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def file_path_url(self): 1561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci """Returns the file path, including the params, query, and fragment.""" 1571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci assert self.is_file 1581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci file_path_url = os.path.normpath(os.path.join(self._base_dir, self.url[7:])) 1591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci # Preserve trailing slash or backslash. 1601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci # It doesn't matter in a file path, but it does matter in a URL. 1611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if self.url.endswith('/'): 1621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci file_path_url += os.sep 1631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return file_path_url 1641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci @property 1661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def serving_dir(self): 1671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci file_path = os.path.realpath(self.file_path) 1681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if os.path.isdir(file_path): 1691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return file_path 1701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci else: 1711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return os.path.dirname(file_path) 1721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci @property 1741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def file_safe_name(self): 1751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci """A version of display_name that's safe to use as a filename.""" 1761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci # Just replace all special characters in the url with underscore. 1771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return re.sub('[^a-zA-Z0-9]', '_', self.display_name) 1781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci @property 1801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def display_name(self): 1811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if self.name: 1821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return self.name 1831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if not self.is_file: 1841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return self.url 1851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci all_urls = [p.url.rstrip('/') for p in self.page_set if p.is_file] 1861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci common_prefix = os.path.dirname(os.path.commonprefix(all_urls)) 1871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return self.url[len(common_prefix):].strip('/') 1881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci @property 1901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def archive_path(self): 1911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return self.page_set.WprFilePathForPage(self) 192