org.chromium.sdk/src/org/chromium/sdk/internal/tools/devtools/DevToolsServiceHandler.java
changeset 2 e4420d2515f1
equal deleted inserted replaced
1:ef76fc2ac88c 2:e4420d2515f1
       
     1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
       
     2 // Use of this source code is governed by a BSD-style license that can be
       
     3 // found in the LICENSE file.
       
     4 
       
     5 package org.chromium.sdk.internal.tools.devtools;
       
     6 
       
     7 import java.util.ArrayList;
       
     8 import java.util.Collections;
       
     9 import java.util.List;
       
    10 import java.util.concurrent.Semaphore;
       
    11 import java.util.concurrent.TimeUnit;
       
    12 import java.util.concurrent.TimeoutException;
       
    13 import java.util.logging.Level;
       
    14 import java.util.logging.Logger;
       
    15 
       
    16 import org.chromium.sdk.internal.JsonUtil;
       
    17 import org.chromium.sdk.internal.tools.ChromeDevToolsProtocol;
       
    18 import org.chromium.sdk.internal.tools.ToolHandler;
       
    19 import org.chromium.sdk.internal.tools.ToolOutput;
       
    20 import org.chromium.sdk.internal.transport.Message;
       
    21 import org.json.simple.JSONArray;
       
    22 import org.json.simple.JSONObject;
       
    23 import org.json.simple.parser.ParseException;
       
    24 
       
    25 /**
       
    26  * Handles the interaction with the "DevToolsService" tool.
       
    27  */
       
    28 public class DevToolsServiceHandler implements ToolHandler {
       
    29 
       
    30   /**
       
    31    * The debugger connection to use.
       
    32    */
       
    33   private final ToolOutput toolOutput;
       
    34 
       
    35   /**
       
    36    * A "list_tabs" command callback. Is accessed in a synchronized way.
       
    37    */
       
    38   private ListTabsCallback listTabsCallback;
       
    39 
       
    40   /**
       
    41    * A "version" command callback. Is accessed in a synchronized way.
       
    42    */
       
    43   private VersionCallback versionCallback;
       
    44 
       
    45   /**
       
    46    * An access/modification lock for the callback fields.
       
    47    */
       
    48   private final Object lock = new Object();
       
    49 
       
    50   public static class TabIdAndUrl {
       
    51     public final int id;
       
    52 
       
    53     public final String url;
       
    54 
       
    55     private TabIdAndUrl(int id, String url) {
       
    56       this.id = id;
       
    57       this.url = url;
       
    58     }
       
    59 
       
    60     @Override
       
    61     public String toString() {
       
    62       return new StringBuilder()
       
    63           .append('[')
       
    64           .append(id)
       
    65           .append('=')
       
    66           .append(url)
       
    67           .append(']')
       
    68           .toString();
       
    69     }
       
    70   }
       
    71 
       
    72   /**
       
    73    * A callback that will be invoked when the ChromeDevTools protocol version
       
    74    * is available.
       
    75    */
       
    76   private interface VersionCallback {
       
    77     void versionReceived(String versionString);
       
    78   }
       
    79 
       
    80   /**
       
    81    * A callback that will be invoked when the tabs from the associated browser
       
    82    * instance are ready (or not...)
       
    83    */
       
    84   private interface ListTabsCallback {
       
    85     void tabsReceived(List<TabIdAndUrl> tabs);
       
    86 
       
    87     void failure(int result);
       
    88   }
       
    89 
       
    90   public DevToolsServiceHandler(ToolOutput toolOutput) {
       
    91     this.toolOutput = toolOutput;
       
    92   }
       
    93 
       
    94   public void onDebuggerDetached() {
       
    95   }
       
    96 
       
    97   public void handleMessage(Message message) {
       
    98     JSONObject json;
       
    99     try {
       
   100       json = JsonUtil.jsonObjectFromJson(message.getContent());
       
   101     } catch (ParseException e) {
       
   102       Logger.getLogger(DevToolsServiceHandler.class.getName()).log(
       
   103           Level.SEVERE, "Invalid JSON received: {0}", message.getContent());
       
   104       return;
       
   105     }
       
   106     String commandString = JsonUtil.getAsString(json, ChromeDevToolsProtocol.COMMAND.key);
       
   107     DevToolsServiceCommand command = DevToolsServiceCommand.forString(commandString);
       
   108     if (command != null) {
       
   109       switch (command) {
       
   110         case LIST_TABS:
       
   111           handleListTabs(json);
       
   112           break;
       
   113         case VERSION:
       
   114           handleVersion(json);
       
   115           break;
       
   116         default:
       
   117           break;
       
   118       }
       
   119     }
       
   120   }
       
   121 
       
   122   public void handleEos() {
       
   123     // ignore this event, we do not close browser in any way; but clients should dismiss
       
   124     // all tickets
       
   125   }
       
   126 
       
   127   private void handleVersion(JSONObject json) {
       
   128     VersionCallback callback;
       
   129     synchronized (lock) {
       
   130       callback = versionCallback;
       
   131       versionCallback = null;
       
   132     }
       
   133     if (callback != null) {
       
   134       String versionString = JsonUtil.getAsString(json, ChromeDevToolsProtocol.DATA.key);
       
   135       callback.versionReceived(versionString);
       
   136     }
       
   137   }
       
   138 
       
   139   private void handleListTabs(JSONObject json) {
       
   140     ListTabsCallback callback;
       
   141     synchronized (lock) {
       
   142       callback = listTabsCallback;
       
   143       listTabsCallback = null;
       
   144     }
       
   145     if (callback != null) {
       
   146       int result = JsonUtil.getAsLong(json, ChromeDevToolsProtocol.RESULT.key).intValue();
       
   147       if (result != 0) {
       
   148         callback.failure(result);
       
   149         return;
       
   150       }
       
   151       JSONArray data = JsonUtil.getAsJSONArray(json, ChromeDevToolsProtocol.DATA.key);
       
   152       List<TabIdAndUrl> tabs = new ArrayList<TabIdAndUrl>(data.size());
       
   153       for (int i = 0; i < data.size(); ++i) {
       
   154         JSONArray idAndUrl = (JSONArray) data.get(i);
       
   155         int id = ((Long) idAndUrl.get(0)).intValue();
       
   156         String url = (String) idAndUrl.get(1);
       
   157         tabs.add(new TabIdAndUrl(id, url));
       
   158       }
       
   159       callback.tabsReceived(tabs);
       
   160     }
       
   161   }
       
   162 
       
   163   @SuppressWarnings("unchecked")
       
   164   public List<TabIdAndUrl> listTabs(int timeout) {
       
   165     final Semaphore sem = new Semaphore(0);
       
   166     final List<TabIdAndUrl>[] output = new List[1];
       
   167     synchronized (lock) {
       
   168       if (listTabsCallback != null) {
       
   169         throw new IllegalStateException("list_tabs request is pending");
       
   170       }
       
   171       listTabsCallback = new ListTabsCallback() {
       
   172         public void failure(int result) {
       
   173           sem.release();
       
   174         }
       
   175 
       
   176         public void tabsReceived(List<TabIdAndUrl> tabs) {
       
   177           output[0] = tabs;
       
   178           sem.release();
       
   179         }
       
   180       };
       
   181     }
       
   182     toolOutput.send(CommandFactory.listTabs());
       
   183     try {
       
   184       if (!sem.tryAcquire(timeout, TimeUnit.MILLISECONDS)) {
       
   185         resetListTabsHandler();
       
   186       }
       
   187     } catch (InterruptedException e) {
       
   188       // Fall through
       
   189     }
       
   190 
       
   191     if (output[0] == null) {
       
   192       return Collections.emptyList();
       
   193     }
       
   194 
       
   195     return output[0];
       
   196   }
       
   197 
       
   198   public String version(int timeout) throws TimeoutException {
       
   199     final Semaphore sem = new Semaphore(0);
       
   200     final String[] output = new String[1];
       
   201     synchronized (lock) {
       
   202       if (versionCallback != null) {
       
   203         throw new IllegalStateException("version request is pending");
       
   204       }
       
   205       versionCallback = new VersionCallback() {
       
   206         public void versionReceived(String versionString) {
       
   207           output[0] = versionString;
       
   208           sem.release();
       
   209         }
       
   210       };
       
   211     }
       
   212     toolOutput.send(CommandFactory.version());
       
   213     boolean res;
       
   214     try {
       
   215       res = sem.tryAcquire(timeout, TimeUnit.MILLISECONDS);
       
   216     } catch (InterruptedException e) {
       
   217       throw new RuntimeException(e);
       
   218     }
       
   219     if (!res) {
       
   220       throw new TimeoutException("Failed to get version response in " + timeout + " ms");
       
   221     }
       
   222     return output[0];
       
   223   }
       
   224 
       
   225   /**
       
   226    * This can get called asynchronously.
       
   227    */
       
   228   public void resetListTabsHandler() {
       
   229     synchronized (lock) {
       
   230       listTabsCallback = null;
       
   231     }
       
   232   }
       
   233 
       
   234   private static class CommandFactory {
       
   235 
       
   236     public static String ping() {
       
   237       return createDevToolsMessage(DevToolsServiceCommand.PING);
       
   238     }
       
   239 
       
   240     public static String version() {
       
   241       return createDevToolsMessage(DevToolsServiceCommand.VERSION);
       
   242     }
       
   243     public static String listTabs() {
       
   244       return createDevToolsMessage(DevToolsServiceCommand.LIST_TABS);
       
   245     }
       
   246 
       
   247     private static String createDevToolsMessage(DevToolsServiceCommand command) {
       
   248       return "{\"command\":" + JsonUtil.quoteString(command.commandName) + "}";
       
   249     }
       
   250   }
       
   251 }