Package com.google.speedtracer.headlessextension.client

Source Code of com.google.speedtracer.headlessextension.client.HeadlessBackgroundPage$MessageHandler$HeadlessDataModel

/*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.speedtracer.headlessextension.client;

import java.util.HashMap;

import com.google.gwt.chrome.crx.client.Chrome;
import com.google.gwt.chrome.crx.client.Console;
import com.google.gwt.chrome.crx.client.Extension;
import com.google.gwt.chrome.crx.client.Port;
import com.google.gwt.chrome.crx.client.events.ConnectEvent;
import com.google.gwt.chrome.crx.client.events.MessageEvent;
import com.google.gwt.chrome.crx.client.events.MessageEvent.Message;
import com.google.gwt.core.client.Duration;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptException;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.coreext.client.JSON;
import com.google.gwt.xhr.client.XMLHttpRequest;
import com.google.speedtracer.client.ClientConfig;
import com.google.speedtracer.client.messages.HeadlessClearDataMessage;
import com.google.speedtracer.client.messages.HeadlessDumpDataAckMessage;
import com.google.speedtracer.client.messages.HeadlessDumpDataMessage;
import com.google.speedtracer.client.messages.HeadlessMonitoringOffAckMessage;
import com.google.speedtracer.client.messages.HeadlessMonitoringOffMessage;
import com.google.speedtracer.client.messages.HeadlessMonitoringOnAckMessage;
import com.google.speedtracer.client.messages.HeadlessMonitoringOnMessage;
import com.google.speedtracer.client.messages.HeadlessSendDataAckMessage;
import com.google.speedtracer.client.messages.HeadlessSendDataMessage;
import com.google.speedtracer.client.model.ChromeDebuggerDataInstance;
import com.google.speedtracer.client.model.DataInstance;
import com.google.speedtracer.client.model.EventRecord;
import com.google.speedtracer.client.util.Xhr;

/**
* A Chrome extension background page script for running a headless version of
* SpeedTracer intended to support benchmarking and unit testing.
*
* NOTE: the public key is included because currently the Headless Extension
* relies on statically referring to the chrome extension ID. While the private
* key is required to generate a signed extension with the proper ID, including
* the public key in the manifest allows an unpacked, unsigned extension to load
* with the proper ID.
*
* TODO(conroy): remove this once the reliance on the chrome extension ID is
* resolved.
*
* TODO(zundel): Before this can move into production, we need some kind of
* security precautions against the dump feature being used by untrustworthy
* websites. The current idea is to use a whitelist to configure sites that can
* access the headless API and sites that can be a target of the XHR when run
* from the query string. See {@link HeadlessOptionsPage}
*/
@Extension.ManifestInfo(name = "Speed Tracer - headless (by Google)", description = "Get insight into the performance of your web applications.", version = ClientConfig.VERSION, permissions = {
    "tabs", "http://*/*", "https://*/*", "debugger"}, icons = {
    "resources/icon16.png", "resources/icon32.png", "resources/icon48.png",
    "resources/icon128.png"}, publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLwpQwF5uAQ8ufE3XErrzZBim2rDzUpKFOD+/jStzSBczBXkZIdUhOpdrfhSbDjDUsPeWkHg1bdsjSGg/4hfGeJCFCOwwPqOJHFKVRPan1hMWu7nIDKWbP6d/eCBw8MWq1o+FObwbB0AIgNFsvoQgN1iwrRZB6rxkQmEdYQqiIOQIDAQAB")
public class HeadlessBackgroundPage extends Extension implements
    ConnectEvent.Listener {

  /**
   * Fires when messages are returned from the content script.
   */
  public class MessageHandler implements MessageEvent.Listener {
    private class HeadlessDataModel implements DataInstance.DataListener {
      private int sequence = 0;

      public void onEventRecord(EventRecord event) {
        event.setSequence(sequence++);
        // Send this message over to the content script
        String dataString = JSON.stringify(event);
        eventRecordData.push(dataString);
      }
     
      public void onEventStreamStarted() {
      }
    }

    private final Port port;

    public MessageHandler(Port port) {
      this.port = port;
    }

    /**
     * Handle an incoming message from the Content Script delivered over a
     * Chrome Extensions Port.
     */
    public void onMessage(Message msg) {
      Port.Message message = msg.cast();
      switch (message.getType()) {
        case HeadlessClearDataMessage.TYPE:
          doHeadlessClearData(message.<HeadlessClearDataMessage> cast());
          break;
        case HeadlessMonitoringOnMessage.TYPE:
          doHeadlessMonitoringOn(message.<HeadlessMonitoringOnMessage> cast());
          break;
        case HeadlessMonitoringOffMessage.TYPE:
          doHeadlessMonitoringOff(message.<HeadlessMonitoringOffMessage> cast());
          break;
        case HeadlessDumpDataMessage.TYPE:
          doHeadlessDumpData(message.<HeadlessDumpDataMessage> cast());
          break;
        case HeadlessSendDataMessage.TYPE:
          doHeadlessSendData(message.<HeadlessSendDataMessage> cast());
          break;
        default:
      }
    }

    private void doHeadlessClearData(HeadlessClearDataMessage message) {
      DataInstance dataInstance = getDataInstance();
      dataInstance.setBaseTime(Duration.currentTimeMillis());
      eventRecordData.setLength(0);
    }

    private void doHeadlessDumpData(HeadlessDumpDataMessage message) {
      // Pack up the data we've been saving in our list and send it back
      // to the API.
      HeadlessDumpDataAckMessage dumpMessage = HeadlessDumpDataAckMessage.create(eventRecordData.join("\n"));
      sendToContentScript(port, dumpMessage);
    }

    private void doHeadlessMonitoringOff(HeadlessMonitoringOffMessage message) {
      // The API is telling us to turn off monitoring.
      DataInstance dataInstance = getDataInstance();
      dataInstance.stopMonitoring();
      HeadlessMonitoringOffAckMessage ackMessage = HeadlessMonitoringOffAckMessage.create();
      sendToContentScript(port, ackMessage);
    }

    private void doHeadlessMonitoringOn(HeadlessMonitoringOnMessage message) {
      // The API is telling us to turn on monitoring.
      DataInstance dataInstance = getDataInstance();
      dataInstance.resumeMonitoring();
      HeadlessMonitoringOnMessage.Options options = message.getOptions();
      HeadlessMonitoringOnAckMessage ackMessage = HeadlessMonitoringOnAckMessage.create();
      if (options != null) {
        if (options.clearData()) {
          doHeadlessClearData(null);
        }
        ackMessage.setOptions(options);
      }
      sendToContentScript(port, ackMessage);
    }

    private void doHeadlessSendData(HeadlessSendDataMessage message) {
      DataInstance dataInstance = getDataInstance();
      // Pack up the data we've been saving in our list and send it out
      // via XHR.
      JsArray<JavaScriptObject> data = getEventRecordData();
      String payload = createXhrPayload(dataInstance.getBaseTime(), message,
          data);

      if (ClientConfig.isDebugMode()) {
        console.log("Sending payload of " + payload.length() + " bytes ("
            + data.length() + " trace records) to " + message.getUrl());
      }
      try {
        Xhr.post(message.getUrl(), payload, "application/json",
            new Xhr.XhrCallback() {
              public void onFail(XMLHttpRequest xhr) {
                HeadlessSendDataAckMessage sendMessage = HeadlessSendDataAckMessage.create(false);
                sendToContentScript(port, sendMessage);
              }

              public void onSuccess(XMLHttpRequest xhr) {
                HeadlessSendDataAckMessage sendMessage = HeadlessSendDataAckMessage.create(true);
                sendToContentScript(port, sendMessage);
              }
            });
      } catch (JavaScriptException ex) {
        console.log("XHR failed: " + ex);
        HeadlessSendDataAckMessage sendMessage = HeadlessSendDataAckMessage.create(false);
        sendToContentScript(port, sendMessage);
        throw ex;
      }
    }

    /**
     * Find the currently active DataInstance associated with this tab, or
     * create a new one.
     */
    private DataInstance getDataInstance() {
      int id = port.getSender().getTab().getId();
      DataInstance dataInstance = dataInstances.get(id);
      if (dataInstance == null) {
        dataInstance = ChromeDebuggerDataInstance.create(id);
        dataInstances.put(id, dataInstance);
        HeadlessDataModel dataModel = new HeadlessDataModel();
        dataInstance.load(dataModel);
      }
      return dataInstance;
    }

    private JsArray<JavaScriptObject> getEventRecordData() {
      JsArray<JavaScriptObject> result = JsArray.createArray().cast();
      for (int i = 0, length = eventRecordData.length(); i < length; ++i) {
        JavaScriptObject row = JSON.parse(eventRecordData.get(i));
        result.push(row);
      }
      return result;
    }
  }

  private static native String createXhrPayload(double baseTime,
      HeadlessSendDataMessage message, JsArray<JavaScriptObject> data) /*-{
    var header = message.header ? message.header : {} ;
    header.timeStamp = baseTime;
    var resultObject = {'header':header, 'data':data};
    return JSON.stringify(resultObject);
  }-*/;

  /**
   * This method provides a convenient place to annotate sending messages with
   * debugging.
   */
  private static void sendToContentScript(Port port, Port.Message message) {
    port.postMessage(message);
  }

  private Console console;

  private HashMap<Integer, DataInstance> dataInstances = new HashMap<Integer, DataInstance>();

  private final JsArrayString eventRecordData = JsArrayString.createArray().cast();

  @Override
  public String getVersion() {
    return null;
  }

  /**
   * Our entry point function. All things start here.
   */
  @Override
  public void onBackgroundPageLoad() {
    GWT.create(HeadlessContentScript.class);
    initialize();
  }

  /**
   * Fires when a new connection from a content script is received.
   */
  public void onConnect(Port port) {
    port.getOnMessageEvent().addListener(new MessageHandler(port));
  }

  private void initialize() {
    // Listen for messages from the content script
    Chrome.getExtension().getOnConnectEvent().addListener(this);
    console = Chrome.getExtension().getBackgroundPage().getConsole();
  }
}
TOP

Related Classes of com.google.speedtracer.headlessextension.client.HeadlessBackgroundPage$MessageHandler$HeadlessDataModel

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.