1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/ArrayUtils.h"
9 #include "inLayoutUtils.h"
11 #include "gfxTextRun.h"
12 #include "mozilla/dom/HTMLSlotElement.h"
14 #include "nsContentList.h"
16 #include "nsIContentInlines.h"
17 #include "nsIScrollableFrame.h"
18 #include "mozilla/dom/Document.h"
19 #include "mozilla/dom/DocumentInlines.h"
20 #include "mozilla/dom/HTMLTemplateElement.h"
21 #include "ChildIterator.h"
22 #include "nsComputedDOMStyle.h"
23 #include "mozilla/EventStateManager.h"
25 #include "nsBlockFrame.h"
26 #include "nsPresContext.h"
28 #include "mozilla/PresShell.h"
29 #include "mozilla/PresShellInlines.h"
30 #include "mozilla/StyleSheetInlines.h"
31 #include "mozilla/dom/CharacterData.h"
32 #include "mozilla/dom/CSSBinding.h"
33 #include "mozilla/dom/Element.h"
34 #include "mozilla/dom/CSSStyleRule.h"
35 #include "mozilla/dom/CSSKeyframesRule.h"
36 #include "mozilla/dom/Highlight.h"
37 #include "mozilla/dom/HighlightRegistry.h"
38 #include "mozilla/dom/InspectorUtilsBinding.h"
39 #include "mozilla/dom/LinkStyle.h"
40 #include "mozilla/dom/ToJSValue.h"
41 #include "nsCSSProps.h"
42 #include "nsCSSValue.h"
44 #include "mozilla/ServoStyleSet.h"
45 #include "nsLayoutUtils.h"
46 #include "nsNameSpaceManager.h"
47 #include "nsStyleUtil.h"
48 #include "nsQueryObject.h"
49 #include "mozilla/ServoBindings.h"
50 #include "mozilla/ServoStyleRuleMap.h"
51 #include "mozilla/ServoCSSParser.h"
52 #include "mozilla/StaticPrefs_layout.h"
53 #include "mozilla/dom/InspectorUtils.h"
54 #include "mozilla/dom/InspectorFontFace.h"
55 #include "mozilla/gfx/Matrix.h"
57 using namespace mozilla
;
58 using namespace mozilla::css
;
59 using namespace mozilla::dom
;
64 static already_AddRefed
<const ComputedStyle
> GetCleanComputedStyleForElement(
65 dom::Element
* aElement
, PseudoStyleType aPseudo
,
66 nsAtom
* aFunctionalPseudoParameter
) {
69 Document
* doc
= aElement
->GetComposedDoc();
74 PresShell
* presShell
= doc
->GetPresShell();
79 nsPresContext
* presContext
= presShell
->GetPresContext();
84 presContext
->EnsureSafeToHandOutCSSRules();
86 return nsComputedDOMStyle::GetComputedStyle(aElement
, aPseudo
,
87 aFunctionalPseudoParameter
);
91 void InspectorUtils::GetAllStyleSheets(GlobalObject
& aGlobalObject
,
92 Document
& aDocument
, bool aDocumentOnly
,
93 nsTArray
<RefPtr
<StyleSheet
>>& aResult
) {
94 // Get the agent, then user and finally xbl sheets in the style set.
95 PresShell
* presShell
= aDocument
.GetPresShell();
96 nsTHashSet
<StyleSheet
*> sheetSet
;
99 ServoStyleSet
* styleSet
= presShell
->StyleSet();
101 if (!aDocumentOnly
) {
102 const StyleOrigin kOrigins
[] = {StyleOrigin::UserAgent
,
104 for (const auto origin
: kOrigins
) {
105 for (size_t i
= 0, count
= styleSet
->SheetCount(origin
); i
< count
;
107 aResult
.AppendElement(styleSet
->SheetAt(origin
, i
));
112 AutoTArray
<StyleSheet
*, 32> nonDocumentSheets
;
113 styleSet
->AppendAllNonDocumentAuthorSheets(nonDocumentSheets
);
115 // The non-document stylesheet array can have duplicates due to adopted
117 nsTHashSet
<StyleSheet
*> sheetSet
;
118 for (StyleSheet
* sheet
: nonDocumentSheets
) {
119 if (sheetSet
.EnsureInserted(sheet
)) {
120 aResult
.AppendElement(sheet
);
125 // Get the document sheets.
126 for (size_t i
= 0; i
< aDocument
.SheetCount(); i
++) {
127 aResult
.AppendElement(aDocument
.SheetAt(i
));
130 for (auto& sheet
: aDocument
.AdoptedStyleSheets()) {
131 if (sheetSet
.EnsureInserted(sheet
)) {
132 aResult
.AppendElement(sheet
);
137 bool InspectorUtils::IsIgnorableWhitespace(CharacterData
& aDataNode
) {
138 if (!aDataNode
.TextIsOnlyWhitespace()) {
142 // Okay. We have only white space. Let's check the white-space
143 // property now and make sure that this isn't preformatted text...
144 if (nsIFrame
* frame
= aDataNode
.GetPrimaryFrame()) {
145 return !frame
->StyleText()->WhiteSpaceIsSignificant();
148 // empty inter-tag text node without frame, e.g., in between <table>\n<tr>
153 nsINode
* InspectorUtils::GetParentForNode(nsINode
& aNode
,
154 bool aShowingAnonymousContent
) {
155 if (nsINode
* parent
= aNode
.GetParentNode()) {
158 if (aNode
.IsDocument()) {
159 return inLayoutUtils::GetContainerFor(*aNode
.AsDocument());
161 if (aShowingAnonymousContent
) {
162 if (auto* frag
= DocumentFragment::FromNode(aNode
)) {
163 // This deals with shadow roots and HTMLTemplateElement.content.
164 return frag
->GetHost();
171 void InspectorUtils::GetChildrenForNode(nsINode
& aNode
,
172 bool aShowingAnonymousContent
,
173 bool aIncludeAssignedNodes
,
174 bool aIncludeSubdocuments
,
175 nsTArray
<RefPtr
<nsINode
>>& aResult
) {
176 if (aIncludeSubdocuments
) {
177 if (auto* doc
= inLayoutUtils::GetSubDocumentFor(&aNode
)) {
178 aResult
.AppendElement(doc
);
179 // XXX Do we really want to early-return?
184 if (!aShowingAnonymousContent
|| !aNode
.IsContent()) {
185 for (nsINode
* child
= aNode
.GetFirstChild(); child
;
186 child
= child
->GetNextSibling()) {
187 aResult
.AppendElement(child
);
192 if (auto* tmpl
= HTMLTemplateElement::FromNode(aNode
)) {
193 aResult
.AppendElement(tmpl
->Content());
194 // XXX Do we really want to early-return?
198 if (auto* element
= Element::FromNode(aNode
)) {
199 if (auto* shadow
= element
->GetShadowRoot()) {
200 aResult
.AppendElement(shadow
);
203 nsIContent
* parent
= aNode
.AsContent();
204 if (auto* node
= nsLayoutUtils::GetMarkerPseudo(parent
)) {
205 aResult
.AppendElement(node
);
207 if (auto* node
= nsLayoutUtils::GetBeforePseudo(parent
)) {
208 aResult
.AppendElement(node
);
210 if (aIncludeAssignedNodes
) {
211 if (auto* slot
= HTMLSlotElement::FromNode(aNode
)) {
212 for (nsINode
* node
: slot
->AssignedNodes()) {
213 aResult
.AppendElement(node
);
217 for (nsIContent
* node
= parent
->GetFirstChild(); node
;
218 node
= node
->GetNextSibling()) {
219 aResult
.AppendElement(node
);
221 AutoTArray
<nsIContent
*, 4> anonKids
;
222 nsContentUtils::AppendNativeAnonymousChildren(parent
, anonKids
,
223 nsIContent::eAllChildren
);
224 for (nsIContent
* node
: anonKids
) {
225 aResult
.AppendElement(node
);
227 if (auto* node
= nsLayoutUtils::GetAfterPseudo(parent
)) {
228 aResult
.AppendElement(node
);
233 void InspectorUtils::GetCSSStyleRules(GlobalObject
& aGlobalObject
,
235 const nsAString
& aPseudo
,
236 bool aIncludeVisitedStyle
,
237 nsTArray
<RefPtr
<CSSStyleRule
>>& aResult
) {
238 auto [type
, functionalPseudoParameter
] =
239 nsCSSPseudoElements::ParsePseudoElement(aPseudo
,
240 CSSEnabledState::ForAllContent
);
245 RefPtr
<const ComputedStyle
> computedStyle
= GetCleanComputedStyleForElement(
246 &aElement
, *type
, functionalPseudoParameter
);
247 if (!computedStyle
) {
248 // This can fail for elements that are not in the document or
249 // if the document they're in doesn't have a presshell. Bail out.
253 if (aIncludeVisitedStyle
) {
254 if (auto* styleIfVisited
= computedStyle
->GetStyleIfVisited()) {
255 computedStyle
= styleIfVisited
;
259 Document
* doc
= aElement
.OwnerDoc();
260 PresShell
* presShell
= doc
->GetPresShell();
265 AutoTArray
<const StyleLockedStyleRule
*, 8> rawRuleList
;
266 Servo_ComputedValues_GetStyleRuleList(computedStyle
, &rawRuleList
);
268 AutoTArray
<ServoStyleRuleMap
*, 8> maps
;
270 ServoStyleSet
* styleSet
= presShell
->StyleSet();
271 ServoStyleRuleMap
* map
= styleSet
->StyleRuleMap();
272 maps
.AppendElement(map
);
275 // Now shadow DOM stuff...
276 if (auto* shadow
= aElement
.GetShadowRoot()) {
277 maps
.AppendElement(&shadow
->ServoStyleRuleMap());
281 for (auto* el
= aElement
.GetClosestNativeAnonymousSubtreeRootParentOrHost();
282 el
; el
= el
->GetClosestNativeAnonymousSubtreeRootParentOrHost()) {
283 if (auto* shadow
= el
->GetShadowRoot()) {
284 maps
.AppendElement(&shadow
->ServoStyleRuleMap());
288 for (auto* shadow
= aElement
.GetContainingShadow(); shadow
;
289 shadow
= shadow
->Host()->GetContainingShadow()) {
290 maps
.AppendElement(&shadow
->ServoStyleRuleMap());
293 // Rules from the assigned slot.
294 for (auto* slot
= aElement
.GetAssignedSlot(); slot
;
295 slot
= slot
->GetAssignedSlot()) {
296 if (auto* shadow
= slot
->GetContainingShadow()) {
297 maps
.AppendElement(&shadow
->ServoStyleRuleMap());
301 // Find matching rules in the table.
302 for (const StyleLockedStyleRule
* rawRule
: Reversed(rawRuleList
)) {
303 CSSStyleRule
* rule
= nullptr;
304 for (ServoStyleRuleMap
* map
: maps
) {
305 rule
= map
->Lookup(rawRule
);
311 aResult
.AppendElement(rule
);
315 printf_stderr("\n\n----\n\n");
316 computedStyle
->DumpMatchedRules();
318 Servo_StyleRule_Debug(rawRule
, &str
);
319 printf_stderr("\n\n----\n\n");
320 printf_stderr("%s\n", str
.get());
321 MOZ_CRASH_UNSAFE_PRINTF(
322 "We should be able to map raw rule %p to a rule in one of the %zu "
324 rawRule
, maps
.Length(), str
.get());
331 uint32_t InspectorUtils::GetRuleLine(GlobalObject
& aGlobal
, css::Rule
& aRule
) {
332 uint32_t line
= aRule
.GetLineNumber();
333 if (StyleSheet
* sheet
= aRule
.GetStyleSheet()) {
334 if (auto* link
= LinkStyle::FromNodeOrNull(sheet
->GetOwnerNode())) {
335 line
+= link
->GetLineNumber();
342 uint32_t InspectorUtils::GetRuleColumn(GlobalObject
& aGlobal
,
344 return aRule
.GetColumnNumber();
348 uint32_t InspectorUtils::GetRelativeRuleLine(GlobalObject
& aGlobal
,
350 // Rule lines are 0-based, but inspector wants 1-based.
351 return aRule
.GetLineNumber() + 1;
354 void InspectorUtils::GetRuleIndex(GlobalObject
& aGlobal
, css::Rule
& aRule
,
355 nsTArray
<uint32_t>& aResult
) {
356 css::Rule
* currentRule
= &aRule
;
359 css::Rule
* parentRule
= currentRule
->GetParentRule();
360 dom::CSSRuleList
* ruleList
= nullptr;
363 if (parentRule
->IsGroupRule()) {
364 ruleList
= static_cast<css::GroupRule
*>(parentRule
)->CssRules();
365 } else if (parentRule
->Type() == StyleCssRuleType::Keyframes
) {
366 ruleList
= static_cast<CSSKeyframesRule
*>(parentRule
)->CssRules();
368 MOZ_ASSERT_UNREACHABLE("Unknown parent rule type?");
370 } else if (StyleSheet
* sheet
= currentRule
->GetStyleSheet()) {
371 ruleList
= sheet
->GetCssRulesInternal();
379 for (uint32_t i
= 0, len
= ruleList
->Length(); i
< len
; ++i
) {
380 css::Rule
* rule
= ruleList
->Item(i
);
381 if (currentRule
== rule
) {
383 aResult
.InsertElementAt(0, i
);
392 currentRule
= parentRule
;
393 } while (currentRule
);
397 bool InspectorUtils::HasRulesModifiedByCSSOM(GlobalObject
& aGlobal
,
398 StyleSheet
& aSheet
) {
399 return aSheet
.HasModifiedRulesForDevtools();
402 static uint32_t CollectAtRules(ServoCSSRuleList
& aRuleList
,
403 Sequence
<OwningNonNull
<css::Rule
>>& aResult
) {
404 uint32_t len
= aRuleList
.Length();
405 uint32_t ruleCount
= len
;
406 for (uint32_t i
= 0; i
< len
; ++i
) {
407 css::Rule
* rule
= aRuleList
.GetRule(i
);
408 // This collect rules we want to display in Devtools Style Editor toolbar.
409 // When adding a new StyleCssRuleType, put it in the "default" list, and
410 // file a new bug with
411 // https://bugzilla.mozilla.org/enter_bug.cgi?product=DevTools&component=Style%20Editor&short_desc=Consider%20displaying%20new%20XXX%20rule%20type%20in%20at-rules%20sidebar
412 // so the DevTools team gets notified and can decide if it should be
414 switch (rule
->Type()) {
415 case StyleCssRuleType::Media
:
416 case StyleCssRuleType::Supports
:
417 case StyleCssRuleType::LayerBlock
:
418 case StyleCssRuleType::Property
:
419 case StyleCssRuleType::Container
: {
420 Unused
<< aResult
.AppendElement(OwningNonNull(*rule
), fallible
);
423 case StyleCssRuleType::Style
:
424 case StyleCssRuleType::Import
:
425 case StyleCssRuleType::Document
:
426 case StyleCssRuleType::LayerStatement
:
427 case StyleCssRuleType::FontFace
:
428 case StyleCssRuleType::Page
:
429 case StyleCssRuleType::Keyframes
:
430 case StyleCssRuleType::Keyframe
:
431 case StyleCssRuleType::Margin
:
432 case StyleCssRuleType::Namespace
:
433 case StyleCssRuleType::CounterStyle
:
434 case StyleCssRuleType::FontFeatureValues
:
435 case StyleCssRuleType::FontPaletteValues
:
436 case StyleCssRuleType::Scope
:
440 if (rule
->IsGroupRule()) {
441 ruleCount
+= CollectAtRules(
442 *static_cast<css::GroupRule
*>(rule
)->CssRules(), aResult
);
448 void InspectorUtils::GetStyleSheetRuleCountAndAtRules(
449 GlobalObject
& aGlobal
, StyleSheet
& aSheet
,
450 InspectorStyleSheetRuleCountAndAtRulesResult
& aResult
) {
452 CollectAtRules(*aSheet
.GetCssRulesInternal(), aResult
.mAtRules
);
456 bool InspectorUtils::IsInheritedProperty(GlobalObject
& aGlobalObject
,
458 const nsACString
& aPropertyName
) {
459 return Servo_Property_IsInherited(aDocument
.EnsureStyleSet().RawData(),
464 void InspectorUtils::GetCSSPropertyNames(GlobalObject
& aGlobalObject
,
465 const PropertyNamesOptions
& aOptions
,
466 nsTArray
<nsString
>& aResult
) {
467 CSSEnabledState enabledState
= aOptions
.mIncludeExperimentals
468 ? CSSEnabledState::IgnoreEnabledState
469 : CSSEnabledState::ForAllContent
;
471 auto appendProperty
= [enabledState
, &aResult
](uint32_t prop
) {
472 nsCSSPropertyID cssProp
= nsCSSPropertyID(prop
);
473 if (nsCSSProps::IsEnabled(cssProp
, enabledState
)) {
474 aResult
.AppendElement(
475 NS_ConvertASCIItoUTF16(nsCSSProps::GetStringValue(cssProp
)));
480 for (; prop
< eCSSProperty_COUNT_no_shorthands
; ++prop
) {
481 if (!nsCSSProps::PropHasFlags(nsCSSPropertyID(prop
),
482 CSSPropFlags::Inaccessible
)) {
483 appendProperty(prop
);
487 if (aOptions
.mIncludeShorthands
) {
488 for (; prop
< eCSSProperty_COUNT
; ++prop
) {
489 appendProperty(prop
);
493 if (aOptions
.mIncludeAliases
) {
494 for (prop
= eCSSProperty_COUNT
; prop
< eCSSProperty_COUNT_with_aliases
;
496 appendProperty(prop
);
502 void InspectorUtils::GetCSSPropertyPrefs(GlobalObject
& aGlobalObject
,
503 nsTArray
<PropertyPref
>& aResult
) {
504 for (const auto* src
= nsCSSProps::kPropertyPrefTable
;
505 src
->mPropID
!= eCSSProperty_UNKNOWN
; src
++) {
506 PropertyPref
& dest
= *aResult
.AppendElement();
508 NS_ConvertASCIItoUTF16(nsCSSProps::GetStringValue(src
->mPropID
)));
509 dest
.mPref
.AssignASCII(src
->mPref
);
514 void InspectorUtils::GetSubpropertiesForCSSProperty(GlobalObject
& aGlobal
,
515 const nsACString
& aProperty
,
516 nsTArray
<nsString
>& aResult
,
518 nsCSSPropertyID propertyID
= nsCSSProps::LookupProperty(aProperty
);
520 if (propertyID
== eCSSProperty_UNKNOWN
) {
521 aRv
.Throw(NS_ERROR_FAILURE
);
525 if (propertyID
== eCSSPropertyExtra_variable
) {
526 aResult
.AppendElement(NS_ConvertUTF8toUTF16(aProperty
));
530 if (!nsCSSProps::IsShorthand(propertyID
)) {
531 nsString
* name
= aResult
.AppendElement();
532 CopyASCIItoUTF16(nsCSSProps::GetStringValue(propertyID
), *name
);
536 for (const nsCSSPropertyID
* props
=
537 nsCSSProps::SubpropertyEntryFor(propertyID
);
538 *props
!= eCSSProperty_UNKNOWN
; ++props
) {
539 nsString
* name
= aResult
.AppendElement();
540 CopyASCIItoUTF16(nsCSSProps::GetStringValue(*props
), *name
);
545 bool InspectorUtils::CssPropertyIsShorthand(GlobalObject
& aGlobalObject
,
546 const nsACString
& aProperty
,
549 bool isShorthand
= Servo_Property_IsShorthand(&aProperty
, &found
);
551 aRv
.Throw(NS_ERROR_FAILURE
);
556 // This should match the constants in specified_value_info.rs
558 // Once we can use bitflags in consts, we can also cbindgen that and use them
560 static uint8_t ToServoCssType(InspectorPropertyType aType
) {
562 case InspectorPropertyType::Color
:
564 case InspectorPropertyType::Gradient
:
566 case InspectorPropertyType::Timing_function
:
569 MOZ_ASSERT_UNREACHABLE("Unknown property type?");
574 bool InspectorUtils::Supports(GlobalObject
&, const nsACString
& aDeclaration
,
575 const SupportsOptions
& aOptions
) {
576 return Servo_CSSSupports(&aDeclaration
, aOptions
.mUserAgent
, aOptions
.mChrome
,
580 bool InspectorUtils::CssPropertySupportsType(GlobalObject
& aGlobalObject
,
581 const nsACString
& aProperty
,
582 InspectorPropertyType aType
,
586 Servo_Property_SupportsType(&aProperty
, ToServoCssType(aType
), &found
);
588 aRv
.Throw(NS_ERROR_FAILURE
);
595 void InspectorUtils::GetCSSValuesForProperty(GlobalObject
& aGlobalObject
,
596 const nsACString
& aProperty
,
597 nsTArray
<nsString
>& aResult
,
600 Servo_Property_GetCSSValuesForProperty(&aProperty
, &found
, &aResult
);
602 aRv
.Throw(NS_ERROR_FAILURE
);
607 void InspectorUtils::RgbToColorName(GlobalObject
&, uint8_t aR
, uint8_t aG
,
608 uint8_t aB
, nsACString
& aColorName
) {
609 Servo_SlowRgbToColorName(aR
, aG
, aB
, &aColorName
);
613 void InspectorUtils::ColorToRGBA(GlobalObject
&, const nsACString
& aColorString
,
614 const Document
* aDoc
,
615 Nullable
<InspectorRGBATuple
>& aResult
) {
616 nscolor color
= NS_RGB(0, 0, 0);
618 ServoStyleSet
* styleSet
= nullptr;
620 if (PresShell
* ps
= aDoc
->GetPresShell()) {
621 styleSet
= ps
->StyleSet();
625 if (!ServoCSSParser::ComputeColor(styleSet
, NS_RGB(0, 0, 0), aColorString
,
631 InspectorRGBATuple
& tuple
= aResult
.SetValue();
632 tuple
.mR
= NS_GET_R(color
);
633 tuple
.mG
= NS_GET_G(color
);
634 tuple
.mB
= NS_GET_B(color
);
635 tuple
.mA
= nsStyleUtil::ColorComponentToFloat(NS_GET_A(color
));
639 void InspectorUtils::ColorTo(GlobalObject
&, const nsACString
& aFromColor
,
640 const nsACString
& aToColorSpace
,
641 Nullable
<InspectorColorToResult
>& aResult
) {
642 nsCString resultColor
;
643 nsTArray
<float> resultComponents
;
644 bool resultAdjusted
= false;
646 if (!ServoCSSParser::ColorTo(aFromColor
, aToColorSpace
, &resultColor
,
647 &resultComponents
, &resultAdjusted
)) {
652 auto& result
= aResult
.SetValue();
653 result
.mColor
.AssignASCII(resultColor
);
654 result
.mComponents
= std::move(resultComponents
);
655 result
.mAdjusted
= resultAdjusted
;
659 bool InspectorUtils::IsValidCSSColor(GlobalObject
& aGlobalObject
,
660 const nsACString
& aColorString
) {
661 return ServoCSSParser::IsValidCSSColor(aColorString
);
665 bool InspectorUtils::SetContentState(GlobalObject
& aGlobalObject
,
666 Element
& aElement
, uint64_t aState
,
668 RefPtr
<EventStateManager
> esm
=
669 inLayoutUtils::GetEventStateManagerFor(aElement
);
670 ElementState
state(aState
);
671 if (!esm
|| !EventStateManager::ManagesState(state
)) {
672 aRv
.Throw(NS_ERROR_INVALID_ARG
);
675 return esm
->SetContentState(&aElement
, state
);
679 bool InspectorUtils::RemoveContentState(GlobalObject
& aGlobalObject
,
680 Element
& aElement
, uint64_t aState
,
681 bool aClearActiveDocument
,
683 RefPtr
<EventStateManager
> esm
=
684 inLayoutUtils::GetEventStateManagerFor(aElement
);
685 ElementState
state(aState
);
686 if (!esm
|| !EventStateManager::ManagesState(state
)) {
687 aRv
.Throw(NS_ERROR_INVALID_ARG
);
691 bool result
= esm
->SetContentState(nullptr, state
);
693 if (aClearActiveDocument
&& state
== ElementState::ACTIVE
) {
694 EventStateManager
* activeESM
= static_cast<EventStateManager
*>(
695 EventStateManager::GetActiveEventStateManager());
696 if (activeESM
== esm
) {
697 EventStateManager::ClearGlobalActiveContent(nullptr);
705 uint64_t InspectorUtils::GetContentState(GlobalObject
& aGlobalObject
,
707 // NOTE: if this method is removed,
708 // please remove GetInternalValue from ElementState
709 return aElement
.State().GetInternalValue();
713 void InspectorUtils::GetUsedFontFaces(GlobalObject
& aGlobalObject
,
714 nsRange
& aRange
, uint32_t aMaxRanges
,
715 bool aSkipCollapsedWhitespace
,
716 nsLayoutUtils::UsedFontFaceList
& aResult
,
719 aRange
.GetUsedFontFaces(aResult
, aMaxRanges
, aSkipCollapsedWhitespace
);
726 static ElementState
GetStatesForPseudoClass(const nsAString
& aStatePseudo
) {
727 if (aStatePseudo
.IsEmpty() || aStatePseudo
[0] != u
':') {
728 return ElementState();
730 NS_ConvertUTF16toUTF8
statePseudo(Substring(aStatePseudo
, 1));
731 return ElementState(Servo_PseudoClass_GetStates(&statePseudo
));
735 void InspectorUtils::GetCSSPseudoElementNames(GlobalObject
& aGlobalObject
,
736 nsTArray
<nsString
>& aResult
) {
737 const auto kPseudoCount
=
738 static_cast<size_t>(PseudoStyleType::CSSPseudoElementsEnd
);
739 for (size_t i
= 0; i
< kPseudoCount
; ++i
) {
740 PseudoStyleType type
= static_cast<PseudoStyleType
>(i
);
741 if (!nsCSSPseudoElements::IsEnabled(type
, CSSEnabledState::ForAllContent
)) {
744 auto& string
= *aResult
.AppendElement();
745 // Use two semi-colons (though internally we use one).
747 nsAtom
* atom
= nsCSSPseudoElements::GetPseudoAtom(type
);
748 string
.Append(nsDependentAtomString(atom
));
753 void InspectorUtils::AddPseudoClassLock(GlobalObject
& aGlobalObject
,
755 const nsAString
& aPseudoClass
,
757 ElementState state
= GetStatesForPseudoClass(aPseudoClass
);
758 if (state
.IsEmpty()) {
762 aElement
.LockStyleStates(state
, aEnabled
);
766 void InspectorUtils::RemovePseudoClassLock(GlobalObject
& aGlobal
,
768 const nsAString
& aPseudoClass
) {
769 ElementState state
= GetStatesForPseudoClass(aPseudoClass
);
770 if (state
.IsEmpty()) {
774 aElement
.UnlockStyleStates(state
);
778 bool InspectorUtils::HasPseudoClassLock(GlobalObject
& aGlobalObject
,
780 const nsAString
& aPseudoClass
) {
781 ElementState state
= GetStatesForPseudoClass(aPseudoClass
);
782 if (state
.IsEmpty()) {
786 ElementState locks
= aElement
.LockedStyleStates().mLocks
;
787 return locks
.HasAllStates(state
);
791 void InspectorUtils::ClearPseudoClassLocks(GlobalObject
& aGlobalObject
,
793 aElement
.ClearStyleStateLocks();
797 void InspectorUtils::ParseStyleSheet(GlobalObject
& aGlobalObject
,
799 const nsACString
& aInput
,
801 aSheet
.ReparseSheet(aInput
, aRv
);
804 bool InspectorUtils::IsCustomElementName(GlobalObject
&, const nsAString
& aName
,
805 const nsAString
& aNamespaceURI
) {
806 if (aName
.IsEmpty()) {
811 nsNameSpaceManager::GetInstance()->RegisterNameSpace(aNamespaceURI
,
814 RefPtr
<nsAtom
> nameElt
= NS_Atomize(aName
);
815 return nsContentUtils::IsCustomElementName(nameElt
, namespaceID
);
818 bool InspectorUtils::IsElementThemed(GlobalObject
&, Element
& aElement
) {
819 // IsThemed will check if the native theme supports the widget using
820 // ThemeSupportsWidget which in turn will check that the widget is not
821 // already styled by content through nsNativeTheme::IsWidgetStyled. We
822 // assume that if the native theme styles the widget and the author did not
823 // override the appropriate styles, the theme will provide focus styling.
824 nsIFrame
* frame
= aElement
.GetPrimaryFrame(FlushType::Frames
);
825 return frame
&& frame
->IsThemed();
828 Element
* InspectorUtils::ContainingBlockOf(GlobalObject
&, Element
& aElement
) {
829 nsIFrame
* frame
= aElement
.GetPrimaryFrame(FlushType::Frames
);
833 nsIFrame
* cb
= frame
->GetContainingBlock();
837 return Element::FromNodeOrNull(cb
->GetContent());
840 void InspectorUtils::GetBlockLineCounts(GlobalObject
& aGlobal
,
842 Nullable
<nsTArray
<uint32_t>>& aResult
) {
843 nsBlockFrame
* block
=
844 do_QueryFrame(aElement
.GetPrimaryFrame(FlushType::Layout
));
850 // If CSS columns were specified on the actual block element (rather than an
851 // ancestor block, GetPrimaryFrame will return its ColumnSetWrapperFrame, and
852 // we need to drill down to the actual block that contains the lines.
853 if (block
->IsColumnSetWrapperFrame()) {
854 nsIFrame
* firstChild
= block
->PrincipalChildList().FirstChild();
855 if (!firstChild
->IsColumnSetFrame()) {
859 block
= do_QueryFrame(firstChild
->PrincipalChildList().FirstChild());
860 if (!block
|| block
->GetContent() != &aElement
) {
866 nsTArray
<uint32_t> result
;
868 result
.AppendElement(block
->Lines().size());
869 block
= static_cast<nsBlockFrame
*>(block
->GetNextInFlow());
872 aResult
.SetValue(std::move(result
));
875 static bool FrameHasSpecifiedSize(const nsIFrame
* aFrame
) {
876 auto wm
= aFrame
->GetWritingMode();
878 const nsStylePosition
* stylePos
= aFrame
->StylePosition();
880 return stylePos
->ISize(wm
).IsLengthPercentage() ||
881 stylePos
->BSize(wm
).IsLengthPercentage();
884 static bool IsFrameOutsideOfAncestor(const nsIFrame
* aFrame
,
885 const nsIFrame
* aAncestorFrame
,
886 const nsRect
& aAncestorRect
) {
887 nsRect frameRectInAncestorSpace
= nsLayoutUtils::TransformFrameRectToAncestor(
888 aFrame
, aFrame
->ScrollableOverflowRect(), RelativeTo
{aAncestorFrame
},
889 nullptr, nullptr, false, nullptr);
891 // We use nsRect::SaturatingUnionEdges because it correctly handles the case
892 // of a zero-width or zero-height frame, which we still want to consider as
893 // contributing to the union.
894 nsRect unionizedRect
=
895 frameRectInAncestorSpace
.SaturatingUnionEdges(aAncestorRect
);
897 // If frameRectInAncestorSpace is inside aAncestorRect then union of
898 // frameRectInAncestorSpace and aAncestorRect should be equal to aAncestorRect
899 // hence if it is equal, then false should be returned.
901 return !(unionizedRect
== aAncestorRect
);
904 static void AddOverflowingChildrenOfElement(const nsIFrame
* aFrame
,
905 const nsIFrame
* aAncestorFrame
,
907 nsSimpleContentList
& aList
) {
908 MOZ_ASSERT(aFrame
, "we assume the passed-in frame is non-null");
909 for (const auto& childList
: aFrame
->ChildLists()) {
910 for (const nsIFrame
* child
: childList
.mList
) {
911 // We want to identify if the child or any of its children have a
912 // frame that is outside of aAncestorFrame. Ideally, child would have
913 // a frame rect that encompasses all of its children, but this is not
914 // guaranteed by the frame tree. So instead we first check other
915 // conditions that indicate child is an interesting frame:
917 // 1) child has a specified size
918 // 2) none of child's children are implicated
920 // If either of these conditions are true, we *then* check if child's
921 // frame is outside of aAncestorFrame, and if so, we add child's content
924 if (FrameHasSpecifiedSize(child
) &&
925 IsFrameOutsideOfAncestor(child
, aAncestorFrame
, aRect
)) {
926 aList
.MaybeAppendElement(child
->GetContent());
930 uint32_t currListLength
= aList
.Length();
931 AddOverflowingChildrenOfElement(child
, aAncestorFrame
, aRect
, aList
);
933 // If child is a leaf node, length of aList should remain same after
934 // calling AddOverflowingChildrenOfElement on it.
935 if (currListLength
== aList
.Length() &&
936 IsFrameOutsideOfAncestor(child
, aAncestorFrame
, aRect
)) {
937 aList
.MaybeAppendElement(child
->GetContent());
943 already_AddRefed
<nsINodeList
> InspectorUtils::GetOverflowingChildrenOfElement(
944 GlobalObject
& aGlobal
, Element
& aElement
) {
945 RefPtr
<nsSimpleContentList
> list
= new nsSimpleContentList(&aElement
);
946 const nsIScrollableFrame
* scrollFrame
= aElement
.GetScrollFrame();
947 // Element must have a nsIScrollableFrame
949 return list
.forget();
952 auto scrollPortRect
= scrollFrame
->GetScrollPortRect();
953 const nsIFrame
* outerFrame
= do_QueryFrame(scrollFrame
);
954 const nsIFrame
* scrolledFrame
= scrollFrame
->GetScrolledFrame();
955 AddOverflowingChildrenOfElement(scrolledFrame
, outerFrame
, scrollPortRect
,
957 return list
.forget();
961 void InspectorUtils::GetRegisteredCssHighlights(GlobalObject
& aGlobalObject
,
964 nsTArray
<nsString
>& aResult
) {
965 for (auto const& iter
: aDocument
.HighlightRegistry().HighlightsOrdered()) {
966 const RefPtr
<nsAtom
>& highlightName
= iter
.first();
967 const RefPtr
<Highlight
>& highlight
= iter
.second();
968 if (!aActiveOnly
|| highlight
->Size() > 0) {
969 aResult
.AppendElement(highlightName
->GetUTF16String());
975 void InspectorUtils::GetCSSRegisteredProperties(
976 GlobalObject
& aGlobalObject
, Document
& aDocument
,
977 nsTArray
<InspectorCSSPropertyDefinition
>& aResult
) {
978 nsTArray
<StylePropDef
> result
;
980 ServoStyleSet
& styleSet
= aDocument
.EnsureStyleSet();
981 // Update the rules before looking up @property rules.
982 styleSet
.UpdateStylistIfNeeded();
984 Servo_GetRegisteredCustomProperties(styleSet
.RawData(), &result
);
985 for (const auto& propDef
: result
) {
986 InspectorCSSPropertyDefinition
& property
= *aResult
.AppendElement();
988 // Servo does not include the "--" prefix in the property definition name.
989 // Add it back as it's easier for DevTools to handle them _with_ "--".
990 property
.mName
.AssignLiteral("--");
991 property
.mName
.Append(nsAtomCString(propDef
.name
.AsAtom()));
992 property
.mSyntax
.Append(propDef
.syntax
);
993 property
.mInherits
= propDef
.inherits
;
994 if (propDef
.has_initial_value
) {
995 property
.mInitialValue
.Append(propDef
.initial_value
);
997 property
.mInitialValue
.SetIsVoid(true);
999 property
.mFromJS
= propDef
.from_js
;
1004 void InspectorUtils::GetRuleBodyTextOffsets(
1005 GlobalObject
&, const nsACString
& aInitialText
,
1006 Nullable
<InspectorGetRuleBodyTextResult
>& aResult
) {
1007 uint32_t resultStartOffset
;
1008 uint32_t resultEndOffset
;
1010 if (!Servo_GetRuleBodyTextOffsets(&aInitialText
, &resultStartOffset
,
1011 &resultEndOffset
)) {
1016 InspectorGetRuleBodyTextResult
& offsets
= aResult
.SetValue();
1017 offsets
.mStartOffset
= resultStartOffset
;
1018 offsets
.mEndOffset
= resultEndOffset
;
1022 } // namespace mozilla