blob: 452a2564df34da3f7e52f9d5ce6c23568c8cb8f3 [file] [log] [blame]
/*
* Copyright (C) 2010, Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
#include "third_party/blink/renderer/core/inspector/inspector_css_agent.h"
#include <utility>
#include "third_party/blink/public/common/metrics/document_update_reason.h"
#include "third_party/blink/renderer/core/animation/css/css_animation_data.h"
#include "third_party/blink/renderer/core/css/cascade_layer.h"
#include "third_party/blink/renderer/core/css/cascade_layer_map.h"
#include "third_party/blink/renderer/core/css/check_pseudo_has_cache_scope.h"
#include "third_party/blink/renderer/core/css/css_color.h"
#include "third_party/blink/renderer/core/css/css_computed_style_declaration.h"
#include "third_party/blink/renderer/core/css/css_container_rule.h"
#include "third_party/blink/renderer/core/css/css_default_style_sheets.h"
#include "third_party/blink/renderer/core/css/css_font_face.h"
#include "third_party/blink/renderer/core/css/css_font_face_source.h"
#include "third_party/blink/renderer/core/css/css_font_palette_values_rule.h"
#include "third_party/blink/renderer/core/css/css_font_selector.h"
#include "third_party/blink/renderer/core/css/css_gradient_value.h"
#include "third_party/blink/renderer/core/css/css_import_rule.h"
#include "third_party/blink/renderer/core/css/css_keyframe_rule.h"
#include "third_party/blink/renderer/core/css/css_keyframes_rule.h"
#include "third_party/blink/renderer/core/css/css_layer_block_rule.h"
#include "third_party/blink/renderer/core/css/css_layer_statement_rule.h"
#include "third_party/blink/renderer/core/css/css_media_rule.h"
#include "third_party/blink/renderer/core/css/css_property_name.h"
#include "third_party/blink/renderer/core/css/css_property_names.h"
#include "third_party/blink/renderer/core/css/css_property_rule.h"
#include "third_party/blink/renderer/core/css/css_property_value_set.h"
#include "third_party/blink/renderer/core/css/css_rule.h"
#include "third_party/blink/renderer/core/css/css_rule_list.h"
#include "third_party/blink/renderer/core/css/css_scope_rule.h"
#include "third_party/blink/renderer/core/css/css_style_rule.h"
#include "third_party/blink/renderer/core/css/css_style_sheet.h"
#include "third_party/blink/renderer/core/css/css_supports_rule.h"
#include "third_party/blink/renderer/core/css/css_variable_data.h"
#include "third_party/blink/renderer/core/css/font_face.h"
#include "third_party/blink/renderer/core/css/font_size_functions.h"
#include "third_party/blink/renderer/core/css/media_list.h"
#include "third_party/blink/renderer/core/css/media_query.h"
#include "third_party/blink/renderer/core/css/media_values.h"
#include "third_party/blink/renderer/core/css/parser/css_parser.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
#include "third_party/blink/renderer/core/css/properties/computed_style_utils.h"
#include "third_party/blink/renderer/core/css/properties/css_property.h"
#include "third_party/blink/renderer/core/css/properties/css_property_ref.h"
#include "third_party/blink/renderer/core/css/resolver/scoped_style_resolver.h"
#include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
#include "third_party/blink/renderer/core/css/resolver/style_rule_usage_tracker.h"
#include "third_party/blink/renderer/core/css/style_change_reason.h"
#include "third_party/blink/renderer/core/css/style_engine.h"
#include "third_party/blink/renderer/core/css/style_rule.h"
#include "third_party/blink/renderer/core/css/style_rule_font_palette_values.h"
#include "third_party/blink/renderer/core/css/style_sheet.h"
#include "third_party/blink/renderer/core/css/style_sheet_contents.h"
#include "third_party/blink/renderer/core/css/style_sheet_list.h"
#include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/dom/dom_node_ids.h"
#include "third_party/blink/renderer/core/dom/flat_tree_traversal.h"
#include "third_party/blink/renderer/core/dom/layout_tree_builder_traversal.h"
#include "third_party/blink/renderer/core/dom/node.h"
#include "third_party/blink/renderer/core/dom/pseudo_element.h"
#include "third_party/blink/renderer/core/dom/slot_assignment_engine.h"
#include "third_party/blink/renderer/core/dom/text.h"
#include "third_party/blink/renderer/core/dom/tree_scope.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/html/html_document.h"
#include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
#include "third_party/blink/renderer/core/html/html_head_element.h"
#include "third_party/blink/renderer/core/inspector/identifiers_factory.h"
#include "third_party/blink/renderer/core/inspector/inspected_frames.h"
#include "third_party/blink/renderer/core/inspector/inspector_contrast.h"
#include "third_party/blink/renderer/core/inspector/inspector_history.h"
#include "third_party/blink/renderer/core/inspector/inspector_network_agent.h"
#include "third_party/blink/renderer/core/inspector/inspector_resource_container.h"
#include "third_party/blink/renderer/core/inspector/inspector_resource_content_loader.h"
#include "third_party/blink/renderer/core/inspector/inspector_style_resolver.h"
#include "third_party/blink/renderer/core/inspector/protocol/css.h"
#include "third_party/blink/renderer/core/layout/hit_test_result.h"
#include "third_party/blink/renderer/core/layout/inline/inline_cursor.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/core/layout/layout_object_inlines.h"
#include "third_party/blink/renderer/core/layout/layout_text.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/loader/document_loader.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/style/computed_style_constants.h"
#include "third_party/blink/renderer/core/style/style_generated_image.h"
#include "third_party/blink/renderer/core/style/style_image.h"
#include "third_party/blink/renderer/core/style_property_shorthand.h"
#include "third_party/blink/renderer/core/svg/svg_element.h"
#include "third_party/blink/renderer/core/view_transition/view_transition.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/fonts/font.h"
#include "third_party/blink/renderer/platform/fonts/font_cache.h"
#include "third_party/blink/renderer/platform/fonts/font_custom_platform_data.h"
#include "third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.h"
#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_view.h"
#include "third_party/blink/renderer/platform/heap/collection_support/clear_collection_scope.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/text/text_run.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/casting.h"
#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
#include "third_party/blink/renderer/platform/wtf/text/string_concatenate.h"
namespace blink {
namespace {
int g_frontend_operation_counter = 0;
class FrontendOperationScope {
STACK_ALLOCATED();
public:
FrontendOperationScope() { ++g_frontend_operation_counter; }
~FrontendOperationScope() { --g_frontend_operation_counter; }
};
Element* GetPseudoIdAndTag(Element* element,
PseudoId& element_pseudo_id,
AtomicString& view_transition_name) {
auto* resolved_element = element;
if (auto* pseudo_element = DynamicTo<PseudoElement>(element)) {
resolved_element = IsTransitionPseudoElement(pseudo_element->GetPseudoId())
? pseudo_element->OriginatingElement()
: pseudo_element->ParentOrShadowHostElement();
// TODO(khushalsagar) : This should never be null.
if (!resolved_element)
return nullptr;
element_pseudo_id = pseudo_element->GetPseudoId();
view_transition_name = pseudo_element->view_transition_name();
}
return resolved_element;
}
String CreateShorthandValue(Document& document,
const String& shorthand,
const String& old_text,
const String& longhand,
const String& new_value) {
auto* style_sheet_contents =
MakeGarbageCollected<StyleSheetContents>(StrictCSSParserContext(
document.GetExecutionContext()->GetSecureContextMode()));
String text = " div { " + shorthand + ": " + old_text + "; }";
CSSParser::ParseSheet(MakeGarbageCollected<CSSParserContext>(document),
style_sheet_contents, text);
auto* style_sheet = MakeGarbageCollected<CSSStyleSheet>(style_sheet_contents);
auto* rule = To<CSSStyleRule>(style_sheet->ItemInternal(0));
CSSStyleDeclaration* style = rule->style();
DummyExceptionStateForTesting exception_state;
style->setProperty(document.GetExecutionContext(), longhand, new_value,
style->getPropertyPriority(longhand), exception_state);
return style->getPropertyValue(shorthand);
}
HeapVector<Member<CSSStyleRule>> FilterDuplicateRules(
RuleIndexList* rule_list) {
HeapVector<Member<CSSStyleRule>> uniq_rules;
HeapHashSet<Member<CSSRule>> uniq_rules_set;
for (unsigned i = rule_list ? rule_list->size() : 0; i > 0; --i) {
CSSRule* rule = rule_list->at(i - 1).first;
auto* style_rule = DynamicTo<CSSStyleRule>(rule);
if (!style_rule || uniq_rules_set.Contains(rule))
continue;
uniq_rules_set.insert(rule);
uniq_rules.push_back(style_rule);
}
uniq_rules.Reverse();
return uniq_rules;
}
void CollectPlatformFontsFromRunFontDataList(
const HeapVector<ShapeResult::RunFontData>& run_font_data_list,
HashMap<std::pair<int, String>, std::pair<int, String>>* font_stats) {
for (const auto& run_font_data : run_font_data_list) {
const auto* simple_font_data = run_font_data.font_data_.Get();
String family_name = simple_font_data->PlatformData().FontFamilyName();
if (family_name.IsNull())
family_name = "";
String postscript_name =
simple_font_data->PlatformData().GetPostScriptName();
if (postscript_name.IsNull()) {
postscript_name = "";
}
auto font_key = std::make_pair(simple_font_data->IsCustomFont() ? 1 : 0,
postscript_name);
auto font_stats_it = font_stats->find(font_key);
if (font_stats_it == font_stats->end()) {
font_stats->insert(
font_key, std::make_pair(run_font_data.glyph_count_, family_name));
} else {
font_stats_it->value.first += run_font_data.glyph_count_;
}
}
}
} // namespace
typedef blink::protocol::CSS::Backend::EnableCallback EnableCallback;
typedef blink::protocol::CSS::Backend::TakeComputedStyleUpdatesCallback
TakeComputedStyleUpdatesCallback;
enum ForcePseudoClassFlags {
kPseudoNone = 0,
kPseudoHover = 1 << 0,
kPseudoFocus = 1 << 1,
kPseudoActive = 1 << 2,
kPseudoVisited = 1 << 3,
kPseudoFocusWithin = 1 << 4,
kPseudoFocusVisible = 1 << 5,
kPseudoTarget = 1 << 6,
};
static unsigned ComputePseudoClassMask(
std::unique_ptr<protocol::Array<String>> pseudo_class_array) {
DEFINE_STATIC_LOCAL(String, active, ("active"));
DEFINE_STATIC_LOCAL(String, hover, ("hover"));
DEFINE_STATIC_LOCAL(String, focus, ("focus"));
DEFINE_STATIC_LOCAL(String, focusVisible, ("focus-visible"));
DEFINE_STATIC_LOCAL(String, focusWithin, ("focus-within"));
DEFINE_STATIC_LOCAL(String, target, ("target"));
DEFINE_STATIC_LOCAL(String, visited, ("visited"));
if (!pseudo_class_array || pseudo_class_array->empty())
return kPseudoNone;
unsigned result = kPseudoNone;
for (const String& pseudo_class : *pseudo_class_array) {
if (pseudo_class == active)
result |= kPseudoActive;
else if (pseudo_class == hover)
result |= kPseudoHover;
else if (pseudo_class == focus)
result |= kPseudoFocus;
else if (pseudo_class == focusVisible)
result |= kPseudoFocusVisible;
else if (pseudo_class == focusWithin)
result |= kPseudoFocusWithin;
else if (pseudo_class == target)
result |= kPseudoTarget;
else if (pseudo_class == visited)
result |= kPseudoVisited;
}
return result;
}
class InspectorCSSAgent::StyleSheetAction : public InspectorHistory::Action {
public:
StyleSheetAction(const String& name) : InspectorHistory::Action(name) {}
StyleSheetAction(const StyleSheetAction&) = delete;
StyleSheetAction& operator=(const StyleSheetAction&) = delete;
virtual std::unique_ptr<protocol::CSS::CSSStyle> TakeSerializedStyle(
Element* element) {
return nullptr;
}
};
class InspectorCSSAgent::SetStyleSheetTextAction final
: public InspectorCSSAgent::StyleSheetAction {
public:
SetStyleSheetTextAction(InspectorStyleSheetBase* style_sheet,
const String& text)
: InspectorCSSAgent::StyleSheetAction("SetStyleSheetText"),
style_sheet_(style_sheet),
text_(text) {}
SetStyleSheetTextAction(const SetStyleSheetTextAction&) = delete;
SetStyleSheetTextAction& operator=(const SetStyleSheetTextAction&) = delete;
bool Perform(ExceptionState& exception_state) override {
if (!style_sheet_->GetText(&old_text_))
return false;
return Redo(exception_state);
}
bool Undo(ExceptionState& exception_state) override {
return style_sheet_->SetText(old_text_, exception_state);
}
bool Redo(ExceptionState& exception_state) override {
return style_sheet_->SetText(text_, exception_state);
}
String MergeId() override {
return String::Format("SetStyleSheetText %s",
style_sheet_->Id().Utf8().c_str());
}
void Merge(Action* action) override {
DCHECK_EQ(action->MergeId(), MergeId());
SetStyleSheetTextAction* other =
static_cast<SetStyleSheetTextAction*>(action);
text_ = other->text_;
}
void Trace(Visitor* visitor) const override {
visitor->Trace(style_sheet_);
InspectorCSSAgent::StyleSheetAction::Trace(visitor);
}
private:
Member<InspectorStyleSheetBase> style_sheet_;
String text_;
String old_text_;
};
class InspectorCSSAgent::ModifyRuleAction final
: public InspectorCSSAgent::StyleSheetAction {
public:
enum Type {
kSetRuleSelector,
kSetStyleText,
kSetMediaRuleText,
kSetContainerRuleText,
kSetSupportsRuleText,
kSetKeyframeKey,
kSetPropertyName,
kSetScopeRuleText,
};
ModifyRuleAction(Type type,
InspectorStyleSheet* style_sheet,
const SourceRange& range,
const String& text)
: InspectorCSSAgent::StyleSheetAction("ModifyRuleAction"),
style_sheet_(style_sheet),
type_(type),
new_text_(text),
old_range_(range),
css_rule_(nullptr) {}
ModifyRuleAction(const ModifyRuleAction&) = delete;
ModifyRuleAction& operator=(const ModifyRuleAction&) = delete;
bool Perform(ExceptionState& exception_state) override {
return Redo(exception_state);
}
bool Undo(ExceptionState& exception_state) override {
switch (type_) {
case kSetRuleSelector:
return style_sheet_->SetRuleSelector(new_range_, old_text_, nullptr,
nullptr, exception_state);
case kSetStyleText:
return style_sheet_->SetStyleText(new_range_, old_text_, nullptr,
nullptr, exception_state);
case kSetMediaRuleText:
return style_sheet_->SetMediaRuleText(new_range_, old_text_, nullptr,
nullptr, exception_state);
case kSetContainerRuleText:
return style_sheet_->SetContainerRuleText(
new_range_, old_text_, nullptr, nullptr, exception_state);
case kSetSupportsRuleText:
return style_sheet_->SetSupportsRuleText(new_range_, old_text_, nullptr,
nullptr, exception_state);
case kSetKeyframeKey:
return style_sheet_->SetKeyframeKey(new_range_, old_text_, nullptr,
nullptr, exception_state);
case kSetPropertyName:
return style_sheet_->SetPropertyName(new_range_, old_text_, nullptr,
nullptr, exception_state);
case kSetScopeRuleText:
return style_sheet_->SetScopeRuleText(new_range_, old_text_, nullptr,
nullptr, exception_state);
default:
NOTREACHED();
}
return false;
}
bool Redo(ExceptionState& exception_state) override {
switch (type_) {
case kSetRuleSelector:
css_rule_ = style_sheet_->SetRuleSelector(
old_range_, new_text_, &new_range_, &old_text_, exception_state);
break;
case kSetStyleText:
css_rule_ = style_sheet_->SetStyleText(
old_range_, new_text_, &new_range_, &old_text_, exception_state);
break;
case kSetMediaRuleText:
css_rule_ = style_sheet_->SetMediaRuleText(
old_range_, new_text_, &new_range_, &old_text_, exception_state);
break;
case kSetContainerRuleText:
css_rule_ = style_sheet_->SetContainerRuleText(
old_range_, new_text_, &new_range_, &old_text_, exception_state);
break;
case kSetSupportsRuleText:
css_rule_ = style_sheet_->SetSupportsRuleText(
old_range_, new_text_, &new_range_, &old_text_, exception_state);
break;
case kSetKeyframeKey:
css_rule_ = style_sheet_->SetKeyframeKey(
old_range_, new_text_, &new_range_, &old_text_, exception_state);
break;
case kSetPropertyName:
css_rule_ = style_sheet_->SetPropertyName(
old_range_, new_text_, &new_range_, &old_text_, exception_state);
break;
case kSetScopeRuleText:
css_rule_ = style_sheet_->SetScopeRuleText(
old_range_, new_text_, &new_range_, &old_text_, exception_state);
break;
default:
NOTREACHED();
}
return css_rule_ != nullptr;
}
CSSRule* TakeRule() {
CSSRule* result = css_rule_;
css_rule_ = nullptr;
return result;
}
std::unique_ptr<protocol::CSS::CSSStyle> TakeSerializedStyle(
Element* element) override {
if (type_ != kSetStyleText)
return nullptr;
CSSRule* rule = TakeRule();
if (auto* style_rule = DynamicTo<CSSStyleRule>(rule))
return style_sheet_->BuildObjectForStyle(style_rule->style(), element);
if (auto* keyframe_rule = DynamicTo<CSSKeyframeRule>(rule))
return style_sheet_->BuildObjectForStyle(keyframe_rule->style(), element);
if (auto* property_rule = DynamicTo<CSSPropertyRule>(rule)) {
return style_sheet_->BuildObjectForStyle(property_rule->Style(), nullptr);
}
if (auto* font_palette_values_rule =
DynamicTo<CSSFontPaletteValuesRule>(rule)) {
return style_sheet_->BuildObjectForStyle(
font_palette_values_rule->Style(), nullptr);
}
if (auto* position_try_rule = DynamicTo<CSSPositionTryRule>(rule)) {
return style_sheet_->BuildObjectForStyle(position_try_rule->style(),
nullptr);
}
return nullptr;
}
void Trace(Visitor* visitor) const override {
visitor->Trace(style_sheet_);
visitor->Trace(css_rule_);
InspectorCSSAgent::StyleSheetAction::Trace(visitor);
}
String MergeId() override {
return String::Format("ModifyRuleAction:%d %s:%d", type_,
style_sheet_->Id().Utf8().c_str(), old_range_.start);
}
bool IsNoop() override { return old_text_ == new_text_; }
void Merge(Action* action) override {
DCHECK_EQ(action->MergeId(), MergeId());
ModifyRuleAction* other = static_cast<ModifyRuleAction*>(action);
new_text_ = other->new_text_;
new_range_ = other->new_range_;
}
private:
Member<InspectorStyleSheet> style_sheet_;
Type type_;
String old_text_;
String new_text_;
SourceRange old_range_;
SourceRange new_range_;
Member<CSSRule> css_rule_;
};
class InspectorCSSAgent::SetElementStyleAction final
: public InspectorCSSAgent::StyleSheetAction {
public:
SetElementStyleAction(InspectorStyleSheetForInlineStyle* style_sheet,
const String& text)
: InspectorCSSAgent::StyleSheetAction("SetElementStyleAction"),
style_sheet_(style_sheet),
text_(text) {}
SetElementStyleAction(const SetElementStyleAction&) = delete;
SetElementStyleAction& operator=(const SetElementStyleAction&) = delete;
bool Perform(ExceptionState& exception_state) override {
return Redo(exception_state);
}
bool Undo(ExceptionState& exception_state) override {
return style_sheet_->SetText(old_text_, exception_state);
}
bool Redo(ExceptionState& exception_state) override {
if (!style_sheet_->GetText(&old_text_))
return false;
return style_sheet_->SetText(text_, exception_state);
}
void Trace(Visitor* visitor) const override {
visitor->Trace(style_sheet_);
InspectorCSSAgent::StyleSheetAction::Trace(visitor);
}
String MergeId() override {
return String::Format("SetElementStyleAction:%s",
style_sheet_->Id().Utf8().c_str());
}
std::unique_ptr<protocol::CSS::CSSStyle> TakeSerializedStyle(
Element* element) override {
return style_sheet_->BuildObjectForStyle(style_sheet_->InlineStyle(),
element);
}
void Merge(Action* action) override {
DCHECK_EQ(action->MergeId(), MergeId());
SetElementStyleAction* other = static_cast<SetElementStyleAction*>(action);
text_ = other->text_;
}
private:
Member<InspectorStyleSheetForInlineStyle> style_sheet_;
String text_;
String old_text_;
};
class InspectorCSSAgent::AddRuleAction final
: public InspectorCSSAgent::StyleSheetAction {
public:
AddRuleAction(InspectorStyleSheet* style_sheet,
const String& rule_text,
const SourceRange& location)
: InspectorCSSAgent::StyleSheetAction("AddRule"),
style_sheet_(style_sheet),
rule_text_(rule_text),
location_(location) {}
AddRuleAction(const AddRuleAction&) = delete;
AddRuleAction& operator=(const AddRuleAction&) = delete;
bool Perform(ExceptionState& exception_state) override {
return Redo(exception_state);
}
bool Undo(ExceptionState& exception_state) override {
CSSStyleSheet::InspectorMutationScope scope(style_sheet_->PageStyleSheet());
return style_sheet_->DeleteRule(added_range_, exception_state);
}
bool Redo(ExceptionState& exception_state) override {
CSSStyleSheet::InspectorMutationScope scope(style_sheet_->PageStyleSheet());
css_rule_ = style_sheet_->AddRule(rule_text_, location_, &added_range_,
exception_state);
if (exception_state.HadException())
return false;
return true;
}
CSSStyleRule* TakeRule() {
CSSStyleRule* result = css_rule_;
css_rule_ = nullptr;
return result;
}
void Trace(Visitor* visitor) const override {
visitor->Trace(style_sheet_);
visitor->Trace(css_rule_);
InspectorCSSAgent::StyleSheetAction::Trace(visitor);
}
private:
Member<InspectorStyleSheet> style_sheet_;
Member<CSSStyleRule> css_rule_;
String rule_text_;
String old_text_;
SourceRange location_;
SourceRange added_range_;
};
// static
CSSStyleRule* InspectorCSSAgent::AsCSSStyleRule(CSSRule* rule) {
return DynamicTo<CSSStyleRule>(rule);
}
// static
CSSMediaRule* InspectorCSSAgent::AsCSSMediaRule(CSSRule* rule) {
return DynamicTo<CSSMediaRule>(rule);
}
// static
CSSContainerRule* InspectorCSSAgent::AsCSSContainerRule(CSSRule* rule) {
return DynamicTo<CSSContainerRule>(rule);
}
// static
CSSSupportsRule* InspectorCSSAgent::AsCSSSupportsRule(CSSRule* rule) {
return DynamicTo<CSSSupportsRule>(rule);
}
// static
CSSScopeRule* InspectorCSSAgent::AsCSSScopeRule(CSSRule* rule) {
return DynamicTo<CSSScopeRule>(rule);
}
InspectorCSSAgent::InspectorCSSAgent(
InspectorDOMAgent* dom_agent,
InspectedFrames* inspected_frames,
InspectorNetworkAgent* network_agent,
InspectorResourceContentLoader* resource_content_loader,
InspectorResourceContainer* resource_container)
: dom_agent_(dom_agent),
inspected_frames_(inspected_frames),
network_agent_(network_agent),
resource_content_loader_(resource_content_loader),
resource_container_(resource_container),
resource_content_loader_client_id_(
resource_content_loader->CreateClientId()),
enable_requested_(&agent_state_, /*default_value=*/false),
enable_completed_(false),
coverage_enabled_(&agent_state_, /*default_value=*/false),
local_fonts_enabled_(&agent_state_, /*default_value=*/true) {
DCHECK(dom_agent);
DCHECK(network_agent);
}
InspectorCSSAgent::~InspectorCSSAgent() = default;
void InspectorCSSAgent::Restore() {
if (enable_requested_.Get())
CompleteEnabled();
if (coverage_enabled_.Get())
SetCoverageEnabled(true);
}
void InspectorCSSAgent::FlushPendingProtocolNotifications() {
if (!invalidated_documents_.size())
return;
HeapHashSet<Member<Document>> invalidated_documents;
invalidated_documents_.swap(invalidated_documents);
for (Document* document : invalidated_documents)
UpdateActiveStyleSheets(document);
}
void InspectorCSSAgent::Reset() {
id_to_inspector_style_sheet_.clear();
id_to_inspector_style_sheet_for_inline_style_.clear();
css_style_sheet_to_inspector_style_sheet_.clear();
document_to_css_style_sheets_.clear();
invalidated_documents_.clear();
node_to_inspector_style_sheet_.clear();
ResetNonPersistentData();
}
void InspectorCSSAgent::ResetNonPersistentData() {
ResetPseudoStates();
}
void InspectorCSSAgent::enable(std::unique_ptr<EnableCallback> prp_callback) {
if (!dom_agent_->Enabled()) {
prp_callback->sendFailure(protocol::Response::ServerError(
"DOM agent needs to be enabled first."));
return;
}
enable_requested_.Set(true);
resource_content_loader_->EnsureResourcesContentLoaded(
resource_content_loader_client_id_,
WTF::BindOnce(&InspectorCSSAgent::ResourceContentLoaded,
WrapPersistent(this), std::move(prp_callback)));
}
void InspectorCSSAgent::ResourceContentLoaded(
std::unique_ptr<EnableCallback> callback) {
if (enable_requested_.Get()) // Could be disabled while fetching resources.
CompleteEnabled();
callback->sendSuccess();
}
void InspectorCSSAgent::CompleteEnabled() {
instrumenting_agents_->AddInspectorCSSAgent(this);
dom_agent_->AddDOMListener(this);
HeapVector<Member<Document>> documents = dom_agent_->Documents();
for (Document* document : documents) {
UpdateActiveStyleSheets(document);
TriggerFontsUpdatedForDocument(document);
}
enable_completed_ = true;
}
void InspectorCSSAgent::TriggerFontsUpdatedForDocument(Document* document) {
const HeapLinkedHashSet<Member<FontFace>>& faces =
document->GetStyleEngine()
.GetFontSelector()
->GetFontFaceCache()
->CssConnectedFontFaces();
for (FontFace* face : faces) {
CSSFontFace* css_face = face->CssFontFace();
if (!css_face)
continue;
const CSSFontFaceSource* source = css_face->FrontSource();
if (!source || !source->IsLoaded())
continue;
const FontCustomPlatformData* data = source->GetCustomPlaftormData();
if (!data)
continue;
FontsUpdated(face, source->GetURL(), data);
}
}
protocol::Response InspectorCSSAgent::disable() {
Reset();
dom_agent_->RemoveDOMListener(this);
instrumenting_agents_->RemoveInspectorCSSAgent(this);
enable_completed_ = false;
enable_requested_.Set(false);
resource_content_loader_->Cancel(resource_content_loader_client_id_);
coverage_enabled_.Set(false);
local_fonts_enabled_.Set(true);
SetCoverageEnabled(false);
return protocol::Response::Success();
}
void InspectorCSSAgent::DidCommitLoadForLocalFrame(LocalFrame* frame) {
if (frame == inspected_frames_->Root())
Reset();
}
void InspectorCSSAgent::MediaQueryResultChanged() {
FlushPendingProtocolNotifications();
GetFrontend()->mediaQueryResultChanged();
}
void InspectorCSSAgent::FontsUpdated(
const FontFace* font,
const String& src,
const FontCustomPlatformData* fontCustomPlatformData) {
FlushPendingProtocolNotifications();
if (!(font && fontCustomPlatformData)) {
GetFrontend()->fontsUpdated();
return;
}
Vector<VariationAxis> variation_axis =
fontCustomPlatformData->GetVariationAxes();
auto variation_axes =
std::make_unique<protocol::Array<protocol::CSS::FontVariationAxis>>();
for (const VariationAxis& axis : variation_axis) {
variation_axes->push_back(protocol::CSS::FontVariationAxis::create()
.setMinValue(axis.minValue)
.setMaxValue(axis.maxValue)
.setDefaultValue(axis.defaultValue)
.setName(axis.name)
.setTag(axis.tag)
.build());
}
// blink::FontFace returns sane property defaults per the web fonts spec,
// so we don't perform null checks here.
std::unique_ptr<protocol::CSS::FontFace> font_face =
protocol::CSS::FontFace::create()
.setFontFamily(font->family())
.setFontStyle(font->style())
.setFontVariant(font->variant())
.setFontWeight(font->weight())
.setFontStretch(font->stretch())
.setFontDisplay(font->display())
.setUnicodeRange(font->unicodeRange())
.setSrc(src)
.setPlatformFontFamily(
fontCustomPlatformData->FamilyNameForInspector())
.setFontVariationAxes(
variation_axes->size() ? std::move(variation_axes) : nullptr)
.build();
GetFrontend()->fontsUpdated(std::move(font_face));
}
void InspectorCSSAgent::ActiveStyleSheetsUpdated(Document* document) {
invalidated_documents_.insert(document);
}
void InspectorCSSAgent::UpdateActiveStyleSheets(Document* document) {
HeapVector<Member<CSSStyleSheet>> new_sheets_vector;
InspectorCSSAgent::CollectAllDocumentStyleSheets(document, new_sheets_vector);
SetActiveStyleSheets(document, new_sheets_vector);
}
void InspectorCSSAgent::SetActiveStyleSheets(
Document* document,
const HeapVector<Member<CSSStyleSheet>>& all_sheets_vector) {
auto it = document_to_css_style_sheets_.find(document);
HeapHashSet<Member<CSSStyleSheet>>* document_css_style_sheets = nullptr;
if (it != document_to_css_style_sheets_.end()) {
document_css_style_sheets = it->value;
} else {
document_css_style_sheets =
MakeGarbageCollected<HeapHashSet<Member<CSSStyleSheet>>>();
document_to_css_style_sheets_.Set(document, document_css_style_sheets);
}
// Style engine sometimes returns the same stylesheet multiple
// times, probably, because it's used in multiple places.
// We need to deduplicate because the frontend does not expect
// duplicate styleSheetAdded events.
HeapHashSet<Member<CSSStyleSheet>> unique_sheets;
for (CSSStyleSheet* css_style_sheet : all_sheets_vector) {
if (!unique_sheets.Contains(css_style_sheet))
unique_sheets.insert(css_style_sheet);
}
HeapHashSet<Member<CSSStyleSheet>> removed_sheets(*document_css_style_sheets);
HeapVector<Member<CSSStyleSheet>> added_sheets;
for (CSSStyleSheet* css_style_sheet : unique_sheets) {
if (removed_sheets.Contains(css_style_sheet)) {
removed_sheets.erase(css_style_sheet);
} else {
added_sheets.push_back(css_style_sheet);
}
}
for (CSSStyleSheet* css_style_sheet : removed_sheets) {
InspectorStyleSheet* inspector_style_sheet =
css_style_sheet_to_inspector_style_sheet_.at(css_style_sheet);
document_css_style_sheets->erase(css_style_sheet);
if (id_to_inspector_style_sheet_.Contains(inspector_style_sheet->Id())) {
String id = UnbindStyleSheet(inspector_style_sheet);
if (GetFrontend())
GetFrontend()->styleSheetRemoved(id);
}
}
for (CSSStyleSheet* css_style_sheet : added_sheets) {
InspectorStyleSheet* new_style_sheet = BindStyleSheet(css_style_sheet);
document_css_style_sheets->insert(css_style_sheet);
new_style_sheet->SyncTextIfNeeded();
if (GetFrontend()) {
GetFrontend()->styleSheetAdded(
new_style_sheet->BuildObjectForStyleSheetInfo());
}
}
if (document_css_style_sheets->empty())
document_to_css_style_sheets_.erase(document);
}
void InspectorCSSAgent::DocumentDetached(Document* document) {
invalidated_documents_.erase(document);
SetActiveStyleSheets(document, HeapVector<Member<CSSStyleSheet>>());
}
void InspectorCSSAgent::ForcePseudoState(Element* element,
CSSSelector::PseudoType pseudo_type,
bool* result) {
if (node_id_to_forced_pseudo_state_.empty())
return;
int node_id = dom_agent_->BoundNodeId(element);
if (!node_id)
return;
// First check whether focus-within was set because focus or focus-within was
// forced for a child node.
NodeIdToNumberFocusedChildren::iterator focused_it =
node_id_to_number_focused_children_.find(node_id);
unsigned focused_count =
focused_it == node_id_to_number_focused_children_.end()
? 0
: focused_it->value;
if (pseudo_type == CSSSelector::kPseudoFocusWithin && focused_count > 0) {
*result = true;
return;
}
NodeIdToForcedPseudoState::iterator it =
node_id_to_forced_pseudo_state_.find(node_id);
if (it == node_id_to_forced_pseudo_state_.end())
return;
bool force = false;
unsigned forced_pseudo_state = it->value;
switch (pseudo_type) {
case CSSSelector::kPseudoActive:
force = forced_pseudo_state & kPseudoActive;
break;
case CSSSelector::kPseudoFocus:
force = forced_pseudo_state & kPseudoFocus;
break;
case CSSSelector::kPseudoFocusWithin:
force = forced_pseudo_state & kPseudoFocusWithin;
break;
case CSSSelector::kPseudoFocusVisible:
force = forced_pseudo_state & kPseudoFocusVisible;
break;
case CSSSelector::kPseudoHover:
force = forced_pseudo_state & kPseudoHover;
break;
case CSSSelector::kPseudoTarget:
force = forced_pseudo_state & kPseudoTarget;
break;
case CSSSelector::kPseudoVisited:
force = forced_pseudo_state & kPseudoVisited;
break;
default:
break;
}
if (force)
*result = true;
}
protocol::Response InspectorCSSAgent::getMediaQueries(
std::unique_ptr<protocol::Array<protocol::CSS::CSSMedia>>* medias) {
*medias = std::make_unique<protocol::Array<protocol::CSS::CSSMedia>>();
for (auto& style : id_to_inspector_style_sheet_) {
InspectorStyleSheet* style_sheet = style.value;
CollectMediaQueriesFromStyleSheet(style_sheet->PageStyleSheet(),
medias->get(), nullptr);
const CSSRuleVector& flat_rules = style_sheet->FlatRules();
for (unsigned i = 0; i < flat_rules.size(); ++i) {
CSSRule* rule = flat_rules.at(i).Get();
if (rule->GetType() == CSSRule::kMediaRule ||
rule->GetType() == CSSRule::kImportRule)
CollectMediaQueriesFromRule(rule, medias->get(), nullptr);
}
}
return protocol::Response::Success();
}
std::unique_ptr<protocol::CSS::CSSLayerData>
InspectorCSSAgent::BuildLayerDataObject(const CascadeLayer* layer,
unsigned& max_order) {
const unsigned order = layer->GetOrder().value_or(0);
max_order = max(max_order, order);
std::unique_ptr<protocol::CSS::CSSLayerData> layer_data =
protocol::CSS::CSSLayerData::create()
.setName(layer->GetName())
.setOrder(order)
.build();
const auto& sublayers = layer->GetDirectSubLayers();
if (sublayers.empty())
return layer_data;
auto sublayers_data =
std::make_unique<protocol::Array<protocol::CSS::CSSLayerData>>();
for (const CascadeLayer* sublayer : sublayers)
sublayers_data->emplace_back(BuildLayerDataObject(sublayer, max_order));
layer_data->setSubLayers(std::move(sublayers_data));
return layer_data;
}
protocol::Response InspectorCSSAgent::getLayersForNode(
int node_id,
std::unique_ptr<protocol::CSS::CSSLayerData>* root_layer) {
Element* element = nullptr;
const protocol::Response response =
dom_agent_->AssertElement(node_id, element);
if (!response.IsSuccess())
return response;
*root_layer = protocol::CSS::CSSLayerData::create()
.setName("implicit outer layer")
.setOrder(0)
.build();
const auto* scoped_resolver =
element->GetTreeScope().GetScopedStyleResolver();
// GetScopedStyleResolver returns a nullptr if the tree scope has no
// stylesheets.
if (!scoped_resolver)
return protocol::Response::Success();
const CascadeLayerMap* layer_map = scoped_resolver->GetCascadeLayerMap();
if (!layer_map)
return protocol::Response::Success();
const CascadeLayer* root = layer_map->GetRootLayer();
unsigned max_order = 0;
auto sublayers_data =
std::make_unique<protocol::Array<protocol::CSS::CSSLayerData>>();
for (const auto& sublayer : root->GetDirectSubLayers())
sublayers_data->emplace_back(BuildLayerDataObject(sublayer, max_order));
(*root_layer)->setOrder(max_order + 1);
(*root_layer)->setSubLayers(std::move(sublayers_data));
return protocol::Response::Success();
}
protocol::Response InspectorCSSAgent::getLocationForSelector(
const String& style_sheet_id,
const String& selector_text,
std::unique_ptr<protocol::Array<protocol::CSS::SourceRange>>* ranges) {
InspectorStyleSheet* style_sheet = nullptr;
protocol::Response response =
AssertInspectorStyleSheetForId(style_sheet_id, style_sheet);
if (response.IsError()) {
return response;
}
*ranges = std::make_unique<protocol::Array<protocol::CSS::SourceRange>>();
const CSSRuleVector& css_rules = style_sheet->FlatRules();
for (auto css_rule : css_rules) {
CSSStyleRule* css_style_rule = DynamicTo<CSSStyleRule>(css_rule.Get());
if (css_style_rule == nullptr) {
continue;
}
const String curr_selector_text = css_style_rule->selectorText();
if (selector_text != curr_selector_text) {
continue;
}
const CSSRuleSourceData* source_data =
style_sheet->SourceDataForRule(css_style_rule);
std::unique_ptr<protocol::CSS::SourceRange> range =
style_sheet->BuildSourceRangeObject(source_data->rule_header_range);
const CSSStyleSheet* page_style_sheet = style_sheet->PageStyleSheet();
const TextPosition start_position =
page_style_sheet->StartPositionInSource();
if (range->getStartLine() == 0) {
range->setStartColumn(range->getStartColumn() +
start_position.column_.ZeroBasedInt());
}
if (range->getEndLine() == 0) {
range->setEndColumn(range->getEndColumn() +
start_position.column_.ZeroBasedInt());
}
range->setStartLine(range->getStartLine() +
start_position.line_.ZeroBasedInt());
range->setEndLine(range->getEndLine() +
start_position.line_.ZeroBasedInt());
(*ranges)->emplace_back(std::move(range));
}
if ((*ranges)->empty()) {
String message = "Failed to find selector '" + selector_text +
"' in style sheet " + style_sheet->FinalURL();
return protocol::Response::InvalidParams(message.Utf8());
}
return protocol::Response::Success();
}
protocol::Response InspectorCSSAgent::getMatchedStylesForNode(
int node_id,
Maybe<protocol::CSS::CSSStyle>* inline_style,
Maybe<protocol::CSS::CSSStyle>* attributes_style,
Maybe<protocol::Array<protocol::CSS::RuleMatch>>* matched_css_rules,
Maybe<protocol::Array<protocol::CSS::PseudoElementMatches>>*
pseudo_id_matches,
Maybe<protocol::Array<protocol::CSS::InheritedStyleEntry>>*
inherited_entries,
Maybe<protocol::Array<protocol::CSS::InheritedPseudoElementMatches>>*
inherited_pseudo_id_matches,
Maybe<protocol::Array<protocol::CSS::CSSKeyframesRule>>*
css_keyframes_rules,
Maybe<protocol::Array<protocol::CSS::CSSPositionFallbackRule>>*
css_position_fallback_rules,
Maybe<protocol::Array<protocol::CSS::CSSPositionTryRule>>*
css_position_try_rules,
Maybe<protocol::Array<protocol::CSS::CSSPropertyRule>>* css_property_rules,
Maybe<protocol::Array<protocol::CSS::CSSPropertyRegistration>>*
css_property_registrations,
Maybe<protocol::CSS::CSSFontPaletteValuesRule>*
css_font_palette_values_rule,
Maybe<int>* parentLayoutNodeId) {
protocol::Response response = AssertEnabled();
if (!response.IsSuccess())
return response;
Element* element = nullptr;
response = dom_agent_->AssertElement(node_id, element);
if (!response.IsSuccess())
return response;
Element* animating_element = element;
PseudoId element_pseudo_id = kPseudoIdNone;
AtomicString view_transition_name = g_null_atom;
// If the requested element is a pseudo element, `element` becomes
// the first non-pseudo parent element or shadow host element
// after `GetPseudoIdAndTag` call below.
element = GetPseudoIdAndTag(element, element_pseudo_id, view_transition_name);
if (!element)
return protocol::Response::ServerError("Pseudo element has no parent");
Document& document = element->GetDocument();
// A non-active document has no styles.
if (!document.IsActive())
return protocol::Response::ServerError("Document is not active");
// The source text of mutable stylesheets needs to be updated
// to sync the latest changes.
for (InspectorStyleSheet* stylesheet :
css_style_sheet_to_inspector_style_sheet_.Values()) {
stylesheet->SyncTextIfNeeded();
}
CheckPseudoHasCacheScope check_pseudo_has_cache_scope(
&document, /*within_selector_checking=*/false);
InspectorStyleResolver resolver(element, element_pseudo_id,
view_transition_name);
// Matched rules.
*matched_css_rules =
BuildArrayForMatchedRuleList(resolver.MatchedRules(), element,
element_pseudo_id, view_transition_name);
// Inherited styles.
*inherited_entries =
std::make_unique<protocol::Array<protocol::CSS::InheritedStyleEntry>>();
for (InspectorCSSMatchedRules* match : resolver.ParentRules()) {
std::unique_ptr<protocol::CSS::InheritedStyleEntry> entry =
protocol::CSS::InheritedStyleEntry::create()
.setMatchedCSSRules(BuildArrayForMatchedRuleList(
match->matched_rules, element, element_pseudo_id,
view_transition_name))
.build();
if (match->element->style() && match->element->style()->length()) {
InspectorStyleSheetForInlineStyle* style_sheet =
AsInspectorStyleSheet(match->element);
if (style_sheet) {
entry->setInlineStyle(style_sheet->BuildObjectForStyle(
style_sheet->InlineStyle(), element, element_pseudo_id,
view_transition_name));
}
}
inherited_entries->value().emplace_back(std::move(entry));
}
*css_keyframes_rules = AnimationsForNode(element, animating_element);
std::tie(*css_property_rules, *css_property_registrations) =
CustomPropertiesForNode(element);
// Pseudo elements.
if (element_pseudo_id)
return protocol::Response::Success();
InspectorStyleSheetForInlineStyle* inline_style_sheet =
AsInspectorStyleSheet(element);
if (inline_style_sheet) {
*inline_style =
inline_style_sheet->BuildObjectForStyle(element->style(), element);
*attributes_style = BuildObjectForAttributesStyle(element);
}
*pseudo_id_matches =
std::make_unique<protocol::Array<protocol::CSS::PseudoElementMatches>>();
for (InspectorCSSMatchedRules* match : resolver.PseudoElementRules()) {
pseudo_id_matches->value().emplace_back(
protocol::CSS::PseudoElementMatches::create()
.setPseudoType(
InspectorDOMAgent::ProtocolPseudoElementType(match->pseudo_id))
.setMatches(BuildArrayForMatchedRuleList(
match->matched_rules, element, match->pseudo_id,
match->view_transition_name))
.build());
if (match->view_transition_name) {
pseudo_id_matches->value().back()->setPseudoIdentifier(
match->view_transition_name);
}
}
*inherited_pseudo_id_matches = std::make_unique<
protocol::Array<protocol::CSS::InheritedPseudoElementMatches>>();
for (InspectorCSSMatchedPseudoElements* match :
resolver.ParentPseudoElementRules()) {
auto parent_pseudo_element_matches = std::make_unique<
protocol::Array<protocol::CSS::PseudoElementMatches>>();
for (InspectorCSSMatchedRules* pseudo_match : match->pseudo_element_rules) {
parent_pseudo_element_matches->emplace_back(
protocol::CSS::PseudoElementMatches::create()
.setPseudoType(InspectorDOMAgent::ProtocolPseudoElementType(
pseudo_match->pseudo_id))
.setMatches(BuildArrayForMatchedRuleList(
pseudo_match->matched_rules, element))
.build());
if (pseudo_match->view_transition_name) {
parent_pseudo_element_matches->back()->setPseudoIdentifier(
pseudo_match->view_transition_name);
}
}
std::unique_ptr<protocol::CSS::InheritedPseudoElementMatches>
inherited_pseudo_element_matches =
protocol::CSS::InheritedPseudoElementMatches::create()
.setPseudoElements(std::move(parent_pseudo_element_matches))
.build();
inherited_pseudo_id_matches->value().emplace_back(
std::move(inherited_pseudo_element_matches));
}
*css_position_try_rules = PositionTryRulesForElement(element);
if (auto rule = FontPalettesForNode(*element)) {
*css_font_palette_values_rule = std::move(rule);
}
auto* parentLayoutNode = LayoutTreeBuilderTraversal::LayoutParent(*element);
if (parentLayoutNode) {
if (int boundNodeId = dom_agent_->BoundNodeId(parentLayoutNode))
*parentLayoutNodeId = boundNodeId;
}
return protocol::Response::Success();
}
template <class CSSRuleCollection>
static CSSKeyframesRule* FindKeyframesRule(CSSRuleCollection* css_rules,
StyleRuleKeyframes* keyframes_rule) {
if (!css_rules) {
return nullptr;
}
CSSKeyframesRule* result = nullptr;
for (unsigned j = 0; j < css_rules->length() && !result; ++j) {
CSSRule* css_rule = css_rules->item(j);
if (auto* css_style_rule = DynamicTo<CSSKeyframesRule>(css_rule)) {
if (css_style_rule->Keyframes() == keyframes_rule)
result = css_style_rule;
} else if (auto* css_import_rule = DynamicTo<CSSImportRule>(css_rule)) {
result = FindKeyframesRule(css_import_rule->styleSheet(), keyframes_rule);
} else {
result = FindKeyframesRule(css_rule->cssRules(), keyframes_rule);
}
}
return result;
}
template <class CSSRuleCollection>
static CSSPositionTryRule* FindPositionTryRule(
CSSRuleCollection* css_rules,
StyleRulePositionTry* position_try_rule) {
if (!css_rules) {
return nullptr;
}
CSSPositionTryRule* result = nullptr;
for (unsigned i = 0; i < css_rules->length() && !result; ++i) {
CSSRule* css_rule = css_rules->item(i);
if (auto* css_style_rule = DynamicTo<CSSPositionTryRule>(css_rule)) {
if (css_style_rule->PositionTry() == position_try_rule) {
result = css_style_rule;
}
} else if (auto* css_import_rule = DynamicTo<CSSImportRule>(css_rule)) {
result =
FindPositionTryRule(css_import_rule->styleSheet(), position_try_rule);
} else {
result = FindPositionTryRule(css_rule->cssRules(), position_try_rule);
}
}
return result;
}
std::unique_ptr<protocol::Array<protocol::CSS::CSSPositionTryRule>>
InspectorCSSAgent::PositionTryRulesForElement(Element* element) {
Document& document = element->GetDocument();
CHECK(!document.NeedsLayoutTreeUpdateForNode(*element));
const ComputedStyle* style = element->EnsureComputedStyle();
if (!style) {
return nullptr;
}
const PositionTryOptions* position_try_options =
style->GetPositionTryOptions();
if (!position_try_options) {
return nullptr;
}
auto css_position_try_rules =
std::make_unique<protocol::Array<protocol::CSS::CSSPositionTryRule>>();
StyleResolver& style_resolver = document.GetStyleResolver();
for (const PositionTryOption& option : position_try_options->GetOptions()) {
if (const ScopedCSSName* scoped_name = option.GetPositionTryName()) {
const TreeScope* tree_scope = scoped_name->GetTreeScope();
if (!tree_scope) {
tree_scope = &document;
}
StyleRulePositionTry* position_try_rule =
style_resolver.ResolvePositionTryRule(tree_scope,
scoped_name->GetName());
if (!position_try_rule) {
continue;
}
// Find CSSOM wrapper from internal Style rule.
DocumentStyleSheets::iterator css_style_sheets_for_document_it =
document_to_css_style_sheets_.find(&document);
if (css_style_sheets_for_document_it ==
document_to_css_style_sheets_.end()) {
continue;
}
for (CSSStyleSheet* style_sheet :
*css_style_sheets_for_document_it->value) {
if (CSSPositionTryRule* css_position_try_rule =
FindPositionTryRule(style_sheet, position_try_rule)) {
InspectorStyleSheet* inspector_style_sheet =
BindStyleSheet(css_position_try_rule->parentStyleSheet());
css_position_try_rules->emplace_back(
inspector_style_sheet->BuildObjectForPositionTryRule(
css_position_try_rule));
break;
}
}
}
}
return css_position_try_rules;
}
template <class CSSRuleCollection>
static CSSPropertyRule* FindPropertyRule(CSSRuleCollection* css_rules,
StyleRuleProperty* property_rule) {
if (!css_rules) {
return nullptr;
}
CSSPropertyRule* result = nullptr;
for (unsigned j = 0; j < css_rules->length() && !result; ++j) {
CSSRule* css_rule = css_rules->item(j);
if (auto* css_style_rule = DynamicTo<CSSPropertyRule>(css_rule)) {
if (css_style_rule->Property() == property_rule)
result = css_style_rule;
} else if (auto* css_import_rule = DynamicTo<CSSImportRule>(css_rule)) {
result = FindPropertyRule(css_import_rule->styleSheet(), property_rule);
} else {
result = FindPropertyRule(css_rule->cssRules(), property_rule);
}
}
return result;
}
std::unique_ptr<protocol::CSS::CSSPropertyRegistration>
BuildObjectForPropertyRegistration(const AtomicString& name,
const PropertyRegistration& registration) {
auto css_property_registration =
protocol::CSS::CSSPropertyRegistration::create()
.setPropertyName(name)
.setInherits(registration.Inherits())
.setSyntax(registration.Syntax().ToString())
.build();
if (registration.Initial()) {
css_property_registration->setInitialValue(
protocol::CSS::Value::create()
.setText(registration.Initial()->CssText())
.build());
}
return css_property_registration;
}
std::pair<
std::unique_ptr<protocol::Array<protocol::CSS::CSSPropertyRule>>,
std::unique_ptr<protocol::Array<protocol::CSS::CSSPropertyRegistration>>>
InspectorCSSAgent::CustomPropertiesForNode(Element* element) {
auto result = std::make_pair(
std::make_unique<protocol::Array<protocol::CSS::CSSPropertyRule>>(),
std::make_unique<
protocol::Array<protocol::CSS::CSSPropertyRegistration>>());
Document& document = element->GetDocument();
DCHECK(!document.NeedsLayoutTreeUpdateForNode(*element));
const ComputedStyle* style = element->EnsureComputedStyle();
if (!style /*|| !style->HasVariableReference()*/)
return result;
for (const AtomicString& var_name : style->GetVariableNames()) {
const auto* registration =
PropertyRegistration::From(document.GetExecutionContext(), var_name);
if (!registration) {
continue;
}
if (StyleRuleProperty* rule = registration->PropertyRule()) {
// Find CSSOM wrapper.
CSSPropertyRule* property_rule = nullptr;
for (CSSStyleSheet* style_sheet :
*document_to_css_style_sheets_.at(&document)) {
property_rule = FindPropertyRule(style_sheet, rule);
if (property_rule)
break;
}
if (property_rule) {
// @property
InspectorStyleSheet* inspector_style_sheet =
BindStyleSheet(property_rule->parentStyleSheet());
result.first->push_back(
inspector_style_sheet->BuildObjectForPropertyRule(property_rule));
}
// If the property_rule wasn't found, just ignore ignore it.
} else {
// CSS.registerProperty
result.second->push_back(
BuildObjectForPropertyRegistration(var_name, *registration));
}
}
return result;
}
template <class CSSRuleCollection>
static CSSFontPaletteValuesRule* FindFontPaletteValuesRule(
CSSRuleCollection* css_rules,
StyleRuleFontPaletteValues* values_rule) {
if (!css_rules) {
return nullptr;
}
CSSFontPaletteValuesRule* result = nullptr;
for (unsigned j = 0; j < css_rules->length() && !result; ++j) {
CSSRule* css_rule = css_rules->item(j);
if (auto* css_style_rule = DynamicTo<CSSFontPaletteValuesRule>(css_rule)) {
if (css_style_rule->FontPaletteValues() == values_rule)
result = css_style_rule;
} else if (auto* css_import_rule = DynamicTo<CSSImportRule>(css_rule)) {
result =
FindFontPaletteValuesRule(css_import_rule->styleSheet(), values_rule);
} else {
result = FindFontPaletteValuesRule(css_rule->cssRules(), values_rule);
}
}
return result;
}
std::unique_ptr<protocol::CSS::CSSFontPaletteValuesRule>
InspectorCSSAgent::FontPalettesForNode(Element& element) {
const ComputedStyle* style = element.EnsureComputedStyle();
const FontPalette* palette = style ? style->GetFontPalette() : nullptr;
if (!palette || !palette->IsCustomPalette()) {
return {};
}
Document& document = element.GetDocument();
StyleRuleFontPaletteValues* rule =
document.GetStyleEngine().FontPaletteValuesForNameAndFamily(
palette->GetPaletteValuesName(),
style->GetFontDescription().Family().FamilyName());
if (!rule) {
return {};
}
// Find CSSOM wrapper.
CSSFontPaletteValuesRule* values_rule = nullptr;
for (CSSStyleSheet* style_sheet :
*document_to_css_style_sheets_.at(&document)) {
values_rule = FindFontPaletteValuesRule(style_sheet, rule);
if (values_rule)
break;
}
InspectorStyleSheet* inspector_style_sheet =
BindStyleSheet(values_rule->parentStyleSheet());
return inspector_style_sheet->BuildObjectForFontPaletteValuesRule(
values_rule);
}
CSSKeyframesRule*
InspectorCSSAgent::FindKeyframesRuleFromUAViewTransitionStylesheet(
Element* element,
StyleRuleKeyframes* keyframes_style_rule) {
// This function should only be called for transition pseudo elements.
CHECK(IsTransitionPseudoElement(element->GetPseudoId()));
auto* transition = ViewTransitionUtils::GetTransition(element->GetDocument());
// There must be a transition and an active UAStyleSheet for the
// transition when the queried element is a transition pseudo element.
CHECK(transition && transition->UAStyleSheet());
if (!user_agent_view_transition_style_sheet_) {
// Save the previous view transition style sheet.
user_agent_view_transition_style_sheet_ = transition->UAStyleSheet();
} else if (user_agent_view_transition_style_sheet_ !=
transition->UAStyleSheet()) {
// If the view transition stylesheet is invalidated
// unbind the previous inspector stylesheet.
user_agent_view_transition_style_sheet_ = transition->UAStyleSheet();
auto previous_css_style_sheet_it =
css_style_sheet_to_inspector_style_sheet_.find(
user_agent_view_transition_style_sheet_);
if (previous_css_style_sheet_it !=
css_style_sheet_to_inspector_style_sheet_.end()) {
UnbindStyleSheet(previous_css_style_sheet_it->value);
}
}
for (wtf_size_t i = 0; i < user_agent_view_transition_style_sheet_->length();
i++) {
CSSKeyframesRule* css_keyframes_rule_from_stylesheet =
DynamicTo<CSSKeyframesRule>(
user_agent_view_transition_style_sheet_->ItemInternal(i));
if (css_keyframes_rule_from_stylesheet &&
css_keyframes_rule_from_stylesheet->name() ==
keyframes_style_rule->GetName()) {
return css_keyframes_rule_from_stylesheet;
}
}
return nullptr;
}
CSSKeyframesRule* InspectorCSSAgent::FindCSSOMWrapperForKeyframesRule(
Element* element,
StyleRuleKeyframes* keyframes_style_rule) {
Document& document = element->GetDocument();
// There might be that there aren't any active stylesheets for the document
// which mean the document_to_css_style_sheets_ map won't contain the
// entry for the document. So, we first check whether there are registered
// stylesheets for the document.
if (document_to_css_style_sheets_.Contains(&document)) {
for (CSSStyleSheet* style_sheet :
*document_to_css_style_sheets_.at(&document)) {
CSSKeyframesRule* css_keyframes_rule =
FindKeyframesRule(style_sheet, keyframes_style_rule);
if (css_keyframes_rule) {
return css_keyframes_rule;
}
}
}
if (IsTransitionPseudoElement(element->GetPseudoId())) {
return FindKeyframesRuleFromUAViewTransitionStylesheet(
element, keyframes_style_rule);
}
return nullptr;
}
std::unique_ptr<protocol::Array<protocol::CSS::CSSKeyframesRule>>
InspectorCSSAgent::AnimationsForNode(Element* element,
Element* animating_element) {
auto css_keyframes_rules =
std::make_unique<protocol::Array<protocol::CSS::CSSKeyframesRule>>();
Document& document = element->GetDocument();
DCHECK(!document.NeedsLayoutTreeUpdateForNode(*element));
// We want to match the animation name of the animating element not the parent
// element's animation names for pseudo elements. When the `element` is a
// non-pseudo element then `animating_element` and the `element` are the same.
const ComputedStyle* style = animating_element->EnsureComputedStyle();
if (!style)
return css_keyframes_rules;
const CSSAnimationData* animation_data = style->Animations();
StyleResolver& style_resolver = document.GetStyleResolver();
for (wtf_size_t i = 0;
animation_data && i < animation_data->NameList().size(); ++i) {
AtomicString animation_name(animation_data->NameList()[i]);
if (animation_name == CSSAnimationData::InitialName())
continue;
StyleRuleKeyframes* keyframes_rule =
style_resolver
.FindKeyframesRule(element, animating_element, animation_name)
.rule;
if (!keyframes_rule) {
continue;
}
CSSKeyframesRule* css_keyframes_rule =
FindCSSOMWrapperForKeyframesRule(animating_element, keyframes_rule);
if (!css_keyframes_rule) {
continue;
}
auto keyframes =
std::make_unique<protocol::Array<protocol::CSS::CSSKeyframeRule>>();
for (unsigned j = 0; j < css_keyframes_rule->length(); ++j) {
InspectorStyleSheet* inspector_style_sheet =
BindStyleSheet(css_keyframes_rule->parentStyleSheet());
keyframes->emplace_back(inspector_style_sheet->BuildObjectForKeyframeRule(
css_keyframes_rule->Item(j), element));
}
InspectorStyleSheet* inspector_style_sheet =
BindStyleSheet(css_keyframes_rule->parentStyleSheet());
CSSRuleSourceData* source_data =
inspector_style_sheet->SourceDataForRule(css_keyframes_rule);
std::unique_ptr<protocol::CSS::Value> name =
protocol::CSS::Value::create()
.setText(css_keyframes_rule->name())
.build();
if (source_data)
name->setRange(inspector_style_sheet->BuildSourceRangeObject(
source_data->rule_header_range));
css_keyframes_rules->emplace_back(protocol::CSS::CSSKeyframesRule::create()
.setAnimationName(std::move(name))
.setKeyframes(std::move(keyframes))
.build());
}
return css_keyframes_rules;
}
protocol::Response InspectorCSSAgent::getInlineStylesForNode(
int node_id,
Maybe<protocol::CSS::CSSStyle>* inline_style,
Maybe<protocol::CSS::CSSStyle>* attributes_style) {
protocol::Response response = AssertEnabled();
if (!response.IsSuccess())
return response;
Element* element = nullptr;
response = dom_agent_->AssertElement(node_id, element);
if (!response.IsSuccess())
return response;
InspectorStyleSheetForInlineStyle* style_sheet =
AsInspectorStyleSheet(element);
if (!style_sheet)
return protocol::Response::ServerError("Element is not a style sheet");
*inline_style = style_sheet->BuildObjectForStyle(element->style(), element);
*attributes_style = BuildObjectForAttributesStyle(element);
return protocol::Response::Success();
}
protocol::Response InspectorCSSAgent::getComputedStyleForNode(
int node_id,
std::unique_ptr<protocol::Array<protocol::CSS::CSSComputedStyleProperty>>*
style) {
protocol::Response response = AssertEnabled();
if (!response.IsSuccess())
return response;
Node* node = nullptr;
response = dom_agent_->AssertNode(node_id, node);
if (!response.IsSuccess())
return response;
if (!node->ownerDocument()) {
return protocol::Response::ServerError(
"Node does not have an owner document");
}
Element* element = DynamicTo<Element>(node);
if (!element && !node->IsDocumentFragment()) {
element = FlatTreeTraversal::ParentElement(*node);
}
if (!element) {
return protocol::Response::ServerError(
"Node is not an element and does not have a parent element");
}
TRACE_EVENT1("devtools", "InspectorCSSAgent::getComputedStyleForNode", "node",
element->DebugName());
auto* computed_style_info =
MakeGarbageCollected<CSSComputedStyleDeclaration>(element, true);
CSSComputedStyleDeclaration::ScopedCleanStyleForAllProperties
clean_style_scope(computed_style_info);
*style = std::make_unique<
protocol::Array<protocol::CSS::CSSComputedStyleProperty>>();
for (CSSPropertyID property_id : CSSPropertyIDList()) {
const CSSProperty& property_class =
CSSProperty::Get(ResolveCSSPropertyID(property_id));
if (!property_class.IsWebExposed(element->GetExecutionContext()) ||
property_class.IsShorthand() || !property_class.IsProperty()) {
continue;
}
(*style)->emplace_back(
protocol::CSS::CSSComputedStyleProperty::create()
.setName(property_class.GetPropertyNameString())
.setValue(computed_style_info->GetPropertyValue(property_id))
.build());
}
for (const auto& it : computed_style_info->GetVariables()) {
(*style)->emplace_back(protocol::CSS::CSSComputedStyleProperty::create()
.setName(it.key)
.setValue(it.value->CssText())
.build());
}
return protocol::Response::Success();
}
void InspectorCSSAgent::CollectPlatformFontsForLayoutObject(
LayoutObject* layout_object,
HashMap<std::pair<int, String>, std::pair<int, String>>* font_stats,
unsigned descendants_depth) {
if (!layout_object->IsText()) {
if (!descendants_depth)
return;
// Skip recursing inside a display-locked tree.
if (DisplayLockUtilities::LockedInclusiveAncestorPreventingPaint(
*layout_object)) {
return;
}
if (!layout_object->IsAnonymous())
--descendants_depth;
for (LayoutObject* child = layout_object->SlowFirstChild(); child;
child = child->NextSibling()) {
CollectPlatformFontsForLayoutObject(child, font_stats, descendants_depth);
}
return;
}
// Don't gather text on a display-locked tree.
if (DisplayLockUtilities::LockedAncestorPreventingPaint(*layout_object))
return;
FontCachePurgePreventer preventer;
DCHECK(layout_object->IsInLayoutNGInlineFormattingContext());
InlineCursor cursor;
cursor.MoveTo(*layout_object);
for (; cursor; cursor.MoveToNextForSameLayoutObject()) {
const ShapeResultView* shape_result = cursor.Current().TextShapeResult();
if (!shape_result) {
continue;
}
HeapVector<ShapeResult::RunFontData> run_font_data_list;
ClearCollectionScope clear_scope(&run_font_data_list);
shape_result->GetRunFontData(&run_font_data_list);
CollectPlatformFontsFromRunFontDataList(run_font_data_list, font_stats);
}
}
protocol::Response InspectorCSSAgent::getPlatformFontsForNode(
int node_id,
std::unique_ptr<protocol::Array<protocol::CSS::PlatformFontUsage>>*
platform_fonts) {
protocol::Response response = AssertEnabled();
if (!response.IsSuccess())
return response;
Node* node = nullptr;
response = dom_agent_->AssertNode(node_id, node);
if (!response.IsSuccess())
return response;
// Key: {isCustomFont, postscript_name}
// Value: {glyph_count (which accumulates), family_name}
HashMap<std::pair<int, String>, std::pair<int, String>> font_stats;
LayoutObject* root = node->GetLayoutObject();
if (root) {
// Iterate upto two layers deep.
const unsigned descendants_depth = 2;
CollectPlatformFontsForLayoutObject(root, &font_stats, descendants_depth);
}
*platform_fonts =
std::make_unique<protocol::Array<protocol::CSS::PlatformFontUsage>>();
for (auto& font : font_stats) {
std::pair<int, String>& font_description = font.key;
std::pair<int, String>& font_value = font.value;
bool is_custom_font = font_description.first == 1;
(*platform_fonts)
->emplace_back(protocol::CSS::PlatformFontUsage::create()
.setFamilyName(font_value.second)
.setPostScriptName(font_description.second)
.setIsCustomFont(is_custom_font)
.setGlyphCount(font_value.first)
.build());
}
return protocol::Response::Success();
}
protocol::Response InspectorCSSAgent::getStyleSheetText(
const String& style_sheet_id,
String* result) {
InspectorStyleSheetBase* inspector_style_sheet = nullptr;
protocol::Response response =
AssertStyleSheetForId(style_sheet_id, inspector_style_sheet);
if (!response.IsSuccess())
return response;
inspector_style_sheet->GetText(result);
return protocol::Response::Success();
}
protocol::Response InspectorCSSAgent::collectClassNames(
const String& style_sheet_id,
std::unique_ptr<protocol::Array<String>>* class_names) {
InspectorStyleSheet* inspector_style_sheet = nullptr;
protocol::Response response =
AssertInspectorStyleSheetForId(style_sheet_id, inspector_style_sheet);
if (!response.IsSuccess())
return response;
*class_names = inspector_style_sheet->CollectClassNames();
return protocol::Response::Success();
}
protocol::Response InspectorCSSAgent::setStyleSheetText(
const String& style_sheet_id,
const String& text,
protocol::Maybe<String>* source_map_url) {
FrontendOperationScope scope;
InspectorStyleSheetBase* inspector_style_sheet = nullptr;
protocol::Response response =
AssertStyleSheetForId(style_sheet_id, inspector_style_sheet);
if (!response.IsSuccess())
return response;
DummyExceptionStateForTesting exception_state;
dom_agent_->History()->Perform(MakeGarbageCollected<SetStyleSheetTextAction>(
inspector_style_sheet, text),
exception_state);
response = InspectorDOMAgent::ToResponse(exception_state);
if (!response.IsSuccess())
return response;
if (!inspector_style_sheet->SourceMapURL().empty())
*source_map_url = inspector_style_sheet->SourceMapURL();
return protocol::Response::Success();
}
static protocol::Response JsonRangeToSourceRange(
InspectorStyleSheetBase* inspector_style_sheet,
protocol::CSS::SourceRange* range,
SourceRange* source_range) {
if (range->getStartLine() < 0) {
return protocol::Response::ServerError(
"range.startLine must be a non-negative integer");
}
if (range->getStartColumn() < 0) {
return protocol::Response::ServerError(
"range.startColumn must be a non-negative integer");
}
if (range->getEndLine() < 0) {
return protocol::Response::ServerError(
"range.endLine must be a non-negative integer");
}
if (range->getEndColumn() < 0) {
return protocol::Response::ServerError(
"range.endColumn must be a non-negative integer");
}
unsigned start_offset = 0;
unsigned end_offset = 0;
bool success =
inspector_style_sheet->LineNumberAndColumnToOffset(
range->getStartLine(), range->getStartColumn(), &start_offset) &&
inspector_style_sheet->LineNumberAndColumnToOffset(
range->getEndLine(), range->getEndColumn(), &end_offset);
if (!success)
return protocol::Response::ServerError("Specified range is out of bounds");
if (start_offset > end_offset) {
return protocol::Response::ServerError(
"Range start must not succeed its end");
}
source_range->start = start_offset;
source_range->end = end_offset;
return protocol::Response::Success();
}
protocol::Response InspectorCSSAgent::setRuleSelector(
const String& style_sheet_id,
std::unique_ptr<protocol::CSS::SourceRange> range,
const String& selector,
std::unique_ptr<protocol::CSS::SelectorList>* result) {
FrontendOperationScope scope;
InspectorStyleSheet* inspector_style_sheet = nullptr;
protocol::Response response =
AssertInspectorStyleSheetForId(style_sheet_id, inspector_style_sheet);
if (!response.IsSuccess())
return response;
SourceRange selector_range;
response = JsonRangeToSourceRange(inspector_style_sheet, range.get(),
&selector_range);
if (!response.IsSuccess())
return response;
DummyExceptionStateForTesting exception_state;
ModifyRuleAction* action = MakeGarbageCollected<ModifyRuleAction>(
ModifyRuleAction::kSetRuleSelector, inspector_style_sheet, selector_range,
selector);
bool success = dom_agent_->History()->Perform(action, exception_state);
if (success) {
CSSStyleRule* rule = InspectorCSSAgent::AsCSSStyleRule(action->TakeRule());
inspector_style_sheet = InspectorStyleSheetForRule(rule);
if (!inspector_style_sheet) {
return protocol::Response::ServerError(
"Failed to get inspector style sheet for rule.");
}
*result = inspector_style_sheet->BuildObjectForSelectorList(rule);
}
return InspectorDOMAgent::ToResponse(exception_state);
}
protocol::Response InspectorCSSAgent::setPropertyRulePropertyName(
const String& in_styleSheetId,
std::unique_ptr<protocol::CSS::SourceRange> in_range,
const String& in_propertyName,
std::unique_ptr<protocol::CSS::Value>* out_propertyName) {
FrontendOperationScope scope;
InspectorStyleSheet* inspector_style_sheet = nullptr;
protocol::Response response =
AssertInspectorStyleSheetForId(in_styleSheetId, inspector_style_sheet);
if (!response.IsSuccess())
return response;
SourceRange name_range;
response = JsonRangeToSourceRange(inspector_style_sheet, in_range.get(),
&name_range);
if (!response.IsSuccess())
return response;
DummyExceptionStateForTesting exception_state;
ModifyRuleAction* action = MakeGarbageCollected<ModifyRuleAction>(
ModifyRuleAction::kSetPropertyName, inspector_style_sheet, name_range,
in_propertyName);
bool success = dom_agent_->History()->Perform(action, exception_state);
if (success) {
auto* rule = To<CSSPropertyRule>(action->TakeRule());
inspector_style_sheet = BindStyleSheet(rule->parentStyleSheet());
if (!inspector_style_sheet) {
return protocol::Response::ServerError(
"Failed to get inspector style sheet for rule.");
}
CSSRuleSourceData* source_data =
inspector_style_sheet->SourceDataForRule(rule);
*out_propertyName =
protocol::CSS::Value::create()
.setText(rule->name())
.setRange(inspector_style_sheet->BuildSourceRangeObject(
source_data->rule_header_range))
.build();
}
return InspectorDOMAgent::ToResponse(exception_state);
}
protocol::Response InspectorCSSAgent::setKeyframeKey(
const String& style_sheet_id,
std::unique_ptr<protocol::CSS::SourceRange> range,
const String& key_text,
std::unique_ptr<protocol::CSS::Value>* result) {
FrontendOperationScope scope;
InspectorStyleSheet* inspector_style_sheet = nullptr;
protocol::Response response =
AssertInspectorStyleSheetForId(style_sheet_id, inspector_style_sheet);
if (!response.IsSuccess())
return response;
SourceRange key_range;
response =
JsonRangeToSourceRange(inspector_style_sheet, range.get(), &key_range);
if (!response.IsSuccess())
return response;
DummyExceptionStateForTesting exception_state;
ModifyRuleAction* action = MakeGarbageCollected<ModifyRuleAction>(
ModifyRuleAction::kSetKeyframeKey, inspector_style_sheet, key_range,
key_text);
bool success = dom_agent_->History()->Perform(action, exception_state);
if (success) {
auto* rule = To<CSSKeyframeRule>(action->TakeRule());
inspector_style_sheet = BindStyleSheet(rule->parentStyleSheet());
if (!inspector_style_sheet) {
return protocol::Response::ServerError(
"Failed to get inspector style sheet for rule.");
}
CSSRuleSourceData* source_data =
inspector_style_sheet->SourceDataForRule(rule);
*result = protocol::CSS::Value::create()
.setText(rule->keyText())
.setRange(inspector_style_sheet->BuildSourceRangeObject(
source_data->rule_header_range))
.build();
}
return InspectorDOMAgent::ToResponse(exception_state);
}
protocol::Response InspectorCSSAgent::MultipleStyleTextsActions(
std::unique_ptr<protocol::Array<protocol::CSS::StyleDeclarationEdit>> edits,
HeapVector<Member<StyleSheetAction>>* actions) {
size_t n = edits->size();
if (n == 0)
return protocol::Response::ServerError("Edits should not be empty");
for (size_t i = 0; i < n; ++i) {
protocol::CSS::StyleDeclarationEdit* edit = (*edits)[i].get();
InspectorStyleSheetBase* inspector_style_sheet = nullptr;
protocol::Response response =
AssertStyleSheetForId(edit->getStyleSheetId(), inspector_style_sheet);
if (!response.IsSuccess()) {
return protocol::Response::ServerError(
String::Format("StyleSheet not found for edit #%zu of %zu", i + 1, n)
.Utf8());
}
SourceRange range;
response =
JsonRangeToSourceRange(inspector_style_sheet, edit->getRange(), &range);
if (!response.IsSuccess())
return response;
if (inspector_style_sheet->IsInlineStyle()) {
InspectorStyleSheetForInlineStyle* inline_style_sheet =
static_cast<InspectorStyleSheetForInlineStyle*>(
inspector_style_sheet);
SetElementStyleAction* action =
MakeGarbageCollected<SetElementStyleAction>(inline_style_sheet,
edit->getText());
actions->push_back(action);
} else {
ModifyRuleAction* action = MakeGarbageCollected<ModifyRuleAction>(
ModifyRuleAction::kSetStyleText,
static_cast<InspectorStyleSheet*>(inspector_style_sheet), range,
edit->getText());
actions->push_back(action);
}
}
return protocol::Response::Success();
}
protocol::Response InspectorCSSAgent::setStyleTexts(
std::unique_ptr<protocol::Array<protocol::CSS::StyleDeclarationEdit>> edits,
protocol::Maybe<int> node_for_property_syntax_validation,
std::unique_ptr<protocol::Array<protocol::CSS::CSSStyle>>* result) {
FrontendOperationScope scope;
HeapVector<Member<StyleSheetAction>> actions;
protocol::Response response =
MultipleStyleTextsActions(std::move(edits), &actions);
if (!response.IsSuccess())
return response;
DummyExceptionStateForTesting exception_state;
Element* element = nullptr;
if (node_for_property_syntax_validation.has_value()) {
response = dom_agent_->AssertElement(
node_for_property_syntax_validation.value(), element);
if (!response.IsSuccess())
return response;
}
int n = actions.size();
auto serialized_styles =
std::make_unique<protocol::Array<protocol::CSS::CSSStyle>>();
for (int i = 0; i < n; ++i) {
Member<StyleSheetAction> action = actions.at(i);
bool success = action->Perform(exception_state);
if (!success) {
for (int j = i - 1; j >= 0; --j) {
Member<StyleSheetAction> revert = actions.at(j);
DummyExceptionStateForTesting undo_exception_state;
revert->Undo(undo_exception_state);
DCHECK(!undo_exception_state.HadException());
}
return protocol::Response::ServerError(
String::Format("Failed applying edit #%d: ", i).Utf8() +
InspectorDOMAgent::ToResponse(exception_state).Message());
}
}
if (element) {
element->GetDocument().UpdateStyleAndLayoutForNode(
element, DocumentUpdateReason::kInspector);
}
for (int i = 0; i < n; ++i) {
Member<StyleSheetAction> action = actions.at(i);
serialized_styles->emplace_back(action->TakeSerializedStyle(element));
}
for (int i = 0; i < n; ++i) {
Member<StyleSheetAction> action = actions.at(i);
dom_agent_->History()->AppendPerformedAction(action);
}
*result = std::move(serialized_styles);
return protocol::Response::Success();
}
protocol::Response InspectorCSSAgent::SetStyleText(
InspectorStyleSheetBase* inspector_style_sheet,
const SourceRange& range,
const String& text,
CSSStyleDeclaration*& result) {
DummyExceptionStateForTesting exception_state;
if (inspector_style_sheet->IsInlineStyle()) {
InspectorStyleSheetForInlineStyle* inline_style_sheet =
static_cast<InspectorStyleSheetForInlineStyle*>(inspector_style_sheet);
SetElementStyleAction* action =
MakeGarbageCollected<SetElementStyleAction>(inline_style_sheet, text);
bool success = dom_agent_->History()->Perform(action, exception_state);
if (success) {
result = inline_style_sheet->InlineStyle();
return protocol::Response::Success();
}
} else {
ModifyRuleAction* action = MakeGarbageCollected<ModifyRuleAction>(
ModifyRuleAction::kSetStyleText,
static_cast<InspectorStyleSheet*>(inspector_style_sheet), range, text);
bool success = dom_agent_->History()->Perform(action, exception_state);
if (success) {
CSSRule* rule = action->TakeRule();
if (auto* style_rule = DynamicTo<CSSStyleRule>(rule)) {
result = style_rule->style();
return protocol::Response::Success();
}
if (auto* keyframe_rule = DynamicTo<CSSKeyframeRule>(rule)) {
result = keyframe_rule->style();
return protocol::Response::Success();
}
}
}
return InspectorDOMAgent::ToResponse(exception_state);
}
protocol::Response InspectorCSSAgent::setMediaText(
const String& style_sheet_id,
std::unique_ptr<protocol::CSS::SourceRange> range,
const String& text,
std::unique_ptr<protocol::CSS::CSSMedia>* result) {
FrontendOperationScope scope;
InspectorStyleSheet* inspector_style_sheet = nullptr;
protocol::Response response =
AssertInspectorStyleSheetForId(style_sheet_id, inspector_style_sheet);
if (!response.IsSuccess())
return response;
SourceRange text_range;
response =
JsonRangeToSourceRange(inspector_style_sheet, range.get(), &text_range);
if (!response.IsSuccess())
return response;
DummyExceptionStateForTesting exception_state;
ModifyRuleAction* action = MakeGarbageCollected<ModifyRuleAction>(
ModifyRuleAction::kSetMediaRuleText, inspector_style_sheet, text_range,
text);
bool success = dom_agent_->History()->Perform(action, exception_state);
if (success) {
CSSMediaRule* rule = InspectorCSSAgent::AsCSSMediaRule(action->TakeRule());
String source_url = rule->parentStyleSheet()->Contents()->BaseURL();
if (source_url.empty())
source_url = InspectorDOMAgent::DocumentURLString(
rule->parentStyleSheet()->OwnerDocument());
*result = BuildMediaObject(rule->media(), kMediaListSourceMediaRule,
source_url, rule->parentStyleSheet());
}
return InspectorDOMAgent::ToResponse(exception_state);
}
protocol::Response InspectorCSSAgent::setContainerQueryText(
const String& style_sheet_id,
std::unique_ptr<protocol::CSS::SourceRange> range,
const String& text,
std::unique_ptr<protocol::CSS::CSSContainerQuery>* result) {
FrontendOperationScope scope;
InspectorStyleSheet* inspector_style_sheet = nullptr;
protocol::Response response =
AssertInspectorStyleSheetForId(style_sheet_id, inspector_style_sheet);
if (!response.IsSuccess())
return response;
SourceRange text_range;
response =
JsonRangeToSourceRange(inspector_style_sheet, range.get(), &text_range);
if (!response.IsSuccess())
return response;
DummyExceptionStateForTesting exception_state;
ModifyRuleAction* action = MakeGarbageCollected<ModifyRuleAction>(
ModifyRuleAction::kSetContainerRuleText, inspector_style_sheet,
text_range, text);
bool success = dom_agent_->History()->Perform(action, exception_state);
if (success) {
CSSContainerRule* rule =
InspectorCSSAgent::AsCSSContainerRule(action->TakeRule());
*result = BuildContainerQueryObject(rule);
}
return InspectorDOMAgent::ToResponse(exception_state);
}
protocol::Response InspectorCSSAgent::setScopeText(
const String& style_sheet_id,
std::unique_ptr<protocol::CSS::SourceRange> range,
const String& text,
std::unique_ptr<protocol::CSS::CSSScope>* result) {
FrontendOperationScope scope;
InspectorStyleSheet* inspector_style_sheet = nullptr;
protocol::Response response =
AssertInspectorStyleSheetForId(style_sheet_id, inspector_style_sheet);
if (!response.IsSuccess())
return response;
SourceRange text_range;
response =
JsonRangeToSourceRange(inspector_style_sheet, range.get(), &text_range);
if (!response.IsSuccess())
return response;
DummyExceptionStateForTesting exception_state;
ModifyRuleAction* action = MakeGarbageCollected<ModifyRuleAction>(
ModifyRuleAction::kSetScopeRuleText, inspector_style_sheet, text_range,
text);
bool success = dom_agent_->History()->Perform(action, exception_state);
if (success) {
CSSScopeRule* rule = InspectorCSSAgent::AsCSSScopeRule(action->TakeRule());
*result = BuildScopeObject(rule);
}
return InspectorDOMAgent::ToResponse(exception_state);
}
protocol::Response InspectorCSSAgent::setSupportsText(
const String& style_sheet_id,
std::unique_ptr<protocol::CSS::SourceRange> range,
const String& text,
std::unique_ptr<protocol::CSS::CSSSupports>* result) {
FrontendOperationScope scope;
InspectorStyleSheet* inspector_style_sheet = nullptr;
protocol::Response response =
AssertInspectorStyleSheetForId(style_sheet_id, inspector_style_sheet);
if (!response.IsSuccess())
return response;
SourceRange text_range;
response =
JsonRangeToSourceRange(inspector_style_sheet, range.get(), &text_range);
if (!response.IsSuccess())
return response;
DummyExceptionStateForTesting exception_state;
ModifyRuleAction* action = MakeGarbageCollected<ModifyRuleAction>(
ModifyRuleAction::kSetSupportsRuleText, inspector_style_sheet, text_range,
text);
bool success = dom_agent_->History()->Perform(action, exception_state);
if (success) {
CSSSupportsRule* rule =
InspectorCSSAgent::AsCSSSupportsRule(action->TakeRule());
*result = BuildSupportsObject(rule);
}
return InspectorDOMAgent::ToResponse(exception_state);
}
protocol::Response InspectorCSSAgent::createStyleSheet(
const String& frame_id,
protocol::CSS::StyleSheetId* out_style_sheet_id) {
LocalFrame* frame =
IdentifiersFactory::FrameById(inspected_frames_, frame_id);
if (!frame)
return protocol::Response::ServerError("Frame not found");
Document* document = frame->GetDocument();
if (!document)
return protocol::Response::ServerError("Frame does not have a document");
InspectorStyleSheet* inspector_style_sheet = ViaInspectorStyleSheet(document);
if (!inspector_style_sheet)
return protocol::Response::ServerError("No target stylesheet found");
UpdateActiveStyleSheets(document);
*out_style_sheet_id = inspector_style_sheet->Id();
return protocol::Response::Success();
}
protocol::Response InspectorCSSAgent::addRule(
const String& style_sheet_id,
const String& rule_text,
std::unique_ptr<protocol::CSS::SourceRange> location,
protocol::Maybe<int> node_for_property_syntax_validation,
std::unique_ptr<protocol::CSS::CSSRule>* result) {
FrontendOperationScope scope;
InspectorStyleSheet* inspector_style_sheet = nullptr;
protocol::Response response =
AssertInspectorStyleSheetForId(style_sheet_id, inspector_style_sheet);
if (!response.IsSuccess())
return response;
Element* element = nullptr;
if (node_for_property_syntax_validation.has_value()) {
response = dom_agent_->AssertElement(
node_for_property_syntax_validation.value(), element);
if (!response.IsSuccess())
return response;
}
SourceRange rule_location;
response = JsonRangeToSourceRange(inspector_style_sheet, location.get(),
&rule_location);
if (!response.IsSuccess())
return response;
DummyExceptionStateForTesting exception_state;
AddRuleAction* action = MakeGarbageCollected<AddRuleAction>(
inspector_style_sheet, rule_text, rule_location);
bool success = dom_agent_->History()->Perform(action, exception_state);
if (!success)
return InspectorDOMAgent::ToResponse(exception_state);
CSSStyleRule* rule = action->TakeRule();
if (element) {
element->GetDocument().UpdateStyleAndLayoutForNode(
element, DocumentUpdateReason::kInspector);
}
*result = BuildObjectForRule(rule, element);
return protocol::Response::Success();
}
protocol::Response InspectorCSSAgent::forcePseudoState(
int node_id,
std::unique_ptr<protocol::Array<String>> forced_pseudo_classes) {
protocol::Response response = AssertEnabled();
if (!response.IsSuccess())
return response;
Element* element = nullptr;
response = dom_agent_->AssertElement(node_id, element);
if (!response.IsSuccess())
return response;
unsigned forced_pseudo_state =
ComputePseudoClassMask(std::move(forced_pseudo_classes));
NodeIdToForcedPseudoState::iterator it =
node_id_to_forced_pseudo_state_.find(node_id);
unsigned current_forced_pseudo_state =
it == node_id_to_forced_pseudo_state_.end() ? 0 : it->value;
bool need_style_recalc = forced_pseudo_state != current_forced_pseudo_state;
if (!need_style_recalc)
return protocol::Response::Success();
if (forced_pseudo_state)
node_id_to_forced_pseudo_state_.Set(node_id, forced_pseudo_state);
else
node_id_to_forced_pseudo_state_.erase(node_id);
// When adding focus or focus-within, we force focus-within for ancestor
// nodes to emulate real focus for user convenience.
// Flips from no forced focus to the forced focus (:focus or :focus-within).
if (((forced_pseudo_state & kPseudoFocus) == kPseudoFocus &&
(current_forced_pseudo_state & kPseudoFocus) == 0) ||
((forced_pseudo_state & kPseudoFocusWithin) == kPseudoFocusWithin &&
(current_forced_pseudo_state & kPseudoFocusWithin) == 0)) {
IncrementFocusedCountForAncestors(element);
}
// Flips from the forced focus (:focus or :focus-within) to no focus.
if (((forced_pseudo_state & kPseudoFocus) == 0 &&
(current_forced_pseudo_state & kPseudoFocus) == kPseudoFocus) ||
((forced_pseudo_state & kPseudoFocusWithin) == 0 &&
(current_forced_pseudo_state & kPseudoFocusWithin) ==
kPseudoFocusWithin)) {
DecrementFocusedCountForAncestors(element);
}
element->GetDocument().GetStyleEngine().MarkAllElementsForStyleRecalc(
StyleChangeReasonForTracing::Create(style_change_reason::kInspector));
return protocol::Response::Success();
}
void InspectorCSSAgent::IncrementFocusedCountForAncestors(Element* element) {
for (Node& ancestor : FlatTreeTraversal::AncestorsOf(*element)) {
if (!IsA<Element>(ancestor))
continue;
int node_id = dom_agent_->BoundNodeId(&ancestor);
if (!node_id)
continue;
NodeIdToNumberFocusedChildren::iterator it =
node_id_to_number_focused_children_.find(node_id);
unsigned count =
it == node_id_to_number_focused_children_.end() ? 0 : it->value;
node_id_to_number_focused_children_.Set(node_id, count + 1);
}
}
void InspectorCSSAgent::DecrementFocusedCountForAncestors(Element* element) {
for (Node& ancestor : FlatTreeTraversal::AncestorsOf(*element)) {
if (!IsA<Element>(ancestor))
continue;
int node_id = dom_agent_->BoundNodeId(&ancestor);
if (!node_id)
continue;
NodeIdToNumberFocusedChildren::iterator it =
node_id_to_number_focused_children_.find(node_id);
unsigned count =
it == node_id_to_number_focused_children_.end() ? 1 : it->value;
if (count <= 1) {
// If `count - 1` is zero or overflows, erase the node_id
// from the map to save memory. If there is zero focused child
// elements, :focus-within should not be forced.
node_id_to_number_focused_children_.erase(node_id);
} else {
node_id_to_number_focused_children_.Set(node_id, count - 1);
}
}
}
std::unique_ptr<protocol::CSS::CSSMedia> InspectorCSSAgent::BuildMediaObject(
const MediaList* media,
MediaListSource media_list_source,
const String& source_url,
CSSStyleSheet* parent_style_sheet) {
// Make certain compilers happy by initializing |source| up-front.
String source = protocol::CSS::CSSMedia::SourceEnum::InlineSheet;
switch (media_list_source) {
case kMediaListSourceMediaRule:
source = protocol::CSS::CSSMedia::SourceEnum::MediaRule;
break;
case kMediaListSourceImportRule:
source = protocol::CSS::CSSMedia::SourceEnum::ImportRule;
break;
case kMediaListSourceLinkedSheet:
source = protocol::CSS::CSSMedia::SourceEnum::LinkedSheet;
break;
case kMediaListSourceInlineSheet:
source = protocol::CSS::CSSMedia::SourceEnum::InlineSheet;
break;
}
const MediaQuerySet* queries = media->Queries();
const HeapVector<Member<const MediaQuery>>& query_vector =
queries->QueryVector();
LocalFrame* frame = nullptr;
if (parent_style_sheet) {
if (Document* document = parent_style_sheet->OwnerDocument())
frame = document->GetFrame();
}
MediaQueryEvaluator* media_evaluator =
MakeGarbageCollected<MediaQueryEvaluator>(frame);
InspectorStyleSheet* inspector_style_sheet = nullptr;
if (parent_style_sheet) {
auto it =
css_style_sheet_to_inspector_style_sheet_.find(parent_style_sheet);
if (it != css_style_sheet_to_inspector_style_sheet_.end())
inspector_style_sheet = it->value;
}
auto media_list_array =
std::make_unique<protocol::Array<protocol::CSS::MediaQuery>>();
MediaValues* media_values = MediaValues::CreateDynamicIfFrameExists(frame);
bool has_media_query_items = false;
for (wtf_size_t i = 0; i < query_vector.size(); ++i) {
const MediaQuery& query = *query_vector.at(i);
HeapVector<MediaQueryExp> expressions;
if (query.ExpNode())
query.ExpNode()->CollectExpressions(expressions);
auto expression_array = std::make_unique<
protocol::Array<protocol::CSS::MediaQueryExpression>>();
bool has_expression_items = false;
for (wtf_size_t j = 0; j < expressions.size(); ++j) {
const MediaQueryExp& media_query_exp = expressions.at(j);
MediaQueryExpValue exp_value = media_query_exp.Bounds().right.value;
if (!exp_value.IsNumeric())
continue;
const char* value_name =
CSSPrimitiveValue::UnitTypeToString(exp_value.Unit());
std::unique_ptr<protocol::CSS::MediaQueryExpression>
media_query_expression =
protocol::CSS::MediaQueryExpression::create()
.setValue(exp_value.Value())
.setUnit(String(value_name))
.setFeature(media_query_exp.MediaFeature())
.build();
if (inspector_style_sheet && media->ParentRule()) {
media_query_expression->setValueRange(
inspector_style_sheet->MediaQueryExpValueSourceRange(
media->ParentRule(), i, j));
}
int computed_length;
if (media_values->ComputeLength(exp_value.Value(), exp_value.Unit(),
computed_length))
media_query_expression->setComputedLength(computed_length);
expression_array->emplace_back(std::move(media_query_expression));
has_expression_items = true;
}
if (!has_expression_items)
continue;
std::unique_ptr<protocol::CSS::MediaQuery> media_query =
protocol::CSS::MediaQuery::create()
.setActive(media_evaluator->Eval(query))
.setExpressions(std::move(expression_array))
.build();
media_list_array->emplace_back(std::move(media_query));
has_media_query_items = true;
}
std::unique_ptr<protocol::CSS::CSSMedia> media_object =
protocol::CSS::CSSMedia::create()
.setText(media->MediaTextInternal())
.setSource(source)
.build();
if (has_media_query_items)
media_object->setMediaList(std::move(media_list_array));
if (inspector_style_sheet && media_list_source != kMediaListSourceLinkedSheet)
media_object->setStyleSheetId(inspector_style_sheet->Id());
if (!source_url.empty()) {
media_object->setSourceURL(source_url);
CSSRule* parent_rule = media->ParentRule();
if (!parent_rule)
return media_object;
inspector_style_sheet = BindStyleSheet(parent_rule->parentStyleSheet());
media_object->setRange(
inspector_style_sheet->RuleHeaderSourceRange(parent_rule));
}
return media_object;
}
void InspectorCSSAgent::CollectMediaQueriesFromStyleSheet(
CSSStyleSheet* style_sheet,
protocol::Array<protocol::CSS::CSSMedia>* media_array,
protocol::Array<protocol::CSS::CSSRuleType>* rule_types) {
MediaList* media_list = style_sheet->media();
String source_url;
if (media_list && media_list->length()) {
Document* doc = style_sheet->OwnerDocument();
if (doc)
source_url = doc->Url();
else if (!style_sheet->Contents()->BaseURL().IsEmpty())
source_url = style_sheet->Contents()->BaseURL();
else
source_url = "";
media_array->emplace_back(
BuildMediaObject(media_list,
style_sheet->ownerNode() ? kMediaListSourceLinkedSheet
: kMediaListSourceInlineSheet,
source_url, style_sheet));
if (rule_types) {
rule_types->emplace_back(protocol::CSS::CSSRuleTypeEnum::MediaRule);
}
}
}
void InspectorCSSAgent::CollectMediaQueriesFromRule(
CSSRule* rule,
protocol::Array<protocol::CSS::CSSMedia>* media_array,
protocol::Array<protocol::CSS::CSSRuleType>* rule_types) {
MediaList* media_list;
String source_url;
CSSStyleSheet* parent_style_sheet = nullptr;
bool is_media_rule = true;
if (auto* media_rule = DynamicTo<CSSMediaRule>(rule)) {
media_list = media_rule->media();
parent_style_sheet = media_rule->parentStyleSheet();
} else if (auto* import_rule = DynamicTo<CSSImportRule>(rule)) {
media_list = import_rule->media();
parent_style_sheet = import_rule->parentStyleSheet();
is_media_rule = false;
} else {
media_list = nullptr;
}
if (parent_style_sheet) {
source_url = parent_style_sheet->Contents()->BaseURL();
if (source_url.empty())
source_url = InspectorDOMAgent::DocumentURLString(
parent_style_sheet->OwnerDocument());
} else {
source_url = "";
}
if (media_list && media_list->length()) {
media_array->emplace_back(BuildMediaObject(
media_list,
is_media_rule ? kMediaListSourceMediaRule : kMediaListSourceImportRule,
source_url, parent_style_sheet));
if (rule_types) {
rule_types->emplace_back(protocol::CSS::CSSRuleTypeEnum::MediaRule);
}
}
}
std::unique_ptr<protocol::CSS::CSSContainerQuery>
InspectorCSSAgent::BuildContainerQueryObject(CSSContainerRule* rule) {
std::unique_ptr<protocol::CSS::CSSContainerQuery> container_query_object =
protocol::CSS::CSSContainerQuery::create()
.setText(rule->containerQuery())
.build();
auto it =
css_style_sheet_to_inspector_style_sheet_.find(rule->parentStyleSheet());
if (it != css_style_sheet_to_inspector_style_sheet_.end()) {
InspectorStyleSheet* inspector_style_sheet = it->value;
container_query_object->setStyleSheetId(inspector_style_sheet->Id());
}
InspectorStyleSheet* inspector_style_sheet =
BindStyleSheet(rule->parentStyleSheet());
container_query_object->setRange(
inspector_style_sheet->RuleHeaderSourceRange(rule));
if (!rule->Name().empty())
container_query_object->setName(rule->Name());
PhysicalAxes physical = rule->Selector().GetPhysicalAxes();
if (physical != kPhysicalAxesNone) {
protocol::DOM::PhysicalAxes physical_proto =
protocol::DOM::PhysicalAxesEnum::Horizontal;
if (physical == kPhysicalAxesVertical) {
physical_proto = protocol::DOM::PhysicalAxesEnum::Vertical;
} else if (physical == kPhysicalAxesBoth) {
physical_proto = protocol::DOM::PhysicalAxesEnum::Both;
} else {
DCHECK(physical == kPhysicalAxesHorizontal);
}
container_query_object->setPhysicalAxes(physical_proto);
}
LogicalAxes logical = rule->Selector().GetLogicalAxes();
if (logical != kLogicalAxesNone) {
protocol::DOM::LogicalAxes logical_proto =
protocol::DOM::LogicalAxesEnum::Inline;
if (logical == kLogicalAxesBlock) {
logical_proto = protocol::DOM::LogicalAxesEnum::Block;
} else if (logical == kLogicalAxesBoth) {
logical_proto = protocol::DOM::LogicalAxesEnum::Both;
} else {
DCHECK(logical == kLogicalAxesInline);
}
container_query_object->setLogicalAxes(logical_proto);
}
return container_query_object;
}
void InspectorCSSAgent::CollectContainerQueriesFromRule(
CSSRule* rule,
protocol::Array<protocol::CSS::CSSContainerQuery>* container_queries,
protocol::Array<protocol::CSS::CSSRuleType>* rule_types) {
if (auto* container_rule = DynamicTo<CSSContainerRule>(rule)) {
container_queries->emplace_back(BuildContainerQueryObject(container_rule));
rule_types->emplace_back(protocol::CSS::CSSRuleTypeEnum::ContainerRule);
}
}
std::unique_ptr<protocol::CSS::CSSSupports>
InspectorCSSAgent::BuildSupportsObject(CSSSupportsRule* rule) {
std::unique_ptr<protocol::CSS::CSSSupports> supports_object =
protocol::CSS::CSSSupports::create()
.setText(rule->ConditionTextInternal())
.setActive(rule->ConditionIsSupported())
.build();
auto it =
css_style_sheet_to_inspector_style_sheet_.find(rule->parentStyleSheet());
if (it != css_style_sheet_to_inspector_style_sheet_.end()) {
InspectorStyleSheet* inspector_style_sheet = it->value;
supports_object->setStyleSheetId(inspector_style_sheet->Id());
}
InspectorStyleSheet* inspector_style_sheet =
BindStyleSheet(rule->parentStyleSheet());
supports_object->setRange(inspector_style_sheet->RuleHeaderSourceRange(rule));
return supports_object;
}
void InspectorCSSAgent::CollectSupportsFromRule(
CSSRule* rule,
protocol::Array<protocol::CSS::CSSSupports>* supports_list,
protocol::Array<protocol::CSS::CSSRuleType>* rule_types) {
if (auto* supports_rule = DynamicTo<CSSSupportsRule>(rule)) {
supports_list->emplace_back(BuildSupportsObject(supports_rule));
rule_types->emplace_back(protocol::CSS::CSSRuleTypeEnum::SupportsRule);
}
}
std::unique_ptr<protocol::CSS::CSSLayer> InspectorCSSAgent::BuildLayerObject(
CSSLayerBlockRule* rule) {
std::unique_ptr<protocol::CSS::CSSLayer> layer_object =
protocol::CSS::CSSLayer::create().setText(rule->name()).build();
auto it =
css_style_sheet_to_inspector_style_sheet_.find(rule->parentStyleSheet());
if (it != css_style_sheet_to_inspector_style_sheet_.end()) {
InspectorStyleSheet* inspector_style_sheet = it->value;
layer_object->setStyleSheetId(inspector_style_sheet->Id());
}
InspectorStyleSheet* inspector_style_sheet =
BindStyleSheet(rule->parentStyleSheet());
layer_object->setRange(inspector_style_sheet->RuleHeaderSourceRange(rule));
return layer_object;
}
std::unique_ptr<protocol::CSS::CSSLayer>
InspectorCSSAgent::BuildLayerObjectFromImport(CSSImportRule* rule) {
std::unique_ptr<protocol::CSS::CSSLayer> layer_object =
protocol::CSS::CSSLayer::create().setText(rule->layerName()).build();
auto it =
css_style_sheet_to_inspector_style_sheet_.find(rule->parentStyleSheet());
if (it != css_style_sheet_to_inspector_style_sheet_.end()) {
InspectorStyleSheet* inspector_style_sheet = it->value;
layer_object->setStyleSheetId(inspector_style_sheet->Id());
}
InspectorStyleSheet* inspector_style_sheet =
BindStyleSheet(rule->parentStyleSheet());
layer_object->setRange(inspector_style_sheet->RuleHeaderSourceRange(rule));
return layer_object;
}
void InspectorCSSAgent::CollectLayersFromRule(
CSSRule* rule,
protocol::Array<protocol::CSS::CSSLayer>* layers_list,
protocol::Array<protocol::CSS::CSSRuleType>* rule_types) {
if (auto* layer_rule = DynamicTo<CSSLayerBlockRule>(rule)) {
layers_list->emplace_back(BuildLayerObject(layer_rule));
rule_types->emplace_back(protocol::CSS::CSSRuleTypeEnum::LayerRule);
} else if (auto* import_rule = DynamicTo<CSSImportRule>(rule)) {
if (import_rule->layerName() != g_null_atom) {
layers_list->emplace_back(BuildLayerObjectFromImport(import_rule));
rule_types->emplace_back(protocol::CSS::CSSRuleTypeEnum::LayerRule);
}
}
}
void InspectorCSSAgent::FillAncestorData(CSSRule* rule,
protocol::CSS::CSSRule* result) {
auto layers_list =
std::make_unique<protocol::Array<protocol::CSS::CSSLayer>>();
auto media_list =
std::make_unique<protocol::Array<protocol::CSS::CSSMedia>>();
auto supports_list =
std::make_unique<protocol::Array<protocol::CSS::CSSSupports>>();
auto container_queries_list =
std::make_unique<protocol::Array<protocol::CSS::CSSContainerQuery>>();
auto scopes_list =
std::make_unique<protocol::Array<protocol::CSS::CSSScope>>();
auto rule_types_list =
std::make_unique<protocol::Array<protocol::CSS::CSSRuleType>>();
CSSRule* parent_rule = rule;
auto nesting_selectors = std::make_unique<protocol::Array<String>>();
while (parent_rule) {
CollectLayersFromRule(parent_rule, layers_list.get(),
rule_types_list.get());
CollectMediaQueriesFromRule(parent_rule, media_list.get(),
rule_types_list.get());
CollectContainerQueriesFromRule(parent_rule, container_queries_list.get(),
rule_types_list.get());
CollectSupportsFromRule(parent_rule, supports_list.get(),
rule_types_list.get());
CollectScopesFromRule(parent_rule, scopes_list.get(),
rule_types_list.get());
if (parent_rule != rule) {
if (auto* style_rule = DynamicTo<CSSStyleRule>(parent_rule)) {
nesting_selectors->emplace_back(style_rule->selectorText());
rule_types_list->emplace_back(
protocol::CSS::CSSRuleTypeEnum::StyleRule);
}
}
if (parent_rule->parentRule()) {
parent_rule = parent_rule->parentRule();
} else {
CSSStyleSheet* style_sheet = parent_rule->parentStyleSheet();
while (style_sheet) {
CollectMediaQueriesFromStyleSheet(style_sheet, media_list.get(),
rule_types_list.get());
parent_rule = style_sheet->ownerRule();
if (parent_rule)
break;
style_sheet = style_sheet->parentStyleSheet();
}
}
}
result->setMedia(std::move(media_list));
result->setSupports(std::move(supports_list));
result->setScopes(std::move(scopes_list));
std::reverse(layers_list.get()->begin(), layers_list.get()->end());
result->setLayers(std::move(layers_list));
result->setContainerQueries(std::move(container_queries_list));
result->setRuleTypes(std::move(rule_types_list));
if (nesting_selectors->size() > 0) {
result->setNestingSelectors(std::move(nesting_selectors));
}
}
std::unique_ptr<protocol::CSS::CSSScope> InspectorCSSAgent::BuildScopeObject(
CSSScopeRule* rule) {
std::unique_ptr<protocol::CSS::CSSScope> scope_object =
protocol::CSS::CSSScope::create().setText(rule->PreludeText()).build();
auto it =
css_style_sheet_to_inspector_style_sheet_.find(rule->parentStyleSheet());
if (it != css_style_sheet_to_inspector_style_sheet_.end()) {
InspectorStyleSheet* inspector_style_sheet = it->value;
scope_object->setStyleSheetId(inspector_style_sheet->Id());
}
InspectorStyleSheet* inspector_style_sheet =
BindStyleSheet(rule->parentStyleSheet());
scope_object->setRange(inspector_style_sheet->RuleHeaderSourceRange(rule));
return scope_object;
}
void InspectorCSSAgent::CollectScopesFromRule(
CSSRule* rule,
protocol::Array<protocol::CSS::CSSScope>* scopes_list,
protocol::Array<protocol::CSS::CSSRuleType>* rule_types) {
if (auto* scope_rule = DynamicTo<CSSScopeRule>(rule)) {
scopes_list->emplace_back(BuildScopeObject(scope_rule));
rule_types->emplace_back(protocol::CSS::CSSRuleTypeEnum::ScopeRule);
}
}
InspectorStyleSheetForInlineStyle* InspectorCSSAgent::AsInspectorStyleSheet(
Element* element) {
NodeToInspectorStyleSheet::iterator it =
node_to_inspector_style_sheet_.find(element);
if (it != node_to_inspector_style_sheet_.end())
return it->value.Get();
CSSStyleDeclaration* style = element->style();
if (!style)
return nullptr;
InspectorStyleSheetForInlineStyle* inspector_style_sheet =
MakeGarbageCollected<InspectorStyleSheetForInlineStyle>(element, this);
id_to_inspector_style_sheet_for_inline_style_.Set(inspector_style_sheet->Id(),
inspector_style_sheet);
node_to_inspector_style_sheet_.Set(element, inspector_style_sheet);
return inspector_style_sheet;
}
// static
void InspectorCSSAgent::CollectAllDocumentStyleSheets(
Document* document,
HeapVector<Member<CSSStyleSheet>>& result) {
for (const auto& style :
document->GetStyleEngine().ActiveStyleSheetsForInspector())
InspectorCSSAgent::CollectStyleSheets(style.first, result);
}
// static
void InspectorCSSAgent::CollectStyleSheets(
CSSStyleSheet* style_sheet,
HeapVector<Member<CSSStyleSheet>>& result) {
result.push_back(style_sheet);
for (unsigned i = 0, size = style_sheet->length(); i < size; ++i) {
CSSRule* rule = style_sheet->ItemInternal(i);
if (auto* import_rule = DynamicTo<CSSImportRule>(rule)) {
CSSStyleSheet* imported_style_sheet = import_rule->styleSheet();
if (imported_style_sheet)
InspectorCSSAgent::CollectStyleSheets(imported_style_sheet, result);
}
}
}
InspectorStyleSheet* InspectorCSSAgent::BindStyleSheet(
CSSStyleSheet* style_sheet) {
auto it = css_style_sheet_to_inspector_style_sheet_.find(style_sheet);
if (it != css_style_sheet_to_inspector_style_sheet_.end())
return it->value;
Document* document = style_sheet->OwnerDocument();
InspectorStyleSheet* inspector_style_sheet =
MakeGarbageCollected<InspectorStyleSheet>(
network_agent_, style_sheet, DetectOrigin(style_sheet, document),
InspectorDOMAgent::DocumentURLString(document), this,
resource_container_);
id_to_inspector_style_sheet_.Set(inspector_style_sheet->Id(),
inspector_style_sheet);
css_style_sheet_to_inspector_style_sheet_.Set(style_sheet,
inspector_style_sheet);
return inspector_style_sheet;
}
String InspectorCSSAgent::StyleSheetId(CSSStyleSheet* style_sheet) {
return BindStyleSheet(style_sheet)->Id();
}
String InspectorCSSAgent::UnbindStyleSheet(
InspectorStyleSheet* inspector_style_sheet) {
String id = inspector_style_sheet->Id();
id_to_inspector_style_sheet_.erase(id);
if (inspector_style_sheet->PageStyleSheet())
css_style_sheet_to_inspector_style_sheet_.erase(
inspector_style_sheet->PageStyleSheet());
return id;
}
InspectorStyleSheet* InspectorCSSAgent::InspectorStyleSheetForRule(
CSSStyleRule* rule) {
if (!rule)
return nullptr;
// CSSRules returned by StyleResolver::pseudoCSSRulesForElement lack parent
// pointers if they are coming from user agent stylesheets. To work around
// this issue, we use CSSOM wrapper created by inspector.
if (!rule->parentStyleSheet()) {
if (!inspector_user_agent_style_sheet_)
inspector_user_agent_style_sheet_ = MakeGarbageCollected<CSSStyleSheet>(
CSSDefaultStyleSheets::Instance().DefaultStyleSheet());
rule->SetParentStyleSheet(inspector_user_agent_style_sheet_.Get());
}
return BindStyleSheet(rule->parentStyleSheet());
}
InspectorStyleSheet* InspectorCSSAgent::ViaInspectorStyleSheet(
Document* document) {
if (!document)
return nullptr;
if (!IsA<HTMLDocument>(document) && !document->IsSVGDocument())
return nullptr;
CSSStyleSheet& inspector_sheet =
document->GetStyleEngine().EnsureInspectorStyleSheet();
FlushPendingProtocolNotifications();
auto it = css_style_sheet_to_inspector_style_sheet_.find(&inspector_sheet);
return it != css_style_sheet_to_inspector_style_sheet_.end() ? it->value
: nullptr;
}
protocol::Response InspectorCSSAgent::AssertEnabled() {
return enable_completed_
? protocol::Response::Success()
: protocol::Response::ServerError("CSS agent was not enabled");
}
protocol::Response InspectorCSSAgent::AssertInspectorStyleSheetForId(
const String& style_sheet_id,
InspectorStyleSheet*& result) {
protocol::Response response = AssertEnabled();
if (!response.IsSuccess())
return response;
IdToInspectorStyleSheet::iterator it =
id_to_inspector_style_sheet_.find(style_sheet_id);
if (it == id_to_inspector_style_sheet_.end()) {
return protocol::Response::ServerError(
"No style sheet with given id found");
}
result = it->value.Get();
return protocol::Response::Success();
}
protocol::Response InspectorCSSAgent::AssertStyleSheetForId(
const String& style_sheet_id,
InspectorStyleSheetBase*& result) {
InspectorStyleSheet* style_sheet = nullptr;
protocol::Response response =
AssertInspectorStyleSheetForId(style_sheet_id, style_sheet);
if (response.IsSuccess()) {
result = style_sheet;
return response;
}
IdToInspectorStyleSheetForInlineStyle::iterator it =
id_to_inspector_style_sheet_for_inline_style_.find(style_sheet_id);
if (it == id_to_inspector_style_sheet_for_inline_style_.end()) {
return protocol::Response::ServerError(
"No style sheet with given id found");
}
result = it->value.Get();
return protocol::Response::Success();
}
protocol::CSS::StyleSheetOrigin InspectorCSSAgent::DetectOrigin(
CSSStyleSheet* page_style_sheet,
Document* owner_document) {
DCHECK(page_style_sheet);
if (!page_style_sheet->ownerNode() && page_style_sheet->href().empty() &&
!page_style_sheet->IsConstructed())
return protocol::CSS::StyleSheetOriginEnum::UserAgent;
if (page_style_sheet->ownerNode() &&
page_style_sheet->ownerNode()->IsDocumentNode()) {
if (page_style_sheet ==
owner_document->GetStyleEngine().InspectorStyleSheet())
return protocol::CSS::StyleSheetOriginEnum::Inspector;
return protocol::CSS::StyleSheetOriginEnum::Injected;
}
return protocol::CSS::StyleSheetOriginEnum::Regular;
}
std::unique_ptr<protocol::CSS::CSSRule> InspectorCSSAgent::BuildObjectForRule(
CSSStyleRule* rule,
Element* element,
PseudoId pseudo_id,
const AtomicString& pseudo_argument) {
InspectorStyleSheet* inspector_style_sheet = InspectorStyleSheetForRule(rule);
if (!inspector_style_sheet)
return nullptr;
std::unique_ptr<protocol::CSS::CSSRule> result =
inspector_style_sheet->BuildObjectForRuleWithoutAncestorData(
rule, element, pseudo_id, pseudo_argument);
FillAncestorData(rule, result.get());
return result;
}
std::unique_ptr<protocol::Array<protocol::CSS::RuleMatch>>
InspectorCSSAgent::BuildArrayForMatchedRuleList(
RuleIndexList* rule_list,
Element* element,
PseudoId pseudo_id,
const AtomicString& pseudo_argument) {
auto result = std::make_unique<protocol::Array<protocol::CSS::RuleMatch>>();
if (!rule_list)
return result;
// Dedupe matches coming from the same rule source.
HeapVector<Member<CSSStyleRule>> uniq_rules;
HeapHashSet<Member<CSSRule>> uniq_rules_set;
HeapHashMap<Member<CSSStyleRule>, std::unique_ptr<Vector<unsigned>>>
rule_indices;
for (auto it = rule_list->rbegin(); it != rule_list->rend(); ++it) {
CSSRule* rule = it->first;
auto* style_rule = DynamicTo<CSSStyleRule>(rule);
if (!style_rule)
continue;
if (!uniq_rules_set.Contains(rule)) {
uniq_rules_set.insert(rule);
uniq_rules.push_back(style_rule);
rule_indices.Set(style_rule, std::make_unique<Vector<unsigned>>());
}
rule_indices.at(style_rule)->push_back(it->second);
}
for (auto it = uniq_rules.rbegin(); it != uniq_rules.rend(); ++it) {
CSSStyleRule* rule = it->Get();
std::unique_ptr<protocol::CSS::CSSRule> rule_object =
BuildObjectForRule(rule, element, pseudo_id, pseudo_argument);
if (!rule_object)
continue;
// Transform complex rule_indices into client-friendly, compound-basis for
// matching_selectors.
// e.g. ".foo + .bar, h1, body h1" for <h1>
// (complex): {.foo: 0, .bar: 1, h1: 2, body: 3, h1: 4}, matches: [2, 4]
// (compound): {.foo: 0, .bar: 0, h1: 1, body: 2, h1: 2}, matches: [1, 2]
auto matching_selectors = std::make_unique<protocol::Array<int>>();
if (rule->GetStyleRule()) {
// Compound index (0 -> 1 -> 2).
int compound = 0;
// Complex index of the next compound (0 -> 2 -> 3 -> kNotFound).
wtf_size_t next_compound_start =
rule->GetStyleRule()->IndexOfNextSelectorAfter(0);
std::sort(rule_indices.at(rule)->begin(), rule_indices.at(rule)->end());
for (unsigned complex_match : (*rule_indices.at(rule))) {
while (complex_match >= next_compound_start &&
next_compound_start != kNotFound) {
next_compound_start = rule->GetStyleRule()->IndexOfNextSelectorAfter(
next_compound_start);
compound++;
}
matching_selectors->push_back(compound);
}
}
result->emplace_back(
protocol::CSS::RuleMatch::create()
.setRule(std::move(rule_object))
.setMatchingSelectors(std::move(matching_selectors))
.build());
}
return result;
}
std::unique_ptr<protocol::CSS::CSSStyle>
InspectorCSSAgent::BuildObjectForAttributesStyle(Element* element) {
if (!element->IsStyledElement())
return nullptr;
// FIXME: Ugliness below.
auto* mutable_attribute_style = DynamicTo<MutableCSSPropertyValueSet>(
const_cast<CSSPropertyValueSet*>(element->PresentationAttributeStyle()));
if (!mutable_attribute_style)
return nullptr;
InspectorStyle* inspector_style = MakeGarbageCollected<InspectorStyle>(
mutable_attribute_style->EnsureCSSStyleDeclaration(
element->GetExecutionContext()),
nullptr, nullptr);
return inspector_style->BuildObjectForStyle();
}
void InspectorCSSAgent::DidAddDocument(Document* document) {
if (!tracker_)
return;
document->GetStyleEngine().SetRuleUsageTracker(tracker_);
document->GetStyleEngine().MarkAllElementsForStyleRecalc(
StyleChangeReasonForTracing::Create(style_change_reason::kInspector));
}
void InspectorCSSAgent::WillRemoveDOMNode(Node* node) {
DCHECK(node);
int node_id = dom_agent_->BoundNodeId(node);
DCHECK(node_id);
node_id_to_forced_pseudo_state_.erase(node_id);
computed_style_updated_node_ids_.erase(node_id);
NodeToInspectorStyleSheet::iterator it =
node_to_inspector_style_sheet_.find(node);
if (it == node_to_inspector_style_sheet_.end())
return;
id_to_inspector_style_sheet_for_inline_style_.erase(it->value->Id());
node_to_inspector_style_sheet_.erase(node);
}
void InspectorCSSAgent::DidModifyDOMAttr(Element* element) {
if (!element)
return;
NodeToInspectorStyleSheet::iterator it =
node_to_inspector_style_sheet_.find(element);
if (it == node_to_inspector_style_sheet_.end())
return;
it->value->DidModifyElementAttribute();
}
void InspectorCSSAgent::DidMutateStyleSheet(CSSStyleSheet* css_style_sheet) {
auto it = css_style_sheet_to_inspector_style_sheet_.find(css_style_sheet);
if (it == css_style_sheet_to_inspector_style_sheet_.end())
return;
InspectorStyleSheet* style_sheet = it->value;
style_sheet->MarkForSync();
StyleSheetChanged(style_sheet);
}
void InspectorCSSAgent::GetTextPosition(wtf_size_t offset,
const String* text,
TextPosition* result) {
std::unique_ptr<Vector<wtf_size_t>> line_endings = WTF::GetLineEndings(*text);
*result = TextPosition::FromOffsetAndLineEndings(offset, *line_endings);
}
void InspectorCSSAgent::DidReplaceStyleSheetText(CSSStyleSheet* css_style_sheet,
const String& text) {
BindStyleSheet(css_style_sheet)->CSSOMStyleSheetTextReplaced(text);
}
void InspectorCSSAgent::StyleSheetChanged(
InspectorStyleSheetBase* style_sheet) {
if (g_frontend_operation_counter)
return;
FlushPendingProtocolNotifications();
GetFrontend()->styleSheetChanged(style_sheet->Id());
}
void InspectorCSSAgent::ResetPseudoStates() {
HeapHashSet<Member<Document>> documents_to_change;
for (auto& state : node_id_to_forced_pseudo_state_) {
if (auto* element = To<Element>(dom_agent_->NodeForId(state.key)))
documents_to_change.insert(&element->GetDocument());
}
for (auto& count : node_id_to_number_focused_children_) {
if (auto* element = To<Element>(dom_agent_->NodeForId(count.key)))
documents_to_change.insert(&element->GetDocument());
}
node_id_to_forced_pseudo_state_.clear();
node_id_to_number_focused_children_.clear();
for (auto& document : documents_to_change) {
document->GetStyleEngine().MarkAllElementsForStyleRecalc(
StyleChangeReasonForTracing::Create(style_change_reason::kInspector));
}
}
HeapVector<Member<CSSStyleDeclaration>> InspectorCSSAgent::MatchingStyles(
Element* element) {
PseudoId pseudo_id = kPseudoIdNone;
AtomicString view_transition_name = g_null_atom;
element = GetPseudoIdAndTag(element, pseudo_id, view_transition_name);
if (!element)
return {};
StyleResolver& style_resolver = element->GetDocument().GetStyleResolver();
// TODO(masonf,futhark): We need to update slot assignments here, so that
// the flat tree is up to date for the call to PseudoCSSRulesForElement().
// Eventually, RecalcSlotAssignments() should be called directly in
// PseudoCSSRulesForElement(), but there are a number of sites within
// inspector code that traverse the tree and call PseudoCSSRulesForElement()
// for each element.
element->GetDocument().GetSlotAssignmentEngine().RecalcSlotAssignments();
// This ensures that active stylesheets are up-to-date, such that
// the subsequent collection of matching rules actually match against
// the correct RuleSets.
element->GetDocument().GetStyleEngine().UpdateActiveStyle();
HeapVector<Member<CSSStyleRule>> rules =
FilterDuplicateRules(style_resolver.PseudoCSSRulesForElement(
element, pseudo_id, view_transition_name,
StyleResolver::kAllCSSRules));
HeapVector<Member<CSSStyleDeclaration>> styles;
if (!pseudo_id && element->style())
styles.push_back(element->style());
for (unsigned i = rules.size(); i > 0; --i) {
CSSStyleSheet* parent_style_sheet = rules.at(i - 1)->parentStyleSheet();
if (!parent_style_sheet || !parent_style_sheet->ownerNode())
continue; // User agent.
styles.push_back(rules.at(i - 1)->style());
}
return styles;
}
CSSStyleDeclaration* InspectorCSSAgent::FindEffectiveDeclaration(
const CSSPropertyName& property_name,
const HeapVector<Member<CSSStyleDeclaration>>& styles) {
if (!styles.size())
return nullptr;
String longhand = property_name.ToAtomicString();
CSSStyleDeclaration* found_style = nullptr;
for (unsigned i = 0; i < styles.size(); ++i) {
CSSStyleDeclaration* style = styles.at(i).Get();
if (style->getPropertyValue(longhand).empty())
continue;
if (style->getPropertyPriority(longhand) == "important")
return style;
if (!found_style)
found_style = style;
}
return found_style ? found_style : styles.at(0).Get();
}
protocol::Response InspectorCSSAgent::setEffectivePropertyValueForNode(
int node_id,
const String& property_name,
const String& value) {
Element* element = nullptr;
protocol::Response response = dom_agent_->AssertElement(node_id, element);
if (!response.IsSuccess())
return response;
if (element->GetPseudoId())
return protocol::Response::ServerError("Elements is pseudo");
if (!element->GetDocument().IsActive()) {
return protocol::Response::ServerError(
"Can't edit a node from a non-active document");
}
std::optional<CSSPropertyName> css_property_name =
CSSPropertyName::From(element->GetExecutionContext(), property_name);
if (!css_property_name.has_value())
return protocol::Response::ServerError("Invalid property name");
CSSStyleDeclaration* style =
FindEffectiveDeclaration(*css_property_name, MatchingStyles(element));
if (!style)
return protocol::Response::ServerError("Can't find a style to edit");
bool force_important = false;
InspectorStyleSheetBase* inspector_style_sheet = nullptr;
CSSRuleSourceData* source_data;
// An absence of the parent rule means that given style is an inline style.
if (style->parentRule()) {
InspectorStyleSheet* style_sheet =
BindStyleSheet(style->ParentStyleSheet());
inspector_style_sheet = style_sheet;
source_data = style_sheet->SourceDataForRule(style->parentRule());
} else {
InspectorStyleSheetForInlineStyle* inline_style_sheet =
AsInspectorStyleSheet(element);
inspector_style_sheet = inline_style_sheet;
source_data = inline_style_sheet->RuleSourceData();
}
if (!source_data)
return protocol::Response::ServerError("Can't find a source to edit");
Vector<StylePropertyShorthand, 4> shorthands;
getMatchingShorthandsForLonghand(css_property_name->Id(), &shorthands);
String shorthand =
shorthands.size() > 0
? CSSProperty::Get(shorthands[0].id()).GetPropertyNameString()
: String();
String longhand = css_property_name->ToAtomicString();
int found_index = -1;
Vector<CSSPropertySourceData>& properties = source_data->property_data;
for (unsigned i = 0; i < properties.size(); ++i) {
CSSPropertySourceData property = properties[properties.size() - i - 1];
String name = property.name;
if (property.disabled)
continue;
if (name != shorthand && name != longhand)
continue;
if (property.important || found_index == -1)
found_index = properties.size() - i - 1;
if (property.important)
break;
}
SourceRange body_range = source_data->rule_body_range;
String style_sheet_text;
inspector_style_sheet->GetText(&style_sheet_text);
String style_text =
style_sheet_text.Substring(body_range.start, body_range.length());
SourceRange change_range;
if (found_index == -1) {
String new_property_text = "\n" + longhand + ": " + value +
(force_important ? " !important" : "") + ";";
if (!style_text.empty() && !style_text.StripWhiteSpace().EndsWith(';'))
new_property_text = ";" + new_property_text;
style_text = style_text + new_property_text;
change_range.start = body_range.end;
change_range.end = body_range.end + new_property_text.length();
} else {
CSSPropertySourceData declaration = properties[found_index];
String new_value_text;
if (declaration.name == shorthand) {
new_value_text = CreateShorthandValue(element->GetDocument(), shorthand,
declaration.value, longhand, value);
} else {
new_value_text = value;
}
String new_property_text =
declaration.name + ": " + new_value_text +
(declaration.important || force_important ? " !important" : "") + ";";
style_text.replace(declaration.range.start - body_range.start,
declaration.range.length(), new_property_text);
change_range.start = declaration.range.start;
change_range.end = change_range.start + new_property_text.length();
}
CSSStyleDeclaration* result_style;
return SetStyleText(inspector_style_sheet, body_range, style_text,
result_style);
}
protocol::Response InspectorCSSAgent::getBackgroundColors(
int node_id,
Maybe<protocol::Array<String>>* background_colors,
Maybe<String>* computed_font_size,
Maybe<String>* computed_font_weight) {
Element* element = nullptr;
protocol::Response response = dom_agent_->AssertElement(node_id, element);
if (!response.IsSuccess())
return response;
Vector<Color> bgcolors;
String fs;
String fw;
float text_opacity = 1.0f;
InspectorCSSAgent::GetBackgroundColors(element, &bgcolors, &fs, &fw,
&text_opacity);
if (bgcolors.size()) {
*background_colors = std::make_unique<protocol::Array<String>>();
for (const auto& color : bgcolors) {
background_colors->value().emplace_back(
cssvalue::CSSColor::SerializeAsCSSComponentValue(color));
}
}
if (!fs.empty())
*computed_font_size = fs;
if (!fw.empty())
*computed_font_weight = fw;
return protocol::Response::Success();
}
// static
void InspectorCSSAgent::GetBackgroundColors(Element* element,
Vector<Color>* colors,
String* computed_font_size,
String* computed_font_weight,
float* text_opacity) {
InspectorContrast contrast(&element->GetDocument());
*colors = contrast.GetBackgroundColors(element, text_opacity);
auto text_info = contrast.GetTextInfo(element);
*computed_font_size = text_info.font_size;
*computed_font_weight = text_info.font_weight;
}
void InspectorCSSAgent::SetCoverageEnabled(bool enabled) {
if (enabled == !!tracker_)
return;
tracker_ = enabled ? MakeGarbageCollected<StyleRuleUsageTracker>() : nullptr;
for (Document* document : dom_agent_->Documents())
document->GetStyleEngine().SetRuleUsageTracker(tracker_);
}
void InspectorCSSAgent::WillChangeStyleElement(Element* element) {
resource_container_->EraseStyleElementContent(element->GetDomNodeId());
}
protocol::Response InspectorCSSAgent::startRuleUsageTracking() {
coverage_enabled_.Set(true);
SetCoverageEnabled(true);
for (Document* document : dom_agent_->Documents()) {
document->GetStyleEngine().MarkAllElementsForStyleRecalc(
StyleChangeReasonForTracing::Create(style_change_reason::kInspector));
document->UpdateStyleAndLayoutTree();
}
return protocol::Response::Success();
}
protocol::Response InspectorCSSAgent::stopRuleUsageTracking(
std::unique_ptr<protocol::Array<protocol::CSS::RuleUsage>>* result) {
for (Document* document : dom_agent_->Documents())
document->UpdateStyleAndLayoutTree();
double timestamp;
protocol::Response response = takeCoverageDelta(result, &timestamp);
SetCoverageEnabled(false);
return response;
}
void InspectorCSSAgent::BuildRulesMap(
InspectorStyleSheet* style_sheet,
HeapHashMap<Member<const StyleRule>, Member<CSSStyleRule>>*
rule_to_css_rule) {
const CSSRuleVector& css_rules = style_sheet->FlatRules();
for (auto css_rule : css_rules) {
if (css_rule->GetType() == CSSRule::kStyleRule) {
CSSStyleRule* css_style_rule = DynamicTo<CSSStyleRule>(css_rule.Get());
rule_to_css_rule->Set(css_style_rule->GetStyleRule(), css_style_rule);
}
if (css_rule->GetType() == CSSRule::kImportRule) {
CSSImportRule* css_import_rule = DynamicTo<CSSImportRule>(css_rule.Get());
if (!css_import_rule->styleSheet())
continue;
auto it = css_style_sheet_to_inspector_style_sheet_.find(
const_cast<CSSStyleSheet*>(css_import_rule->styleSheet()));
if (it == css_style_sheet_to_inspector_style_sheet_.end())
continue;
InspectorStyleSheet* imported_style_sheet = it->value;
BuildRulesMap(imported_style_sheet, rule_to_css_rule);
}
}
}
protocol::Response InspectorCSSAgent::takeCoverageDelta(
std::unique_ptr<protocol::Array<protocol::CSS::RuleUsage>>* result,
double* out_timestamp) {
if (!tracker_) {
return protocol::Response::ServerError(
"CSS rule usage tracking is not enabled");
}
StyleRuleUsageTracker::RuleListByStyleSheet coverage_delta =
tracker_->TakeDelta();
*out_timestamp = base::TimeTicks::Now().since_origin().InSecondsF();
*result = std::make_unique<protocol::Array<protocol::CSS::RuleUsage>>();
for (const auto& entry : coverage_delta) {
const CSSStyleSheet* css_style_sheet = entry.key.Get();
auto style_sheet_it = css_style_sheet_to_inspector_style_sheet_.find(
const_cast<CSSStyleSheet*>(css_style_sheet));
if (style_sheet_it == css_style_sheet_to_inspector_style_sheet_.end())
continue;
InspectorStyleSheet* style_sheet = style_sheet_it->value;
HeapHashMap<Member<const StyleRule>, Member<CSSStyleRule>> rule_to_css_rule;
BuildRulesMap(style_sheet, &rule_to_css_rule);
for (auto used_rule : *entry.value) {
auto rule_to_css_rule_it = rule_to_css_rule.find(used_rule);
if (rule_to_css_rule_it == rule_to_css_rule.end())
continue;
CSSStyleRule* css_style_rule = rule_to_css_rule_it->value;
auto it = css_style_sheet_to_inspector_style_sheet_.find(
const_cast<CSSStyleSheet*>(css_style_rule->parentStyleSheet()));
if (it == css_style_sheet_to_inspector_style_sheet_.end())
continue;
// If the rule comes from an @import'ed file, the `rule_style_sheet` is
// different from `style_sheet`.
InspectorStyleSheet* rule_style_sheet = it->value;
CSSRule* rule = css_style_rule;
while (rule) {
if (std::unique_ptr<protocol::CSS::RuleUsage> rule_usage_object =
rule_style_sheet->BuildObjectForRuleUsage(rule, true)) {
(*result)->emplace_back(std::move(rule_usage_object));
}
rule = rule->parentRule();
}
}
}
return protocol::Response::Success();
}
protocol::Response InspectorCSSAgent::trackComputedStyleUpdates(
std::unique_ptr<protocol::Array<protocol::CSS::CSSComputedStyleProperty>>
properties_to_track) {
tracked_computed_styles_.clear();
if (properties_to_track->size() == 0) {
if (computed_style_updated_callback_) {
computed_style_updated_callback_->sendSuccess(
BuildArrayForComputedStyleUpdatedNodes());
computed_style_updated_callback_ = nullptr;
}
computed_style_updated_node_ids_.clear();
return protocol::Response::Success();
}
for (const auto& property : *properties_to_track) {
String property_name = property->getName();
HashMap<String, HashSet<String>>::iterator it =
tracked_computed_styles_.find(property_name);
if (it != tracked_computed_styles_.end()) {
it->value.insert(property->getValue());
} else {
HashSet<String> tracked_values;
tracked_values.insert(property->getValue());
tracked_computed_styles_.Set(property_name, tracked_values);
}
}
return protocol::Response::Success();
}
void InspectorCSSAgent::takeComputedStyleUpdates(
std::unique_ptr<TakeComputedStyleUpdatesCallback> callback) {
if (tracked_computed_styles_.empty()) {
callback->sendFailure(protocol::Response::ServerError(
"No computed styles are being tracked right now."));
return;
}
if (computed_style_updated_callback_) {
callback->sendFailure(protocol::Response::ServerError(
"A previous request has not been resolved yet."));
return;
}
if (computed_style_updated_node_ids_.size()) {
callback->sendSuccess(BuildArrayForComputedStyleUpdatedNodes());
computed_style_updated_node_ids_.clear();
return;
}
computed_style_updated_callback_ = std::move(callback);
}
void InspectorCSSAgent::DidUpdateComputedStyle(Element* element,
const ComputedStyle* old_style,
const ComputedStyle* new_style) {
if (tracked_computed_styles_.empty())
return;
if (!old_style && !new_style)
return;
int id = dom_agent_->BoundNodeId(element);
// If node is not mapped yet -> ignore the event.
if (!id)
return;
if (computed_style_updated_node_ids_.Contains(id))
return;
// Compares with the currently tracked styles to see if this node should be
// included
for (const auto& tracked_computed_style : tracked_computed_styles_) {
const HashSet<String>& tracked_values = tracked_computed_style.value;
CSSPropertyRef ref(tracked_computed_style.key, element->GetDocument());
if (!ref.IsValid())
continue;
const CSSProperty& tracked_property = ref.GetProperty();
// TODO(crbug/1108356): consider using the Prepared Value once it's ready
const CSSValue* old_value = old_style
? ComputedStyleUtils::ComputedPropertyValue(
tracked_property, *old_style)
: nullptr;
const CSSValue* new_value = new_style
? ComputedStyleUtils::ComputedPropertyValue(
tracked_property, *new_style)
: nullptr;
if (old_value == new_value)
continue;
String old_value_text = old_value ? old_value->CssText() : "";
String new_value_text = new_value ? new_value->CssText() : "";
if (old_value_text == new_value_text)
continue;
if (tracked_values.Contains(old_value_text) ||
tracked_values.Contains(new_value_text)) {
computed_style_updated_node_ids_.insert(id);
return;
}
}
}
void InspectorCSSAgent::Will(const probe::RecalculateStyle&) {}
void InspectorCSSAgent::Did(const probe::RecalculateStyle&) {
if (computed_style_updated_callback_ &&
computed_style_updated_node_ids_.size()) {
computed_style_updated_callback_->sendSuccess(
BuildArrayForComputedStyleUpdatedNodes());
computed_style_updated_node_ids_.clear();
computed_style_updated_callback_ = nullptr;
}
}
std::unique_ptr<protocol::Array<int>>
InspectorCSSAgent::BuildArrayForComputedStyleUpdatedNodes() {
std::unique_ptr<protocol::Array<int>> nodes =
std::make_unique<protocol::Array<int>>();
for (int node_id : computed_style_updated_node_ids_) {
nodes->push_back(node_id);
}
return nodes;
}
void InspectorCSSAgent::Trace(Visitor* visitor) const {
visitor->Trace(dom_agent_);
visitor->Trace(inspected_frames_);
visitor->Trace(network_agent_);
visitor->Trace(resource_content_loader_);
visitor->Trace(resource_container_);
visitor->Trace(id_to_inspector_style_sheet_);
visitor->Trace(id_to_inspector_style_sheet_for_inline_style_);
visitor->Trace(css_style_sheet_to_inspector_style_sheet_);
visitor->Trace(document_to_css_style_sheets_);
visitor->Trace(invalidated_documents_);
visitor->Trace(node_to_inspector_style_sheet_);
visitor->Trace(inspector_user_agent_style_sheet_);
visitor->Trace(user_agent_view_transition_style_sheet_);
visitor->Trace(tracker_);
InspectorBaseAgent::Trace(visitor);
}
void InspectorCSSAgent::LocalFontsEnabled(bool* result) {
if (!local_fonts_enabled_.Get())
*result = false;
}
protocol::Response InspectorCSSAgent::setLocalFontsEnabled(bool enabled) {
local_fonts_enabled_.Set(enabled);
// TODO(alexrudenko): how to rerender fonts so that
// local_fonts_enabled_ applies without page reload?
return protocol::Response::Success();
}
} // namespace blink