/* * Copyright (c) 2012, the Dart project authors. * * Licensed under the Eclipse Public License v1.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.eclipse.org/legal/epl-v10.html * * 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.dart.tools.debug.core.webkit; import com.google.dart.tools.debug.core.DartDebugCorePlugin; import com.google.dart.tools.debug.core.webkit.WebkitConnection.NotificationHandler; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; // TODO(devoncarew): review and add new css methods to this file /** * A WIP css domain object. * <p> * This domain exposes CSS read/write operations. All CSS objects (stylesheets, rules, and styles) * have an associated <code>id</code> used in subsequent operations on the related object. Each * object type has a specific <code>id</code> structure, and those are not interchangeable between * objects of different kinds. CSS objects can be loaded using the <code>get*ForNode()</code> calls * (which accept a DOM node id). A client can also discover all the existing stylesheets with the * <code>getAllStyleSheets()</code> method (or keeping track of the <code>styleSheetAdded</code>/ * <code>styleSheetRemoved</code> events) and subsequently load the required stylesheet contents * using the <code>getStyleSheet[Text]()</code> methods. */ @WebkitUnsupported public class WebkitCSS extends WebkitDomain { public static interface CSSListener { /** * Fires whenever a MediaQuery result changes (for example, after a browser window has been * resized.) The current implementation considers only viewport-dependent media features. */ public void mediaQueryResultChanged(); /** * Called when a style sheet is added. * * @param styleSheet */ public void styleSheetAdded(WebkitStyleSheetRef styleSheet); /** * Called when the given style sheet changes. * * @param styleSheetId */ public void styleSheetChanged(String styleSheetId); /** * Called when the given style sheet is removed. * * @param styleSheetId */ public void styleSheetRemoved(String styleSheetId); } private static final String STYLE_SHEET_ADDED = "CSS.styleSheetAdded"; private static final String STYLE_SHEET_CHANGED = "CSS.styleSheetChanged"; private static final String STYLE_SHEET_REMOVED = "CSS.styleSheetRemoved"; private static final String MEDIA_QUERY_RESULT_CHANGED = "CSS.mediaQueryResultChanged"; private List<CSSListener> listeners = new ArrayList<WebkitCSS.CSSListener>(); private List<WebkitStyleSheetRef> styleSheets = Collections.synchronizedList(new ArrayList<WebkitStyleSheetRef>()); /** * @param connection */ public WebkitCSS(WebkitConnection connection) { super(connection); connection.registerNotificationHandler("CSS.", new NotificationHandler() { @Override public void handleNotification(String method, JSONObject params) throws JSONException { handleCssNotification(method, params); } }); } public void addCSSListener(CSSListener listener) { listeners.add(listener); } public void disable() throws IOException { sendSimpleCommand("CSS.disable"); } public void enable() throws IOException { sendSimpleCommand("CSS.enable"); } public void getStyleSheet(String styleSheetId, final WebkitCallback<WebkitStyleSheet> callback) throws IOException { // "result":{ // "styleSheet":{ // "text":"h1 { font-size: 10pt }", // "styleSheetId":"1", // "rules":[ // { // "sourceLine":3, // "style":{ ... }, // "sourceURL":"http://0.0.0.0:3030/Users/dcarew/projects/dart/dart/samples/clock/Clock.html", // "selectorText":"h1", // "ruleId":{ // "ordinal":0, // "styleSheetId":"1" // }, // "origin":"regular", // "selectorRange":{ // "start":0, // "end":2 // } // } // ] // } // } // "result":{"styleSheet":{"text":"h2 {\n font-size: 8pt;\n}\n","styleSheetId":"2","rules" : // [{"sourceLine":0,"style":{"styleId":{"ordinal":0,"styleSheetId":"2"},"height":"","range":{"start" : // 4,"end":23},"width":"","cssText":"\n font-size: 8pt;\n","shorthandEntries":[],"cssProperties" : // [{"text":"font-size: 8pt;","range":{"start":3,"end":18},"status":"active","name":"font-size", // "implicit":false,"value":"8pt"}]},"sourceURL": // "http://0.0.0.0:3030/Users/foo/projects/dart/dart/samples/clock/clockstyle.css" // ,"selectorText":"h2","ruleId":{"ordinal":0,"styleSheetId":"2"},"origin":"regular","selectorRange" // :{"start":0,"end":2}}]}} try { JSONObject request = new JSONObject(); request.put("method", "CSS.getStyleSheet"); request.put("params", new JSONObject().put("styleSheetId", styleSheetId)); connection.sendRequest(request, new WebkitConnection.Callback() { @Override public void handleResult(JSONObject result) throws JSONException { callback.handleResult(convertGetStyleSheetResult(result)); } }); } catch (JSONException exception) { throw new IOException(exception); } } public List<WebkitStyleSheetRef> getStyleSheets() { return styleSheets; } public void getStyleSheetText(String styleSheetId, final WebkitCallback<String> callback) throws IOException { try { JSONObject request = new JSONObject(); request.put("method", "CSS.getStyleSheetText"); request.put("params", new JSONObject().put("styleSheetId", styleSheetId)); connection.sendRequest(request, new WebkitConnection.Callback() { @Override public void handleResult(JSONObject result) throws JSONException { callback.handleResult(convertGetStyleSheetTextResult(result)); } }); } catch (JSONException exception) { throw new IOException(exception); } } public void getSupportedCSSProperties(final WebkitCallback<String[]> callback) throws IOException { sendSimpleCommand("CSS.getSupportedCSSProperties", new WebkitConnection.Callback() { @Override public void handleResult(JSONObject result) throws JSONException { callback.handleResult(convertGetSupportedPropertiesResult(result)); } }); } public void removeCSSListener(CSSListener listener) { listeners.remove(listener); } public void setStyleSheetText(String styleSheetId, String text) throws IOException { try { JSONObject request = new JSONObject(); request.put("method", "CSS.setStyleSheetText"); request.put("params", new JSONObject().put("styleSheetId", styleSheetId).put("text", text)); connection.sendRequest(request); } catch (JSONException exception) { throw new IOException(exception); } } protected void handleCssNotification(String method, JSONObject params) throws JSONException { if (method.equals(STYLE_SHEET_ADDED)) { // {"method":"CSS.styleSheetAdded","params":{"header":{"title":"","frameId":"69818.1", // "sourceURL":"http://127.0.0.1:3030/Users/devoncarew/dart/todomvc-47/web/out/index.html", // "origin":"regular","styleSheetId":"7","disabled":false}}} WebkitStyleSheetRef styleSheet = WebkitStyleSheetRef.createFrom(params.getJSONObject("header")); styleSheets.add(styleSheet); //String styleSheetId = params.getJSONObject("header").getString("styleSheetId"); for (CSSListener listener : listeners) { listener.styleSheetAdded(styleSheet); } } else if (method.equals(STYLE_SHEET_CHANGED)) { String styleSheetId = params.getString("styleSheetId"); for (CSSListener listener : listeners) { listener.styleSheetChanged(styleSheetId); } } else if (method.equals(STYLE_SHEET_REMOVED)) { String styleSheetId = params.getString("styleSheetId"); for (CSSListener listener : listeners) { listener.styleSheetRemoved(styleSheetId); } } else if (method.equals(MEDIA_QUERY_RESULT_CHANGED)) { for (CSSListener listener : listeners) { listener.mediaQueryResultChanged(); } } else { DartDebugCorePlugin.logInfo("unhandled notification: " + method); } } void frameStartedLoading() { // Clear out our cached style sheet information. styleSheets.clear(); } private WebkitResult<WebkitStyleSheet> convertGetStyleSheetResult(JSONObject object) throws JSONException { WebkitResult<WebkitStyleSheet> result = WebkitResult.createFrom(object); if (object.has("result")) { result.setResult(WebkitStyleSheet.createFrom(object.getJSONObject("result").getJSONObject( "styleSheet"))); } return result; } private WebkitResult<String> convertGetStyleSheetTextResult(JSONObject object) throws JSONException { WebkitResult<String> result = WebkitResult.createFrom(object); // "result":{"text":"h1 { font-size: 10pt }"} if (object.has("result")) { JSONObject obj = object.getJSONObject("result"); result.setResult(obj.getString("text")); } return result; } private WebkitResult<String[]> convertGetSupportedPropertiesResult(JSONObject object) throws JSONException { WebkitResult<String[]> result = WebkitResult.createFrom(object); // "result": { // "cssProperties": [ "color","direction","display","font","font-family", // "font-size","font-style","font-variant","font-weight","text-rendering","-webkit-font-feature-settings" ... // ] // } if (object.has("result")) { JSONObject obj = object.getJSONObject("result"); JSONArray arr = obj.getJSONArray("cssProperties"); String[] properties = new String[arr.length()]; for (int i = 0; i < properties.length; i++) { properties[i] = arr.getString(i); } result.setResult(properties); } return result; } }