| Index: tools/perf/profile_creators/fast_navigation_profile_extender.py
|
| diff --git a/tools/perf/profile_creators/fast_navigation_profile_extender.py b/tools/perf/profile_creators/fast_navigation_profile_extender.py
|
| index d5b3da74caea6f517258f50e6b0c5dd16037b862..f37bc5da24b7e7a22b2847e9784333f61184afdf 100644
|
| --- a/tools/perf/profile_creators/fast_navigation_profile_extender.py
|
| +++ b/tools/perf/profile_creators/fast_navigation_profile_extender.py
|
| @@ -5,6 +5,7 @@ import time
|
|
|
| from profile_creators import profile_extender
|
| from telemetry.core import exceptions
|
| +from telemetry.core import util
|
|
|
|
|
| class FastNavigationProfileExtender(profile_extender.ProfileExtender):
|
| @@ -36,11 +37,13 @@ class FastNavigationProfileExtender(profile_extender.ProfileExtender):
|
| # The number of tabs to use.
|
| self._NUM_TABS = maximum_batch_size
|
|
|
| - # The amount of time to wait for a batch of pages to finish loading.
|
| - self._BATCH_PAGE_LOAD_TIMEOUT_IN_SECONDS = 10
|
| + # The amount of additional time to wait for a batch of pages to finish
|
| + # loading for each page in the batch.
|
| + self._BATCH_TIMEOUT_PER_PAGE_IN_SECONDS = 20
|
|
|
| - # The default amount of time to wait for the retrieval of the URL of a tab.
|
| - self._TAB_URL_RETRIEVAL_TIMEOUT_IN_SECONDS = 1
|
| + # The amount of time to wait for a page to quiesce. Some pages will never
|
| + # quiesce.
|
| + self._TIME_TO_WAIT_FOR_PAGE_TO_QUIESCE_IN_SECONDS = 10
|
|
|
| def Run(self):
|
| """Superclass override."""
|
| @@ -50,6 +53,21 @@ class FastNavigationProfileExtender(profile_extender.ProfileExtender):
|
| finally:
|
| self.TearDownBrowser()
|
|
|
| + # When there hasn't been an exception, verify that the profile was
|
| + # correctly extended.
|
| + # TODO(erikchen): I've intentionally omitted my implementation of
|
| + # VerifyProfileWasExtended() in small_profile_extender, since the profile
|
| + # is not being correctly extended. http://crbug.com/484833
|
| + # http://crbug.com/484880
|
| + self.VerifyProfileWasExtended()
|
| +
|
| + def VerifyProfileWasExtended(self):
|
| + """Verifies that the profile was correctly extended.
|
| +
|
| + Can be overridden by subclasses.
|
| + """
|
| + pass
|
| +
|
| def GetUrlIterator(self):
|
| """Gets URLs for the browser to navigate to.
|
|
|
| @@ -75,21 +93,6 @@ class FastNavigationProfileExtender(profile_extender.ProfileExtender):
|
| """
|
| pass
|
|
|
| - def _AddNewTab(self):
|
| - """Adds a new tab to the browser."""
|
| -
|
| - # Adding a new tab requires making a request over devtools. This can fail
|
| - # for a variety of reasons. Retry 3 times.
|
| - retry_count = 3
|
| - for i in range(retry_count):
|
| - try:
|
| - self._navigation_tabs.append(self._browser.tabs.New())
|
| - except exceptions.Error:
|
| - if i == retry_count - 1:
|
| - raise
|
| - else:
|
| - break
|
| -
|
| def _RefreshNavigationTabs(self):
|
| """Updates the member self._navigation_tabs to contain self._NUM_TABS
|
| elements, each of which is not crashed. The crashed tabs are intentionally
|
| @@ -104,7 +107,7 @@ class FastNavigationProfileExtender(profile_extender.ProfileExtender):
|
| self._navigation_tabs = live_tabs
|
|
|
| while len(self._navigation_tabs) < self._NUM_TABS:
|
| - self._AddNewTab()
|
| + self._navigation_tabs.append(self._browser.tabs.New())
|
|
|
| def _RemoveNavigationTab(self, tab):
|
| """Removes a tab which is no longer in a useable state from
|
| @@ -114,23 +117,23 @@ class FastNavigationProfileExtender(profile_extender.ProfileExtender):
|
|
|
| def _RetrieveTabUrl(self, tab, timeout):
|
| """Retrives the URL of the tab."""
|
| - try:
|
| - return tab.EvaluateJavaScript('document.URL', timeout)
|
| - except exceptions.Error:
|
| - return None
|
| + # TODO(erikchen): Use tab.url instead, which talks to the browser process
|
| + # instead of the renderer process. http://crbug.com/486119
|
| + return tab.EvaluateJavaScript('document.URL', timeout)
|
| +
|
| + def _WaitForUrlToChange(self, tab, initial_url, end_time):
|
| + """Waits for the tab to navigate away from its initial url.
|
|
|
| - def _WaitForUrlToChange(self, tab, initial_url, timeout):
|
| - """Waits for the tab to navigate away from its initial url."""
|
| - end_time = time.time() + timeout
|
| + If time.time() is larger than end_time, the function does nothing.
|
| + Otherwise, the function tries to return no later than end_time.
|
| + """
|
| while True:
|
| seconds_to_wait = end_time - time.time()
|
| - seconds_to_wait = max(0, seconds_to_wait)
|
| -
|
| - if seconds_to_wait == 0:
|
| + if seconds_to_wait <= 0:
|
| break
|
|
|
| current_url = self._RetrieveTabUrl(tab, seconds_to_wait)
|
| - if current_url != initial_url:
|
| + if current_url != initial_url and current_url != "":
|
| break
|
|
|
| # Retrieving the current url is a non-trivial operation. Add a small
|
| @@ -138,6 +141,26 @@ class FastNavigationProfileExtender(profile_extender.ProfileExtender):
|
| # navigation.
|
| time.sleep(0.01)
|
|
|
| + def _WaitForTabToBeReady(self, tab, end_time):
|
| + """Waits for the tab to be ready.
|
| +
|
| + If time.time() is larger than end_time, the function does nothing.
|
| + Otherwise, the function tries to return no later than end_time.
|
| + """
|
| + seconds_to_wait = end_time - time.time()
|
| + if seconds_to_wait <= 0:
|
| + return
|
| + tab.WaitForDocumentReadyStateToBeComplete(seconds_to_wait)
|
| +
|
| + # Wait up to 10 seconds for the page to quiesce. If the page hasn't
|
| + # quiesced in 10 seconds, it will probably never quiesce.
|
| + seconds_to_wait = end_time - time.time()
|
| + seconds_to_wait = max(0, seconds_to_wait)
|
| + try:
|
| + util.WaitFor(tab.HasReachedQuiescence, seconds_to_wait)
|
| + except exceptions.TimeoutException:
|
| + pass
|
| +
|
| def _BatchNavigateTabs(self, batch):
|
| """Performs a batch of tab navigations with minimal delay.
|
|
|
| @@ -148,21 +171,22 @@ class FastNavigationProfileExtender(profile_extender.ProfileExtender):
|
| A list of tuples (tab, initial_url). |initial_url| is the url of the
|
| |tab| prior to a navigation command being sent to it.
|
| """
|
| - timeout_in_seconds = 0
|
| + # Attempting to pass in a timeout of 0 seconds results in a synchronous
|
| + # socket error from the websocket library. Pass in a very small timeout
|
| + # instead so that the websocket library raises a Timeout exception. This
|
| + # prevents the logic from accidentally catching different socket
|
| + # exceptions.
|
| + timeout_in_seconds = 0.01
|
|
|
| queued_tabs = []
|
| for tab, url in batch:
|
| - initial_url = self._RetrieveTabUrl(tab,
|
| - self._TAB_URL_RETRIEVAL_TIMEOUT_IN_SECONDS)
|
| -
|
| + initial_url = self._RetrieveTabUrl(tab, 20)
|
| try:
|
| tab.Navigate(url, None, timeout_in_seconds)
|
| - except exceptions.Error:
|
| - # We expect a time out. It's possible for other problems to arise, but
|
| - # this method is not responsible for dealing with them. Ignore all
|
| - # exceptions.
|
| + except exceptions.TimeoutException:
|
| + # We expect to receive a timeout exception, since we're not waiting for
|
| + # the navigation to complete.
|
| pass
|
| -
|
| queued_tabs.append((tab, initial_url))
|
| return queued_tabs
|
|
|
| @@ -173,30 +197,14 @@ class FastNavigationProfileExtender(profile_extender.ProfileExtender):
|
| queued_tabs: A list of tuples (tab, initial_url). Each tab is guaranteed
|
| to have already been sent a navigation command.
|
| """
|
| - end_time = time.time() + self._BATCH_PAGE_LOAD_TIMEOUT_IN_SECONDS
|
| + total_batch_timeout = (len(queued_tabs) *
|
| + self._BATCH_TIMEOUT_PER_PAGE_IN_SECONDS)
|
| + end_time = time.time() + total_batch_timeout
|
| for tab, initial_url in queued_tabs:
|
| - seconds_to_wait = end_time - time.time()
|
| - seconds_to_wait = max(0, seconds_to_wait)
|
| -
|
| - if seconds_to_wait == 0:
|
| - break
|
| -
|
| - # Since we don't wait any time for the tab url navigation to commit, it's
|
| + # Since we didn't wait any time for the tab url navigation to commit, it's
|
| # possible that the tab hasn't started navigating yet.
|
| - self._WaitForUrlToChange(tab, initial_url, seconds_to_wait)
|
| -
|
| - seconds_to_wait = end_time - time.time()
|
| - seconds_to_wait = max(0, seconds_to_wait)
|
| -
|
| - try:
|
| - tab.WaitForDocumentReadyStateToBeComplete(seconds_to_wait)
|
| - except exceptions.TimeoutException:
|
| - # Ignore time outs.
|
| - pass
|
| - except exceptions.Error:
|
| - # If any error occurs, remove the tab. it's probably in an
|
| - # unrecoverable state.
|
| - self._RemoveNavigationTab(tab)
|
| + self._WaitForUrlToChange(tab, initial_url, end_time)
|
| + self._WaitForTabToBeReady(tab, end_time)
|
|
|
| def _GetUrlsToNavigate(self, url_iterator):
|
| """Returns an array of urls to navigate to, given a url_iterator."""
|
|
|