// TODO(mdsteele): Make a cleaner API than this (will need to change some other classes). public synchronized JSONObject getMetrics() { JSONObject obj = new JSONObject(); Json.put(obj, "time_to_first_byte_ms", this.timeToFirstByteMillis); Json.put(obj, "time_to_base_page_complete_ms", this.timeToBasePageCompleteMillis); Json.put(obj, "load_time_ms", this.loadTimeMillis); return obj; }
// Callback for fetchResourceContent(). private synchronized void handleResourceContent(String requestId, JSONObject result) { try { this.testData.addResourceContent( requestId, Json.getString(result, "body"), Json.getBoolean(result, "base64Encoded")); this.checkIfDone(); } catch (JsonException e) { this.abortTest("Bad JSON from Chrome: " + e.getMessage()); } }
// Callback for fetchDomTree(). private synchronized void handleDomTree(JSONObject result) { assert this.phase == Phase.FETCHING_DOM; this.phase = Phase.FINISHING; try { this.testData.setDomString(Json.getString(Json.getObject(result, "result"), "value")); this.checkIfDone(); } catch (JsonException e) { this.abortTest("Bad JSON from Chrome: " + e.getMessage()); } }
public synchronized void addResourceContent( String requestId, String body, boolean isBase64Encoded) { JSONObject result = new JSONObject(); Json.put(result, "content", body); Json.put(result, "base64Encoded", isBase64Encoded); JSONObject obj = new JSONObject(); Json.put(obj, "method", "__Internal.resourceContent"); Json.put(obj, "requestId", requestId); Json.put(obj, "result", result); this.dataForHar.add(obj); }
// Ask the tab to load the given URL. private synchronized void startLoadingUrl(String url) { // A previous version of Chrome had a "Page.load" method, but that has since been removed (at // least as of M16). Simply evaluating "window.location.href=url" seems to work fine, // though. JSONObject params = new JSONObject(); Json.put(params, "expression", "window.location.href=" + JSONValue.toJSONString(url)); Json.put(params, "returnByValue", true); this.tab.asyncCall( "Runtime.evaluate", params, new Callback() { public void onSuccess(JSONObject result) {} }); }
// Receive a notification message from the Chrome tab. @Override public synchronized void handleNotification(String method, JSONObject params) { this.testData.addDataForHar(method, params); try { if (this.phase == Phase.ABOUT_BLANK) { // Once we've loaded about:blank, we can begin the real test. if (method.equals("Page.loadEventFired")) { this.resetBrowserAndBeginLoadingPage(); } } else if (method.equals("Network.requestWillBeSent")) { if (this.mainRequestId == null) { this.mainRequestId = Json.getString(params, REQUEST_ID_KEY); } } else if (method.equals("Network.loadingFinished") || method.equals("Network.resourceLoadedFromMemoryCache")) { this.fetchResourceContent(Json.getString(params, REQUEST_ID_KEY)); } else if (method.equals("Network.responseReceived")) { if (this.mainRequestId != null && this.mainRequestId.equals(Json.getString(params, REQUEST_ID_KEY))) { this.startTimestampMillis = 1000.0 * Json.getDouble( Json.getObject(Json.getObject(params, "response"), "timing"), "requestTime"); this.testData.setTimeToFirstByteMillis( 1000.0 * Json.getDouble(params, "timestamp") - this.startTimestampMillis); } } else if (method.equals("Page.domContentEventFired")) { if (!Double.isNaN(this.startTimestampMillis)) { this.testData.setTimeToBasePageCompleteMillis( 1000.0 * Json.getDouble(params, "timestamp") - this.startTimestampMillis); } else { this.abortTest("Saw Page.domContentEventFired before receiving main response"); } } else if (method.equals("Page.loadEventFired")) { if (!Double.isNaN(this.startTimestampMillis)) { this.testData.setLoadTimeMillis( 1000.0 * Json.getDouble(params, "timestamp") - this.startTimestampMillis); this.fetchDomTree(); } else { this.abortTest("Saw Page.loadEventFired before receiving main response"); } } else if (method.equals("Timeline.eventRecorded")) { this.testData.addTimelineEvent(Json.getObject(params, "record")); } } catch (JsonException e) { this.abortTest("Bad JSON from Chrome: " + e.getMessage()); } }
// Enter the FETCHING_DOM phase; ask the Chrome tab to retrieve the DOM tree from the page. private synchronized void fetchDomTree() { assert this.phase == Phase.LOADING_PAGE; this.phase = Phase.FETCHING_DOM; JSONObject params = new JSONObject(); Json.put(params, "expression", DOM_COLLECTOR_SCRIPT); Json.put(params, "returnByValue", true); this.tab.asyncCall( "Runtime.evaluate", params, new Callback() { public void onSuccess(JSONObject response) { RunnerImpl.this.handleDomTree(response); } }); }
// Ask the Chrome tab to retrieve the contents of the specified resource. The requestId object // is some key object given to us by the Chrome tab. private synchronized void fetchResourceContent(final String requestId) { JSONObject params = new JSONObject(); Json.put(params, REQUEST_ID_KEY, requestId); this.tab.asyncCall( "Network.getResponseBody", params, new AsyncCallback() { public void onSuccess(JSONObject response) { RunnerImpl.this.handleResourceContent(requestId, response); } public void onError(String message) { // Some resources just don't have data; that's okay, so if that's the error we got, // ignore it. if (!message.equals("No data found for resource with given identifier")) { RunnerImpl.this.abortTest(message); } } }); }
// TODO(mdsteele): Make a cleaner API than this (will need to change TestRunner). public synchronized void addDataForHar(String methodName, JSONObject params) { JSONObject obj = new JSONObject(); Json.put(obj, "method", methodName); Json.put(obj, "params", params); this.dataForHar.add(obj); }
/** Append a single event record to the timeline data. */ public synchronized void addTimelineEvent(JSONObject record) { Json.add(this.timelineData, record); }