def blocking_navigate(self, url, timeout=DEFAULT_TIMEOUT_SECS): ''' Do a blocking navigate to url `url`. This function triggers a navigation, and then waits for the browser to claim the page has finished loading. Roughly, this corresponds to the javascript `DOMContentLoaded` event, meaning the dom for the page is ready. Internals: A navigation command results in a sequence of events: - Page.frameStartedLoading" (with frameid) - Page.frameStoppedLoading" (with frameid) - Page.loadEventFired" (not attached to an ID) Therefore, this call triggers a navigation option, and then waits for the expected set of response event messages. ''' self.transport.flush(tab_key=self.tab_id) self.log.debug("Blocking navigate to URL: '%s'", url) ret = self.Page_navigate(url=url) assert ("result" in ret), "Missing return content" assert ("frameId" in ret['result']), "Missing 'frameId' in return content" assert ("loaderId" in ret['result']), "Missing 'loaderId' in return content" expected_id = ret['result']['frameId'] loader_id = ret['result']['loaderId'] try: self.log.debug("Waiting for frame navigated command response.") self.transport.recv_filtered( filter_funcs.check_frame_navigated_command(expected_id), tab_key=self.tab_id, timeout=timeout) self.log.debug("Waiting for frameStartedLoading response.") self.transport.recv_filtered(filter_funcs.check_frame_load_command( "Page.frameStartedLoading"), tab_key=self.tab_id, timeout=timeout) self.log.debug("Waiting for frameStoppedLoading response.") self.transport.recv_filtered(filter_funcs.check_frame_load_command( "Page.frameStoppedLoading"), tab_key=self.tab_id, timeout=timeout) # self.transport.recv_filtered(check_load_event_fired, tab_key=self.tab_id, timeout=timeout) self.log.debug("Waiting for responseReceived response.") resp = self.transport.recv_filtered( filter_funcs.network_response_recieved_for_url( url=None, expected_id=expected_id), tab_key=self.tab_id, timeout=timeout) if resp is None: raise ChromeNavigateTimedOut("Blocking navigate timed out!") return resp['params'] # The `Page.frameNavigated ` event does not get fired for non-markup responses. # Therefore, if we timeout on waiting for that, check to see if we received a binary response. except ChromeResponseNotReceived: # So this is basically broken, fix is https://bugs.chromium.org/p/chromium/issues/detail?id=831887 # but that bug report isn't fixed yet. # Siiiigh. self.log.warning( "Failed to receive expected response to navigate command. Checking if response is a binary object." ) resp = self.transport.recv_filtered( keycheck=filter_funcs.check_frame_loader_command( method_name="Network.responseReceived", loader_id=loader_id), tab_key=self.tab_id, timeout=timeout) return resp['params']
def blocking_navigate(self, url, timeout=DEFAULT_TIMEOUT_SECS): ''' Do a blocking navigate to url `url`. This function triggers a navigation, and then waits for the browser to claim the page has finished loading. Roughly, this corresponds to the javascript `DOMContentLoaded` event, meaning the dom for the page is ready. Internals: A navigation command results in a sequence of events: - Page.frameStartedLoading" (with frameid) - Page.frameStoppedLoading" (with frameid) - Page.loadEventFired" (not attached to an ID) Therefore, this call triggers a navigation option, and then waits for the expected set of response event messages. ''' ret = self.Page_navigate(url=url) assert ("result" in ret), "Missing return content" assert ("frameId" in ret['result']), "Missing 'frameId' in return content" expected_id = ret['result']['frameId'] def check_frame_navigated(message): if not message: return False if "method" not in message: return False if message['method'] != "Page.frameNavigated": return False if 'params' not in message: return False params = message['params'] if 'frame' not in params: return False frame = params['frame'] if 'id' in frame: ret = frame['id'] == expected_id # print('check_frame_navigated', message) return ret return False def check_frame_load_command(method_name): def frame_loading_tracker(message): if not message: return False if "method" not in message: return False if message['method'] != method_name: return False if 'params' not in message: return False return ret # Disabled. See https://bugs.chromium.org/p/chromedriver/issues/detail?id=1387 # params = message['params'] # if 'frameId' not in params: # return False # if 'frameId' in params: # ret = params['frameId'] == expected_id # # print("frame_loading_tracker", message) # return False return frame_loading_tracker def check_load_event_fired(message): if not message: return False if "method" not in message: return False if message['method'] == 'Page.loadEventFired': # print("check_load_event_fired", message) return True return False def network_response_recieved_for_url(url): def network_response_recieved_tracker(message): if not message: return False if "method" not in message: return False if message['method'] != 'Network.responseReceived': return False if 'params' not in message: return False params = message['params'] if 'frameId' not in params: return False if 'frameId' in params: if params[ 'frameId'] == expected_id and 'response' in params: return True # Checking the url in the response breaks if # the remote issues a 301 or 302. # response = params['response'] # if 'url' in response: # return url == response['url'] return False return network_response_recieved_tracker self.transport.recv_filtered(check_frame_navigated, tab_key=self.tab_id) self.transport.recv_filtered( check_frame_load_command("Page.frameStartedLoading"), tab_key=self.tab_id) self.transport.recv_filtered( check_frame_load_command("Page.frameStoppedLoading"), tab_key=self.tab_id) # self.transport.recv_filtered(check_load_event_fired, tab_key=self.tab_id) resp = self.transport.recv_filtered( network_response_recieved_for_url(url), tab_key=self.tab_id) if resp is None: raise ChromeNavigateTimedOut("Blocking navigate timed out!") return resp['params']