no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / layout / inspector / InspectorUtils.cpp
blob0b2138e9fc1b3fd64791b2e21d51c7d2dd2eada2
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"
13 #include "nsArray.h"
14 #include "nsContentList.h"
15 #include "nsString.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"
24 #include "nsAtom.h"
25 #include "nsBlockFrame.h"
26 #include "nsPresContext.h"
27 #include "nsRange.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"
43 #include "nsColor.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;
61 namespace mozilla {
62 namespace dom {
64 static already_AddRefed<const ComputedStyle> GetCleanComputedStyleForElement(
65 dom::Element* aElement, PseudoStyleType aPseudo,
66 nsAtom* aFunctionalPseudoParameter) {
67 MOZ_ASSERT(aElement);
69 Document* doc = aElement->GetComposedDoc();
70 if (!doc) {
71 return nullptr;
74 PresShell* presShell = doc->GetPresShell();
75 if (!presShell) {
76 return nullptr;
79 nsPresContext* presContext = presShell->GetPresContext();
80 if (!presContext) {
81 return nullptr;
84 presContext->EnsureSafeToHandOutCSSRules();
86 return nsComputedDOMStyle::GetComputedStyle(aElement, aPseudo,
87 aFunctionalPseudoParameter);
90 /* static */
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;
98 if (presShell) {
99 ServoStyleSet* styleSet = presShell->StyleSet();
101 if (!aDocumentOnly) {
102 const StyleOrigin kOrigins[] = {StyleOrigin::UserAgent,
103 StyleOrigin::User};
104 for (const auto origin : kOrigins) {
105 for (size_t i = 0, count = styleSet->SheetCount(origin); i < count;
106 i++) {
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
116 // stylesheets.
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()) {
139 return false;
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>
149 return true;
152 /* static */
153 nsINode* InspectorUtils::GetParentForNode(nsINode& aNode,
154 bool aShowingAnonymousContent) {
155 if (nsINode* parent = aNode.GetParentNode()) {
156 return parent;
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();
167 return nullptr;
170 /* static */
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?
180 return;
184 if (!aShowingAnonymousContent || !aNode.IsContent()) {
185 for (nsINode* child = aNode.GetFirstChild(); child;
186 child = child->GetNextSibling()) {
187 aResult.AppendElement(child);
189 return;
192 if (auto* tmpl = HTMLTemplateElement::FromNode(aNode)) {
193 aResult.AppendElement(tmpl->Content());
194 // XXX Do we really want to early-return?
195 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);
232 /* static */
233 void InspectorUtils::GetCSSStyleRules(GlobalObject& aGlobalObject,
234 Element& aElement,
235 const nsAString& aPseudo,
236 bool aIncludeVisitedStyle,
237 nsTArray<RefPtr<CSSStyleRule>>& aResult) {
238 auto [type, functionalPseudoParameter] =
239 nsCSSPseudoElements::ParsePseudoElement(aPseudo,
240 CSSEnabledState::ForAllContent);
241 if (!type) {
242 return;
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.
250 return;
253 if (aIncludeVisitedStyle) {
254 if (auto* styleIfVisited = computedStyle->GetStyleIfVisited()) {
255 computedStyle = styleIfVisited;
259 Document* doc = aElement.OwnerDoc();
260 PresShell* presShell = doc->GetPresShell();
261 if (!presShell) {
262 return;
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());
280 // Now NAC:
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);
306 if (rule) {
307 break;
310 if (rule) {
311 aResult.AppendElement(rule);
312 } else {
313 #ifdef DEBUG
314 aElement.Dump();
315 printf_stderr("\n\n----\n\n");
316 computedStyle->DumpMatchedRules();
317 nsAutoCString str;
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 "
323 "maps: %s\n",
324 rawRule, maps.Length(), str.get());
325 #endif
330 /* static */
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();
338 return line;
341 /* static */
342 uint32_t InspectorUtils::GetRuleColumn(GlobalObject& aGlobal,
343 css::Rule& aRule) {
344 return aRule.GetColumnNumber();
347 /* static */
348 uint32_t InspectorUtils::GetRelativeRuleLine(GlobalObject& aGlobal,
349 css::Rule& aRule) {
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;
358 do {
359 css::Rule* parentRule = currentRule->GetParentRule();
360 dom::CSSRuleList* ruleList = nullptr;
362 if (parentRule) {
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();
367 } else {
368 MOZ_ASSERT_UNREACHABLE("Unknown parent rule type?");
370 } else if (StyleSheet* sheet = currentRule->GetStyleSheet()) {
371 ruleList = sheet->GetCssRulesInternal();
374 if (!ruleList) {
375 return;
378 bool found = false;
379 for (uint32_t i = 0, len = ruleList->Length(); i < len; ++i) {
380 css::Rule* rule = ruleList->Item(i);
381 if (currentRule == rule) {
382 found = true;
383 aResult.InsertElementAt(0, i);
384 break;
388 if (!found) {
389 return;
392 currentRule = parentRule;
393 } while (currentRule);
396 /* static */
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
413 // displayed.
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);
421 break;
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:
437 break;
440 if (rule->IsGroupRule()) {
441 ruleCount += CollectAtRules(
442 *static_cast<css::GroupRule*>(rule)->CssRules(), aResult);
445 return ruleCount;
448 void InspectorUtils::GetStyleSheetRuleCountAndAtRules(
449 GlobalObject& aGlobal, StyleSheet& aSheet,
450 InspectorStyleSheetRuleCountAndAtRulesResult& aResult) {
451 aResult.mRuleCount =
452 CollectAtRules(*aSheet.GetCssRulesInternal(), aResult.mAtRules);
455 /* static */
456 bool InspectorUtils::IsInheritedProperty(GlobalObject& aGlobalObject,
457 Document& aDocument,
458 const nsACString& aPropertyName) {
459 return Servo_Property_IsInherited(aDocument.EnsureStyleSet().RawData(),
460 &aPropertyName);
463 /* static */
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)));
479 uint32_t prop = 0;
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;
495 ++prop) {
496 appendProperty(prop);
501 /* static */
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();
507 dest.mName.Assign(
508 NS_ConvertASCIItoUTF16(nsCSSProps::GetStringValue(src->mPropID)));
509 dest.mPref.AssignASCII(src->mPref);
513 /* static */
514 void InspectorUtils::GetSubpropertiesForCSSProperty(GlobalObject& aGlobal,
515 const nsACString& aProperty,
516 nsTArray<nsString>& aResult,
517 ErrorResult& aRv) {
518 nsCSSPropertyID propertyID = nsCSSProps::LookupProperty(aProperty);
520 if (propertyID == eCSSProperty_UNKNOWN) {
521 aRv.Throw(NS_ERROR_FAILURE);
522 return;
525 if (propertyID == eCSSPropertyExtra_variable) {
526 aResult.AppendElement(NS_ConvertUTF8toUTF16(aProperty));
527 return;
530 if (!nsCSSProps::IsShorthand(propertyID)) {
531 nsString* name = aResult.AppendElement();
532 CopyASCIItoUTF16(nsCSSProps::GetStringValue(propertyID), *name);
533 return;
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);
544 /* static */
545 bool InspectorUtils::CssPropertyIsShorthand(GlobalObject& aGlobalObject,
546 const nsACString& aProperty,
547 ErrorResult& aRv) {
548 bool found;
549 bool isShorthand = Servo_Property_IsShorthand(&aProperty, &found);
550 if (!found) {
551 aRv.Throw(NS_ERROR_FAILURE);
553 return isShorthand;
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
559 // here instead.
560 static uint8_t ToServoCssType(InspectorPropertyType aType) {
561 switch (aType) {
562 case InspectorPropertyType::Color:
563 return 1;
564 case InspectorPropertyType::Gradient:
565 return 1 << 1;
566 case InspectorPropertyType::Timing_function:
567 return 1 << 2;
568 default:
569 MOZ_ASSERT_UNREACHABLE("Unknown property type?");
570 return 0;
574 bool InspectorUtils::Supports(GlobalObject&, const nsACString& aDeclaration,
575 const SupportsOptions& aOptions) {
576 return Servo_CSSSupports(&aDeclaration, aOptions.mUserAgent, aOptions.mChrome,
577 aOptions.mQuirks);
580 bool InspectorUtils::CssPropertySupportsType(GlobalObject& aGlobalObject,
581 const nsACString& aProperty,
582 InspectorPropertyType aType,
583 ErrorResult& aRv) {
584 bool found;
585 bool result =
586 Servo_Property_SupportsType(&aProperty, ToServoCssType(aType), &found);
587 if (!found) {
588 aRv.Throw(NS_ERROR_FAILURE);
589 return false;
591 return result;
594 /* static */
595 void InspectorUtils::GetCSSValuesForProperty(GlobalObject& aGlobalObject,
596 const nsACString& aProperty,
597 nsTArray<nsString>& aResult,
598 ErrorResult& aRv) {
599 bool found;
600 Servo_Property_GetCSSValuesForProperty(&aProperty, &found, &aResult);
601 if (!found) {
602 aRv.Throw(NS_ERROR_FAILURE);
606 /* static */
607 void InspectorUtils::RgbToColorName(GlobalObject&, uint8_t aR, uint8_t aG,
608 uint8_t aB, nsACString& aColorName) {
609 Servo_SlowRgbToColorName(aR, aG, aB, &aColorName);
612 /* static */
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;
619 if (aDoc) {
620 if (PresShell* ps = aDoc->GetPresShell()) {
621 styleSet = ps->StyleSet();
625 if (!ServoCSSParser::ComputeColor(styleSet, NS_RGB(0, 0, 0), aColorString,
626 &color)) {
627 aResult.SetNull();
628 return;
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));
638 /* static */
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)) {
648 aResult.SetNull();
649 return;
652 auto& result = aResult.SetValue();
653 result.mColor.AssignASCII(resultColor);
654 result.mComponents = std::move(resultComponents);
655 result.mAdjusted = resultAdjusted;
658 /* static */
659 bool InspectorUtils::IsValidCSSColor(GlobalObject& aGlobalObject,
660 const nsACString& aColorString) {
661 return ServoCSSParser::IsValidCSSColor(aColorString);
664 /* static */
665 bool InspectorUtils::SetContentState(GlobalObject& aGlobalObject,
666 Element& aElement, uint64_t aState,
667 ErrorResult& aRv) {
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);
673 return false;
675 return esm->SetContentState(&aElement, state);
678 /* static */
679 bool InspectorUtils::RemoveContentState(GlobalObject& aGlobalObject,
680 Element& aElement, uint64_t aState,
681 bool aClearActiveDocument,
682 ErrorResult& aRv) {
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);
688 return false;
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);
701 return result;
704 /* static */
705 uint64_t InspectorUtils::GetContentState(GlobalObject& aGlobalObject,
706 Element& aElement) {
707 // NOTE: if this method is removed,
708 // please remove GetInternalValue from ElementState
709 return aElement.State().GetInternalValue();
712 /* static */
713 void InspectorUtils::GetUsedFontFaces(GlobalObject& aGlobalObject,
714 nsRange& aRange, uint32_t aMaxRanges,
715 bool aSkipCollapsedWhitespace,
716 nsLayoutUtils::UsedFontFaceList& aResult,
717 ErrorResult& aRv) {
718 nsresult rv =
719 aRange.GetUsedFontFaces(aResult, aMaxRanges, aSkipCollapsedWhitespace);
721 if (NS_FAILED(rv)) {
722 aRv.Throw(rv);
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));
734 /* static */
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)) {
742 continue;
744 auto& string = *aResult.AppendElement();
745 // Use two semi-colons (though internally we use one).
746 string.Append(u':');
747 nsAtom* atom = nsCSSPseudoElements::GetPseudoAtom(type);
748 string.Append(nsDependentAtomString(atom));
752 /* static */
753 void InspectorUtils::AddPseudoClassLock(GlobalObject& aGlobalObject,
754 Element& aElement,
755 const nsAString& aPseudoClass,
756 bool aEnabled) {
757 ElementState state = GetStatesForPseudoClass(aPseudoClass);
758 if (state.IsEmpty()) {
759 return;
762 aElement.LockStyleStates(state, aEnabled);
765 /* static */
766 void InspectorUtils::RemovePseudoClassLock(GlobalObject& aGlobal,
767 Element& aElement,
768 const nsAString& aPseudoClass) {
769 ElementState state = GetStatesForPseudoClass(aPseudoClass);
770 if (state.IsEmpty()) {
771 return;
774 aElement.UnlockStyleStates(state);
777 /* static */
778 bool InspectorUtils::HasPseudoClassLock(GlobalObject& aGlobalObject,
779 Element& aElement,
780 const nsAString& aPseudoClass) {
781 ElementState state = GetStatesForPseudoClass(aPseudoClass);
782 if (state.IsEmpty()) {
783 return false;
786 ElementState locks = aElement.LockedStyleStates().mLocks;
787 return locks.HasAllStates(state);
790 /* static */
791 void InspectorUtils::ClearPseudoClassLocks(GlobalObject& aGlobalObject,
792 Element& aElement) {
793 aElement.ClearStyleStateLocks();
796 /* static */
797 void InspectorUtils::ParseStyleSheet(GlobalObject& aGlobalObject,
798 StyleSheet& aSheet,
799 const nsACString& aInput,
800 ErrorResult& aRv) {
801 aSheet.ReparseSheet(aInput, aRv);
804 bool InspectorUtils::IsCustomElementName(GlobalObject&, const nsAString& aName,
805 const nsAString& aNamespaceURI) {
806 if (aName.IsEmpty()) {
807 return false;
810 int32_t namespaceID;
811 nsNameSpaceManager::GetInstance()->RegisterNameSpace(aNamespaceURI,
812 namespaceID);
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);
830 if (!frame) {
831 return nullptr;
833 nsIFrame* cb = frame->GetContainingBlock();
834 if (!cb) {
835 return nullptr;
837 return Element::FromNodeOrNull(cb->GetContent());
840 void InspectorUtils::GetBlockLineCounts(GlobalObject& aGlobal,
841 Element& aElement,
842 Nullable<nsTArray<uint32_t>>& aResult) {
843 nsBlockFrame* block =
844 do_QueryFrame(aElement.GetPrimaryFrame(FlushType::Layout));
845 if (!block) {
846 aResult.SetNull();
847 return;
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()) {
856 aResult.SetNull();
857 return;
859 block = do_QueryFrame(firstChild->PrincipalChildList().FirstChild());
860 if (!block || block->GetContent() != &aElement) {
861 aResult.SetNull();
862 return;
866 nsTArray<uint32_t> result;
867 do {
868 result.AppendElement(block->Lines().size());
869 block = static_cast<nsBlockFrame*>(block->GetNextInFlow());
870 } while (block);
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,
906 const nsRect& aRect,
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
922 // to aList.
924 if (FrameHasSpecifiedSize(child) &&
925 IsFrameOutsideOfAncestor(child, aAncestorFrame, aRect)) {
926 aList.MaybeAppendElement(child->GetContent());
927 continue;
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
948 if (!scrollFrame) {
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,
956 *list);
957 return list.forget();
960 /* static */
961 void InspectorUtils::GetRegisteredCssHighlights(GlobalObject& aGlobalObject,
962 Document& aDocument,
963 bool aActiveOnly,
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());
974 /* static */
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);
996 } else {
997 property.mInitialValue.SetIsVoid(true);
999 property.mFromJS = propDef.from_js;
1003 /* static */
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)) {
1012 aResult.SetNull();
1013 return;
1016 InspectorGetRuleBodyTextResult& offsets = aResult.SetValue();
1017 offsets.mStartOffset = resultStartOffset;
1018 offsets.mEndOffset = resultEndOffset;
1021 } // namespace dom
1022 } // namespace mozilla