123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- // Copyright 2016 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "components/ui_devtools/css_agent.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/strings/string_split.h"
- #include "base/strings/string_util.h"
- #include "components/ui_devtools/agent_util.h"
- #include "components/ui_devtools/ui_element.h"
- namespace ui_devtools {
- namespace CSS = protocol::CSS;
- using protocol::Array;
- using protocol::Response;
- namespace {
- const char kHeight[] = "height";
- const char kWidth[] = "width";
- const char kX[] = "x";
- const char kY[] = "y";
- const char kVisibility[] = "visibility";
- std::unique_ptr<CSS::SourceRange> BuildDefaultPropertySourceRange() {
- // These tell the frontend where in the stylesheet a certain style
- // is located. Since we don't have stylesheets, this is all 0.
- // We need this because CSS fields are not editable unless
- // the range is provided.
- return CSS::SourceRange::create()
- .setStartLine(0)
- .setEndLine(0)
- .setStartColumn(0)
- .setEndColumn(0)
- .build();
- }
- std::unique_ptr<CSS::SourceRange> BuildDefaultSelectorSourceRange() {
- // This is a different source range from BuildDefaultPropertySourceRange()
- // used for the Selectors, so the frontend correctly handles property edits.
- return CSS::SourceRange::create()
- .setStartLine(1)
- .setEndLine(0)
- .setStartColumn(0)
- .setEndColumn(0)
- .build();
- }
- std::unique_ptr<Array<int>> BuildDefaultMatchingSelectors() {
- auto matching_selectors = std::make_unique<Array<int>>();
- // Add index 0 to matching selectors array, so frontend uses the class name
- // from the selectors array as the header for the properties section
- matching_selectors->emplace_back(0);
- return matching_selectors;
- }
- std::unique_ptr<CSS::CSSProperty> BuildCSSProperty(const std::string& name,
- const std::string& value) {
- return CSS::CSSProperty::create()
- .setRange(BuildDefaultPropertySourceRange())
- .setName(name)
- .setValue(value)
- .build();
- }
- std::unique_ptr<Array<CSS::CSSProperty>> BuildCSSProperties(
- const std::vector<UIElement::UIProperty>& properties_vector) {
- auto css_properties = std::make_unique<Array<CSS::CSSProperty>>();
- for (const auto& property : properties_vector) {
- css_properties->emplace_back(
- BuildCSSProperty(property.name_, property.value_));
- }
- return css_properties;
- }
- std::unique_ptr<CSS::CSSStyle> BuildCSSStyle(
- std::string stylesheet_uid,
- const std::vector<UIElement::UIProperty>& properties) {
- return CSS::CSSStyle::create()
- .setRange(BuildDefaultPropertySourceRange())
- .setCssProperties(BuildCSSProperties(properties))
- .setShorthandEntries(std::make_unique<Array<CSS::ShorthandEntry>>())
- .setStyleSheetId(stylesheet_uid)
- .build();
- }
- std::unique_ptr<Array<CSS::Value>> BuildSelectors(const std::string& name) {
- auto selectors = std::make_unique<Array<CSS::Value>>();
- selectors->emplace_back(CSS::Value::create()
- .setText(name)
- .setRange(BuildDefaultSelectorSourceRange())
- .build());
- return selectors;
- }
- std::unique_ptr<CSS::SelectorList> BuildSelectorList(const std::string& name) {
- return CSS::SelectorList::create().setSelectors(BuildSelectors(name)).build();
- }
- std::unique_ptr<CSS::CSSRule> BuildCSSRule(
- std::string stylesheet_uid,
- const UIElement::ClassProperties& class_properties) {
- return CSS::CSSRule::create()
- .setStyleSheetId(stylesheet_uid)
- .setSelectorList(BuildSelectorList(class_properties.class_name_))
- .setStyle(BuildCSSStyle(stylesheet_uid, class_properties.properties_))
- .build();
- }
- std::vector<UIElement::ClassProperties> GetClassPropertiesWithBounds(
- UIElement* ui_element) {
- std::vector<UIElement::ClassProperties> properties_vector =
- ui_element->GetCustomPropertiesForMatchedStyle();
- // If GetCustomPropertiesForMatchedStyle not overridden to return custom
- // properties, populate vector with bounds properties.
- if (properties_vector.empty()) {
- gfx::Rect bounds;
- ui_element->GetBounds(&bounds);
- std::vector<UIElement::UIProperty> bound_properties;
- bound_properties.emplace_back(kX, base::NumberToString(bounds.x()));
- bound_properties.emplace_back(kY, base::NumberToString(bounds.y()));
- bound_properties.emplace_back(kWidth, base::NumberToString(bounds.width()));
- bound_properties.emplace_back(kHeight,
- base::NumberToString(bounds.height()));
- if (ui_element->type() != VIEW) {
- bool visible;
- ui_element->GetVisible(&visible);
- bound_properties.emplace_back(kVisibility, visible ? "true" : "false");
- }
- properties_vector.emplace_back(ui_element->GetTypeName(), bound_properties);
- }
- // Set base stylesheet ID to the last index in the vector, so when bounds
- // properties are modified, CSSAgent can update and return the right
- // properties section
- ui_element->SetBaseStylesheetId(properties_vector.size() - 1);
- return properties_vector;
- }
- std::string BuildStylesheetUId(int node_id, int stylesheet_id) {
- return base::NumberToString(node_id) + "_" +
- base::NumberToString(stylesheet_id);
- }
- Response NodeNotFoundError(int node_id) {
- return Response::ServerError("Node with id=" + base::NumberToString(node_id) +
- " not found");
- }
- Response ParseProperties(const std::string& style_text,
- gfx::Rect* bounds,
- bool* visible) {
- std::vector<std::string> tokens = base::SplitString(
- style_text, ":;", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
- if (tokens.size() < 2 || tokens.size() % 2 != 0)
- return Response::ServerError("Need both a property name and value.");
- for (size_t i = 0; i < tokens.size() - 1; i += 2) {
- const std::string& property = tokens.at(i);
- int value;
- if (!base::StringToInt(tokens.at(i + 1), &value)) {
- return Response::ServerError("Unable to parse value for property=" +
- property);
- }
- if (property == kHeight)
- bounds->set_height(std::max(0, value));
- else if (property == kWidth)
- bounds->set_width(std::max(0, value));
- else if (property == kX)
- bounds->set_x(value);
- else if (property == kY)
- bounds->set_y(value);
- else if (property == kVisibility)
- *visible = std::max(0, value) == 1;
- else
- return Response::ServerError("Unsupported property=" + property);
- }
- return Response::Success();
- }
- std::unique_ptr<CSS::CSSStyleSheetHeader> BuildObjectForStyleSheetInfo(
- std::string stylesheet_uid,
- std::string url_path,
- int line) {
- std::unique_ptr<CSS::CSSStyleSheetHeader> result =
- CSS::CSSStyleSheetHeader::create()
- .setStyleSheetId(stylesheet_uid)
- .setSourceURL(kChromiumCodeSearchSrcURL + url_path +
- "?l=" + base::NumberToString(line))
- .setStartLine(line)
- .setStartColumn(0)
- .build();
- return result;
- }
- } // namespace
- CSSAgent::CSSAgent(DOMAgent* dom_agent) : dom_agent_(dom_agent) {
- DCHECK(dom_agent_);
- }
- CSSAgent::~CSSAgent() {
- disable();
- }
- Response CSSAgent::enable() {
- dom_agent_->AddObserver(this);
- return Response::Success();
- }
- Response CSSAgent::disable() {
- dom_agent_->RemoveObserver(this);
- return Response::Success();
- }
- Response CSSAgent::getMatchedStylesForNode(
- int node_id,
- protocol::Maybe<Array<CSS::RuleMatch>>* matched_css_rules) {
- UIElement* ui_element = dom_agent_->GetElementFromNodeId(node_id);
- if (!ui_element)
- return NodeNotFoundError(node_id);
- *matched_css_rules = BuildMatchedStyles(ui_element);
- return Response::Success();
- }
- Response CSSAgent::getStyleSheetText(const protocol::String& style_sheet_id,
- protocol::String* result) {
- int node_id;
- int stylesheet_id;
- std::vector<std::string> ids = base::SplitString(
- style_sheet_id, "_", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
- if (ids.size() < 2 || !base::StringToInt(ids[0], &node_id) ||
- !base::StringToInt(ids[1], &stylesheet_id))
- return Response::ServerError("Invalid stylesheet id");
- UIElement* ui_element = dom_agent_->GetElementFromNodeId(node_id);
- if (!ui_element)
- return Response::ServerError("Node id not found");
- auto sources = ui_element->GetSources();
- if (static_cast<int>(sources.size()) <= stylesheet_id)
- return Response::ServerError("Stylesheet id not found");
- if (GetSourceCode(sources[stylesheet_id].path_, result))
- return Response::Success();
- return Response::ServerError("Could not read source file");
- }
- Response CSSAgent::setStyleTexts(
- std::unique_ptr<Array<CSS::StyleDeclarationEdit>> edits,
- std::unique_ptr<Array<CSS::CSSStyle>>* result) {
- auto updated_styles = std::make_unique<Array<CSS::CSSStyle>>();
- for (const auto& edit : *edits) {
- int node_id;
- int stylesheet_id;
- std::vector<std::string> ids =
- base::SplitString(edit->getStyleSheetId(), "_", base::TRIM_WHITESPACE,
- base::SPLIT_WANT_NONEMPTY);
- if (ids.size() < 2 || !base::StringToInt(ids[0], &node_id) ||
- !base::StringToInt(ids[1], &stylesheet_id))
- return Response::ServerError("Invalid stylesheet id");
- UIElement* ui_element = dom_agent_->GetElementFromNodeId(node_id);
- if (!ui_element)
- return Response::ServerError("Node id not found");
- // Handle setting properties from metadata for elements which use metadata.
- if (!ui_element->SetPropertiesFromString(edit->getText())) {
- gfx::Rect updated_bounds;
- bool visible = false;
- if (!GetPropertiesForUIElement(ui_element, &updated_bounds, &visible))
- return NodeNotFoundError(node_id);
- Response response(
- ParseProperties(edit->getText(), &updated_bounds, &visible));
- if (!response.IsSuccess())
- return response;
- if (!SetPropertiesForUIElement(ui_element, updated_bounds, visible))
- return NodeNotFoundError(node_id);
- }
- updated_styles->emplace_back(BuildCSSStyle(
- edit->getStyleSheetId(), GetClassPropertiesWithBounds(ui_element)
- .at(stylesheet_id)
- .properties_));
- }
- *result = std::move(updated_styles);
- return Response::Success();
- }
- void CSSAgent::OnElementBoundsChanged(UIElement* ui_element) {
- InvalidateStyleSheet(ui_element);
- }
- void CSSAgent::InvalidateStyleSheet(UIElement* ui_element) {
- // The stylesheetId for each node is equivalent to a string of its
- // node_id + "_" + index of CSS::RuleMatch in vector.
- frontend()->styleSheetChanged(BuildStylesheetUId(
- ui_element->node_id(), ui_element->GetBaseStylesheetId()));
- }
- bool CSSAgent::GetPropertiesForUIElement(UIElement* ui_element,
- gfx::Rect* bounds,
- bool* visible) {
- if (ui_element) {
- ui_element->GetBounds(bounds);
- if (ui_element->type() != VIEW)
- ui_element->GetVisible(visible);
- return true;
- }
- return false;
- }
- bool CSSAgent::SetPropertiesForUIElement(UIElement* ui_element,
- const gfx::Rect& bounds,
- bool visible) {
- if (ui_element) {
- ui_element->SetBounds(bounds);
- ui_element->SetVisible(visible);
- return true;
- }
- return false;
- }
- std::unique_ptr<Array<CSS::RuleMatch>> CSSAgent::BuildMatchedStyles(
- UIElement* ui_element) {
- auto result = std::make_unique<Array<CSS::RuleMatch>>();
- std::vector<UIElement::ClassProperties> properties_vector =
- GetClassPropertiesWithBounds(ui_element);
- for (size_t i = 0; i < properties_vector.size(); i++) {
- result->emplace_back(
- CSS::RuleMatch::create()
- .setRule(BuildCSSRule(BuildStylesheetUId(ui_element->node_id(), i),
- properties_vector[i]))
- .setMatchingSelectors(BuildDefaultMatchingSelectors())
- .build());
- }
- if (!ui_element->header_sent()) {
- InitStylesheetHeaders(ui_element);
- }
- return result;
- }
- void CSSAgent::InitStylesheetHeaders(UIElement* ui_element) {
- std::vector<UIElement::Source> sources = ui_element->GetSources();
- for (size_t i = 0; i < sources.size(); i++) {
- frontend()->styleSheetAdded(BuildObjectForStyleSheetInfo(
- BuildStylesheetUId(ui_element->node_id(), i), sources[i].path_,
- sources[i].line_));
- }
- ui_element->set_header_sent();
- }
- } // namespace ui_devtools
|