weexteam/weex_devtools_android

View on GitHub
inspector/src/main/java/com/taobao/weex/devtools/inspector/protocol/module/CSS.java

Summary

Maintainability
C
7 hrs
Test Coverage
/*
 * Copyright (c) 2014-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

package com.taobao.weex.devtools.inspector.protocol.module;

import com.taobao.weex.devtools.common.ListUtil;
import com.taobao.weex.devtools.common.LogUtil;
import com.taobao.weex.devtools.common.Util;
import com.taobao.weex.devtools.inspector.elements.Document;
import com.taobao.weex.devtools.inspector.elements.Origin;
import com.taobao.weex.devtools.inspector.elements.StyleAccumulator;
import com.taobao.weex.devtools.inspector.elements.W3CStyleConstants;
import com.taobao.weex.devtools.inspector.helper.ChromePeerManager;
import com.taobao.weex.devtools.inspector.helper.PeersRegisteredListener;
import com.taobao.weex.devtools.inspector.jsonrpc.JsonRpcPeer;
import com.taobao.weex.devtools.inspector.jsonrpc.JsonRpcResult;
import com.taobao.weex.devtools.inspector.protocol.ChromeDevtoolsDomain;
import com.taobao.weex.devtools.inspector.protocol.ChromeDevtoolsMethod;
import com.taobao.weex.devtools.json.ObjectMapper;
import com.taobao.weex.devtools.json.annotation.JsonProperty;

import org.json.JSONObject;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CSS implements ChromeDevtoolsDomain {
  private final ChromePeerManager mPeerManager;
  private final Document mDocument;
  private final ObjectMapper mObjectMapper;

  public CSS(Document document) {
    mDocument = Util.throwIfNull(document);
    mObjectMapper = new ObjectMapper();
    mPeerManager = new ChromePeerManager();
    mPeerManager.setListener(new PeerManagerListener());
  }

  @ChromeDevtoolsMethod
  public void enable(JsonRpcPeer peer, JSONObject params) {
  }

  @ChromeDevtoolsMethod
  public void disable(JsonRpcPeer peer, JSONObject params) {
  }

  @ChromeDevtoolsMethod
  public JsonRpcResult getComputedStyleForNode(JsonRpcPeer peer, JSONObject params) {
    final GetComputedStyleForNodeRequest request = mObjectMapper.convertValue(
        params,
        GetComputedStyleForNodeRequest.class);

    final GetComputedStyleForNodeResult result = new GetComputedStyleForNodeResult();
    result.computedStyle = new ArrayList<>();

    mDocument.postAndWait(new Runnable() {
      @Override
      public void run() {
        Object element = mDocument.getElementForNodeId(request.nodeId);

        if (element == null) {
          LogUtil.e("Tried to get the style of an element that does not exist, using nodeid=" +
              request.nodeId);

          return;
        }

        // fix devtools not show the box
        mockStyleProperty(result.computedStyle, sProperties);
        mDocument.getElementStyles(
            element,
            new StyleAccumulator() {
              @Override
              public void store(String name, String value, boolean isDefault) {
                if (!isDefault) {
                  CSSComputedStyleProperty property = new CSSComputedStyleProperty();
                  if (!name.startsWith("v-")) {
                    property.name = name;
                    property.value = value;
                    result.computedStyle.add(property);
                  }
                }
              }
            });
      }
    });

    return result;
  }

  private static final HashMap<String, String> sProperties = new HashMap<String, String>();

  static {
    sProperties.put(W3CStyleConstants.WIDTH, "");
    sProperties.put(W3CStyleConstants.HEIGHT, "");

    sProperties.put(W3CStyleConstants.PADDING_LEFT, "");
    sProperties.put(W3CStyleConstants.PADDING_TOP, "");
    sProperties.put(W3CStyleConstants.PADDING_RIGHT, "");
    sProperties.put(W3CStyleConstants.PADDING_BOTTOM, "");

    sProperties.put(W3CStyleConstants.BORDER_LEFT_WIDTH, "");
    sProperties.put(W3CStyleConstants.BORDER_TOP_WIDTH, "");
    sProperties.put(W3CStyleConstants.BORDER_RIGHT_WIDTH, "");
    sProperties.put(W3CStyleConstants.BORDER_BOTTOM_WIDTH, "");

    sProperties.put(W3CStyleConstants.MARGIN_LEFT, "");
    sProperties.put(W3CStyleConstants.MARGIN_TOP, "");
    sProperties.put(W3CStyleConstants.MARGIN_RIGHT, "");
    sProperties.put(W3CStyleConstants.MARGIN_BOTTOM, "");

    sProperties.put(W3CStyleConstants.LEFT, "");
    sProperties.put(W3CStyleConstants.TOP, "");
    sProperties.put(W3CStyleConstants.RIGHT, "");
    sProperties.put(W3CStyleConstants.BOTTOM, "");
  }

  private void mockStyleProperty(List<CSSComputedStyleProperty> computedStyle, HashMap<String, String> properties) {
    for (Map.Entry<String, String> entry : properties.entrySet()) {
      addStyleProperty(computedStyle, entry.getKey(), entry.getValue());
    }
  }

  private void addStyleProperty(List<CSSComputedStyleProperty> computedStyle, String name, String value) {
    CSSComputedStyleProperty property = new CSSComputedStyleProperty();
    property.name = name;
    property.value = value;
    computedStyle.add(property);
  }

  @ChromeDevtoolsMethod
  public JsonRpcResult getMatchedStylesForNode(JsonRpcPeer peer, JSONObject params) {
    final GetMatchedStylesForNodeRequest request = mObjectMapper.convertValue(
        params,
        GetMatchedStylesForNodeRequest.class);

    final GetMatchedStylesForNodeResult result = new GetMatchedStylesForNodeResult();
    List<RuleMatch> matches = new ArrayList<>();
    final RuleMatch localMatch = new RuleMatch();
    initMatch(localMatch, "local");
    matches.add(localMatch);
    final RuleMatch virtualMatch = new RuleMatch();
    if (!DOM.isNativeMode()) {
      initMatch(virtualMatch, "virtual");
      matches.add(virtualMatch);
    }

    result.matchedCSSRules = matches;// ListUtil.newImmutableList(match);

    mDocument.postAndWait(new Runnable() {
      @Override
      public void run() {
        Object elementForNodeId = mDocument.getElementForNodeId(request.nodeId);

        if (elementForNodeId == null) {
          LogUtil.w("Failed to get style of an element that does not exist, nodeid=" +
              request.nodeId);
          return;
        }

        mDocument.getElementStyles(
            elementForNodeId,
            new StyleAccumulator() {
              @Override
              public void store(String name, String value, boolean isDefault) {
                if (!isDefault) {
                  CSSProperty property = new CSSProperty();
                  if (!name.startsWith(W3CStyleConstants.V_PREFIX)) {
                    property.name = name;
                    property.value = value;
                    localMatch.rule.style.cssProperties.add(property);
                  } else {
                    if (!DOM.isNativeMode()) {
                      CSSProperty virtualProperty = new CSSProperty();
                      virtualProperty.name = name;
                      virtualProperty.value = value;
                      virtualMatch.rule.style.cssProperties.add(virtualProperty);
                    }
                  }
                }
              }
            });
      }
    });

    result.inherited = Collections.emptyList();
    result.pseudoElements = Collections.emptyList();

    return result;
  }

  private final class PeerManagerListener extends PeersRegisteredListener {
    @Override
    protected synchronized void onFirstPeerRegistered() {
      mDocument.addRef();
    }

    @Override
    protected synchronized void onLastPeerUnregistered() {
      mDocument.release();
    }
  }

  private static class CSSComputedStyleProperty {
    @JsonProperty(required = true)
    public String name;

    @JsonProperty(required = true)
    public String value;
  }

  private static class RuleMatch {
    @JsonProperty
    public CSSRule rule;

    @JsonProperty
    public List<Integer> matchingSelectors;
  }

  private static class SelectorList {
    @JsonProperty
    public List<Selector> selectors;

    @JsonProperty
    public String text;
  }

  private static class SourceRange {
    @JsonProperty(required = true)
    public int startLine;

    @JsonProperty(required = true)
    public int startColumn;

    @JsonProperty(required = true)
    public int endLine;

    @JsonProperty(required = true)
    public int endColumn;
  }

  private static class Selector {
    @JsonProperty(required = true)
    public String text;

    @JsonProperty
    public SourceRange range;
  }

  private static class CSSRule {
    @JsonProperty
    public String styleSheetId;

    @JsonProperty(required = true)
    public SelectorList selectorList;

    @JsonProperty
    public Origin origin;

    @JsonProperty
    public CSSStyle style;
  }

  private static class CSSStyle {
    @JsonProperty
    public String styleSheetId;

    @JsonProperty(required = true)
    public List<CSSProperty> cssProperties;

    @JsonProperty
    public List<ShorthandEntry> shorthandEntries;

    @JsonProperty
    public String cssText;

    @JsonProperty
    public SourceRange range;
  }

  private static class ShorthandEntry {
    @JsonProperty(required = true)
    public String name;

    @JsonProperty(required = true)
    public String value;

    @JsonProperty
    public Boolean important;
  }

  private static class CSSProperty {
    @JsonProperty(required = true)
    public String name;

    @JsonProperty(required = true)
    public String value;

    @JsonProperty
    public Boolean important;

    @JsonProperty
    public Boolean implicit;

    @JsonProperty
    public String text;

    @JsonProperty
    public Boolean parsedOk;

    @JsonProperty
    public Boolean disabled;

    @JsonProperty
    public SourceRange range;
  }

  private static class PseudoIdMatches {
    @JsonProperty(required = true)
    public int pseudoId;

    @JsonProperty(required = true)
    public List<RuleMatch> matches;

    public PseudoIdMatches() {
      this.matches = new ArrayList<>();
    }
  }

  private static class GetComputedStyleForNodeRequest {
    @JsonProperty(required = true)
    public int nodeId;
  }

  private static class InheritedStyleEntry {
    @JsonProperty(required = true)
    public CSSStyle inlineStyle;

    @JsonProperty(required = true)
    public List<RuleMatch> matchedCSSRules;
  }

  private static class GetComputedStyleForNodeResult implements JsonRpcResult {
    @JsonProperty(required = true)
    public List<CSSComputedStyleProperty> computedStyle;
  }

  private static class GetMatchedStylesForNodeRequest {
    @JsonProperty(required = true)
    public int nodeId;

    @JsonProperty
    public Boolean excludePseudo;

    @JsonProperty
    public Boolean excludeInherited;
  }

  private static class GetMatchedStylesForNodeResult implements JsonRpcResult {
    @JsonProperty
    public List<RuleMatch> matchedCSSRules;

    @JsonProperty
    public List<PseudoIdMatches> pseudoElements;

    @JsonProperty
    public List<InheritedStyleEntry> inherited;
  }

  private void initMatch(RuleMatch match, String value) {
    match.matchingSelectors = ListUtil.newImmutableList(0);

    Selector selector = new Selector();
    selector.text = value;

    CSSRule rule = new CSSRule();

    rule.origin = Origin.REGULAR;
    rule.selectorList = new SelectorList();

    rule.selectorList.selectors = ListUtil.newImmutableList(selector);

    rule.style = new CSSStyle();
    rule.style.cssProperties = new ArrayList<>();

    match.rule = rule;

    rule.style.shorthandEntries = Collections.emptyList();
  }
}