mirror of
https://github.com/mozilla-firefox/firefox
synced 2026-08-10 11:48:50 +00:00
There are probably more passes. Differential Revision: https://phabricator.services.mozilla.com/D310910
1474 lines
51 KiB
C++
1474 lines
51 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "mozilla/dom/InspectorUtils.h"
|
|
|
|
#include "AnchorPositioningUtils.h"
|
|
#include "ChildIterator.h"
|
|
#include "Units.h"
|
|
#include "gfxTextRun.h"
|
|
#include "inLayoutUtils.h"
|
|
#include "mozilla/DeclarationBlock.h"
|
|
#include "mozilla/EventStateManager.h"
|
|
#include "mozilla/PresShell.h"
|
|
#include "mozilla/PresShellInlines.h"
|
|
#include "mozilla/RefPtr.h"
|
|
#include "mozilla/RelativeLuminanceUtils.h"
|
|
#include "mozilla/ScrollContainerFrame.h"
|
|
#include "mozilla/ServoBindings.h"
|
|
#include "mozilla/ServoCSSParser.h"
|
|
#include "mozilla/ServoStyleRuleMap.h"
|
|
#include "mozilla/ServoStyleSet.h"
|
|
#include "mozilla/StaticPrefs_layout.h"
|
|
#include "mozilla/StyleSheetInlines.h"
|
|
#include "mozilla/dom/BrowserParent.h"
|
|
#include "mozilla/dom/CSSBinding.h"
|
|
#include "mozilla/dom/CSSKeyframesRule.h"
|
|
#include "mozilla/dom/CSSStylePropertiesBinding.h"
|
|
#include "mozilla/dom/CSSStyleRule.h"
|
|
#include "mozilla/dom/CanonicalBrowsingContext.h"
|
|
#include "mozilla/dom/CharacterData.h"
|
|
#include "mozilla/dom/ContentList.h"
|
|
#include "mozilla/dom/Document.h"
|
|
#include "mozilla/dom/DocumentInlines.h"
|
|
#include "mozilla/dom/Element.h"
|
|
#include "mozilla/dom/HTMLSlotElement.h"
|
|
#include "mozilla/dom/HTMLTemplateElement.h"
|
|
#include "mozilla/dom/Highlight.h"
|
|
#include "mozilla/dom/HighlightRegistry.h"
|
|
#include "mozilla/dom/InspectorFontFace.h"
|
|
#include "mozilla/dom/InspectorUtilsBinding.h"
|
|
#include "mozilla/dom/LinkStyle.h"
|
|
#include "mozilla/dom/ToJSValue.h"
|
|
#include "mozilla/gfx/Matrix.h"
|
|
#include "nsArray.h"
|
|
#include "nsAtom.h"
|
|
#include "nsBlockFrame.h"
|
|
#include "nsCSSProps.h"
|
|
#include "nsCSSValue.h"
|
|
#include "nsColor.h"
|
|
#include "nsComputedDOMStyle.h"
|
|
#include "nsFieldSetFrame.h"
|
|
#include "nsGlobalWindowInner.h"
|
|
#include "nsGridContainerFrame.h"
|
|
#include "nsIContentInlines.h"
|
|
#include "nsIFrameInlines.h"
|
|
#include "nsLayoutUtils.h"
|
|
#include "nsNameSpaceManager.h"
|
|
#include "nsPresContext.h"
|
|
#include "nsQueryObject.h"
|
|
#include "nsRange.h"
|
|
#include "nsString.h"
|
|
#include "nsStyleUtil.h"
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::css;
|
|
using namespace mozilla::dom;
|
|
|
|
namespace mozilla::dom {
|
|
|
|
static nsPresContext* EnsureSafeToHandOutRules(Element& aElement) {
|
|
Document* doc = aElement.GetComposedDoc();
|
|
if (!doc) {
|
|
return nullptr;
|
|
}
|
|
const PresShell* presShell = doc->GetPresShell();
|
|
if (!presShell) {
|
|
return nullptr;
|
|
}
|
|
nsPresContext* presContext = presShell->GetPresContext();
|
|
if (!presContext) {
|
|
return nullptr;
|
|
}
|
|
presContext->EnsureSafeToHandOutCSSRules();
|
|
return presContext;
|
|
}
|
|
|
|
static already_AddRefed<const ComputedStyle> GetCleanComputedStyleForElement(
|
|
dom::Element* aElement, const PseudoStyleRequest& aPseudo) {
|
|
MOZ_ASSERT(aElement);
|
|
nsPresContext* pc = EnsureSafeToHandOutRules(*aElement);
|
|
if (!pc) {
|
|
return nullptr;
|
|
}
|
|
return nsComputedDOMStyle::GetComputedStyle(aElement, aPseudo);
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::GetAllStyleSheets(GlobalObject& aGlobalObject,
|
|
Document& aDocument, bool aDocumentOnly,
|
|
nsTArray<RefPtr<StyleSheet>>& aResult) {
|
|
// Get the agent, then user and finally xbl sheets in the style set.
|
|
PresShell* presShell = aDocument.GetPresShell();
|
|
nsTHashSet<StyleSheet*> sheetSet;
|
|
|
|
if (presShell) {
|
|
ServoStyleSet* styleSet = presShell->StyleSet();
|
|
|
|
if (!aDocumentOnly) {
|
|
const StyleOrigin kOrigins[] = {StyleOrigin::UserAgent,
|
|
StyleOrigin::User};
|
|
for (const auto origin : kOrigins) {
|
|
for (size_t i = 0, count = styleSet->SheetCount(origin); i < count;
|
|
i++) {
|
|
aResult.AppendElement(styleSet->SheetAt(origin, i));
|
|
}
|
|
}
|
|
}
|
|
|
|
AutoTArray<StyleSheet*, 32> nonDocumentSheets;
|
|
styleSet->AppendAllNonDocumentAuthorSheets(nonDocumentSheets);
|
|
|
|
// The non-document stylesheet array can have duplicates due to adopted
|
|
// stylesheets.
|
|
nsTHashSet<StyleSheet*> sheetSet;
|
|
for (StyleSheet* sheet : nonDocumentSheets) {
|
|
if (sheetSet.EnsureInserted(sheet)) {
|
|
aResult.AppendElement(sheet);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get the document sheets.
|
|
for (size_t i = 0; i < aDocument.SheetCount(); i++) {
|
|
aResult.AppendElement(aDocument.SheetAt(i));
|
|
}
|
|
|
|
for (auto& sheet : aDocument.AdoptedStyleSheets()) {
|
|
if (sheetSet.EnsureInserted(sheet)) {
|
|
aResult.AppendElement(sheet);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool InspectorUtils::IsIgnorableWhitespace(CharacterData& aDataNode) {
|
|
if (!aDataNode.TextIsOnlyWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
// Okay. We have only white space. Let's check the white-space
|
|
// property now and make sure that this isn't preformatted text...
|
|
if (nsIFrame* frame = aDataNode.GetPrimaryFrame()) {
|
|
return !frame->StyleText()->WhiteSpaceIsSignificant();
|
|
}
|
|
|
|
// empty inter-tag text node without frame, e.g., in between <table>\n<tr>
|
|
return true;
|
|
}
|
|
|
|
/* static */
|
|
nsINode* InspectorUtils::GetParentForNode(nsINode& aNode,
|
|
bool aShowingAnonymousContent) {
|
|
if (nsINode* parent = aNode.GetParentNode()) {
|
|
return parent;
|
|
}
|
|
if (aNode.IsDocument()) {
|
|
return inLayoutUtils::GetContainerFor(*aNode.AsDocument());
|
|
}
|
|
if (aShowingAnonymousContent) {
|
|
if (auto* frag = DocumentFragment::FromNode(aNode)) {
|
|
// This deals with shadow roots and HTMLTemplateElement.content.
|
|
return frag->GetHost();
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::GetChildrenForNode(nsINode& aNode,
|
|
bool aShowingAnonymousContent,
|
|
bool aIncludeAssignedNodes,
|
|
bool aIncludeSubdocuments,
|
|
nsTArray<RefPtr<nsINode>>& aResult) {
|
|
if (aIncludeSubdocuments) {
|
|
if (auto* doc = inLayoutUtils::GetSubDocumentFor(&aNode)) {
|
|
aResult.AppendElement(doc);
|
|
// XXX Do we really want to early-return?
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!aShowingAnonymousContent || !aNode.IsContent()) {
|
|
for (nsINode* child = aNode.GetFirstChild(); child;
|
|
child = child->GetNextSibling()) {
|
|
aResult.AppendElement(child);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (auto* tmpl = HTMLTemplateElement::FromNode(aNode)) {
|
|
aResult.AppendElement(tmpl->Content());
|
|
// XXX Do we really want to early-return?
|
|
return;
|
|
}
|
|
|
|
if (auto* element = Element::FromNode(aNode)) {
|
|
if (auto* shadow = element->GetShadowRoot()) {
|
|
aResult.AppendElement(shadow);
|
|
}
|
|
}
|
|
nsIContent* parent = aNode.AsContent();
|
|
if (auto* node = nsLayoutUtils::GetBackdropPseudo(parent)) {
|
|
aResult.AppendElement(node);
|
|
}
|
|
if (auto* node = nsLayoutUtils::GetMarkerPseudo(parent)) {
|
|
aResult.AppendElement(node);
|
|
}
|
|
if (auto* node = nsLayoutUtils::GetCheckmarkPseudo(parent)) {
|
|
aResult.AppendElement(node);
|
|
}
|
|
if (auto* node = nsLayoutUtils::GetBeforePseudo(parent)) {
|
|
aResult.AppendElement(node);
|
|
}
|
|
if (aIncludeAssignedNodes) {
|
|
if (auto* slot = HTMLSlotElement::FromNode(aNode)) {
|
|
for (nsINode* node : slot->AssignedNodes()) {
|
|
aResult.AppendElement(node);
|
|
}
|
|
}
|
|
}
|
|
for (nsIContent* node = parent->GetFirstChild(); node;
|
|
node = node->GetNextSibling()) {
|
|
aResult.AppendElement(node);
|
|
}
|
|
AutoTArray<nsIContent*, 4> anonKids;
|
|
nsContentUtils::AppendNativeAnonymousChildren(parent, anonKids,
|
|
nsIContent::eAllChildren);
|
|
for (nsIContent* node : anonKids) {
|
|
aResult.AppendElement(node);
|
|
}
|
|
if (auto* node = nsLayoutUtils::GetAfterPseudo(parent)) {
|
|
aResult.AppendElement(node);
|
|
}
|
|
|
|
if (auto* node = nsLayoutUtils::GetPickerIconPseudo(parent)) {
|
|
aResult.AppendElement(node);
|
|
}
|
|
}
|
|
|
|
class ReadOnlyInspectorDeclaration final : public nsDOMCSSDeclaration {
|
|
public:
|
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
|
NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(ReadOnlyInspectorDeclaration)
|
|
|
|
explicit ReadOnlyInspectorDeclaration(const StyleLockedDeclarationBlock* aRaw)
|
|
: mRaw(aRaw) {}
|
|
|
|
nsINode* GetAssociatedNode() const final { return nullptr; }
|
|
nsISupports* GetParentObject() const final { return nullptr; }
|
|
void GetPropertyValue(const nsACString& aPropName, nsACString& aValue) final {
|
|
Servo_DeclarationBlock_GetPropertyValue(mRaw, &aPropName, &aValue);
|
|
}
|
|
void GetPropertyValue(NonCustomCSSPropertyId aId, nsACString& aValue) final {
|
|
Servo_DeclarationBlock_GetPropertyValueByNonCustomId(mRaw, aId, &aValue);
|
|
}
|
|
bool HasLonghandProperty(const nsACString& aPropName) final {
|
|
return Servo_DeclarationBlock_HasLonghandProperty(mRaw, &aPropName);
|
|
}
|
|
void IndexedGetter(uint32_t aIndex, bool& aFound,
|
|
nsACString& aPropName) final {
|
|
CSSPropertyId id{eCSSProperty_UNKNOWN};
|
|
if ((aFound = Servo_DeclarationBlock_GetAt(mRaw, aIndex, &id))) {
|
|
id.ToString(aPropName);
|
|
}
|
|
}
|
|
void RemoveProperty(const nsACString& aPropertyName, nsACString& aValue,
|
|
ErrorResult& aRv) final {
|
|
aRv.ThrowInvalidModificationError("Can't mutate this declaration");
|
|
}
|
|
void SetProperty(const nsACString& aPropertyName, const nsACString& aValue,
|
|
const nsACString& aPriority, nsIPrincipal* aSubjectPrincipal,
|
|
ErrorResult& aRv) final {
|
|
aRv.ThrowInvalidModificationError("Can't mutate this declaration");
|
|
}
|
|
void SetPropertyValue(NonCustomCSSPropertyId aId, const nsACString& aValue,
|
|
nsIPrincipal* aSubjectPrincipal,
|
|
ErrorResult& aRv) final {
|
|
aRv.ThrowInvalidModificationError("Can't mutate this declaration");
|
|
}
|
|
void SetCssText(const nsACString& aString, nsIPrincipal* aSubjectPrincipal,
|
|
ErrorResult& aRv) final {
|
|
aRv.ThrowInvalidModificationError("Can't mutate this declaration");
|
|
}
|
|
void GetCssText(nsACString& aString) final {
|
|
Servo_DeclarationBlock_GetCssText(mRaw, &aString);
|
|
}
|
|
uint32_t Length() final { return Servo_DeclarationBlock_Count(mRaw); }
|
|
void GetPropertyPriority(const nsACString& aPropName,
|
|
nsACString& aPriority) final {
|
|
if (Servo_DeclarationBlock_GetPropertyIsImportant(mRaw, &aPropName)) {
|
|
aPriority.AssignLiteral("important");
|
|
}
|
|
}
|
|
css::Rule* GetParentRule() final { return nullptr; }
|
|
JSObject* WrapObject(JSContext* aCx,
|
|
JS::Handle<JSObject*> aGivenProto) final {
|
|
return CSSStyleProperties_Binding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
// These ones are a bit sad, but matches e.g. nsComputedDOMStyle.
|
|
nsresult SetCSSDeclaration(Block* aDecl, MutationClosureData*) final {
|
|
MOZ_CRASH("called ReadOnlyInspectorDeclaration::SetCSSDeclaration");
|
|
}
|
|
Block* GetOrCreateCSSDeclaration(Operation, Block**) override {
|
|
MOZ_CRASH("called ReadOnlyInspectorDeclaration::GetOrCreateCSSDeclaration");
|
|
}
|
|
ParsingEnvironment GetParsingEnvironment(nsIPrincipal*) const final {
|
|
MOZ_CRASH("called ReadOnlyInspectorDeclaration::GetParsingEnvironment");
|
|
}
|
|
|
|
private:
|
|
~ReadOnlyInspectorDeclaration() = default;
|
|
|
|
RefPtr<const StyleLockedDeclarationBlock> mRaw;
|
|
};
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadOnlyInspectorDeclaration)
|
|
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
|
NS_INTERFACE_MAP_ENTRY(nsICSSDeclaration)
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(ReadOnlyInspectorDeclaration)
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(ReadOnlyInspectorDeclaration)
|
|
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(ReadOnlyInspectorDeclaration)
|
|
|
|
static void GetCSSRulesFromComputedValues(
|
|
Element& aElement, const ComputedStyle* aComputedStyle,
|
|
bool aWithStartingStyle,
|
|
nsTArray<OwningCSSRuleOrInspectorDeclaration>& aResult) {
|
|
const PresShell* presShell = aElement.OwnerDoc()->GetPresShell();
|
|
if (!presShell) {
|
|
return;
|
|
}
|
|
|
|
AutoTArray<StyleMatchingDeclarationBlock, 8> rawDecls;
|
|
Servo_ComputedValues_GetMatchingDeclarations(aComputedStyle,
|
|
aWithStartingStyle, &rawDecls);
|
|
|
|
AutoTArray<ServoStyleRuleMap*, 8> maps;
|
|
{
|
|
ServoStyleSet* styleSet = presShell->StyleSet();
|
|
ServoStyleRuleMap* map = styleSet->StyleRuleMap();
|
|
maps.AppendElement(map);
|
|
}
|
|
|
|
// Now shadow DOM stuff...
|
|
if (auto* shadow = aElement.GetShadowRoot()) {
|
|
maps.AppendElement(&shadow->ServoStyleRuleMap());
|
|
}
|
|
|
|
// Now NAC:
|
|
for (auto* el = aElement.GetClosestNativeAnonymousSubtreeRootParentOrHost();
|
|
el; el = el->GetClosestNativeAnonymousSubtreeRootParentOrHost()) {
|
|
if (auto* shadow = el->GetShadowRoot()) {
|
|
maps.AppendElement(&shadow->ServoStyleRuleMap());
|
|
}
|
|
}
|
|
|
|
for (auto* shadow = aElement.GetContainingShadow(); shadow;
|
|
shadow = shadow->Host()->GetContainingShadow()) {
|
|
maps.AppendElement(&shadow->ServoStyleRuleMap());
|
|
}
|
|
|
|
// Rules from the assigned slot.
|
|
for (auto* slot = aElement.GetAssignedSlot(); slot;
|
|
slot = slot->GetAssignedSlot()) {
|
|
if (auto* shadow = slot->GetContainingShadow()) {
|
|
maps.AppendElement(&shadow->ServoStyleRuleMap());
|
|
}
|
|
}
|
|
|
|
// Find matching rules in the table.
|
|
for (const StyleMatchingDeclarationBlock& block : Reversed(rawDecls)) {
|
|
bool found = false;
|
|
for (ServoStyleRuleMap* map : maps) {
|
|
if (css::Rule* rule = map->Lookup(block.block)) {
|
|
aResult.AppendElement()->SetAsCSSRule() = rule;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
auto& declaration = aResult.AppendElement()->SetAsInspectorDeclaration();
|
|
declaration.mStyle = OwningNonNull<ReadOnlyInspectorDeclaration>(
|
|
*new ReadOnlyInspectorDeclaration(block.block));
|
|
declaration.mDeclarationOrigin = [&] {
|
|
switch (block.origin) {
|
|
case StyleMatchingDeclarationBlockOrigin::Author:
|
|
return DeclarationOrigin::Style_attribute;
|
|
case StyleMatchingDeclarationBlockOrigin::User:
|
|
MOZ_ASSERT_UNREACHABLE(
|
|
"Where did this user agent declaration come from?");
|
|
return DeclarationOrigin::User;
|
|
case StyleMatchingDeclarationBlockOrigin::UserAgent:
|
|
return DeclarationOrigin::User_agent;
|
|
case StyleMatchingDeclarationBlockOrigin::PositionFallback:
|
|
return DeclarationOrigin::Position_fallback;
|
|
case StyleMatchingDeclarationBlockOrigin::Animations:
|
|
return DeclarationOrigin::Animations;
|
|
case StyleMatchingDeclarationBlockOrigin::Transitions:
|
|
return DeclarationOrigin::Transitions;
|
|
case StyleMatchingDeclarationBlockOrigin::SMIL:
|
|
return DeclarationOrigin::Smil;
|
|
case StyleMatchingDeclarationBlockOrigin::PresHints:
|
|
return DeclarationOrigin::Pres_hints;
|
|
}
|
|
MOZ_ASSERT_UNREACHABLE("Unkown origin?");
|
|
return DeclarationOrigin::Pres_hints;
|
|
}();
|
|
}
|
|
}
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::GetMatchingCSSRules(
|
|
GlobalObject& aGlobalObject, Element& aElement, const nsAString& aPseudo,
|
|
bool aIncludeVisitedStyle, bool aWithStartingStyle,
|
|
nsTArray<OwningCSSRuleOrInspectorDeclaration>& aResult) {
|
|
auto pseudo = PseudoStyleRequest::Parse(
|
|
aPseudo, aElement.OwnerDoc()->DefaultStyleAttrURLData());
|
|
if (!pseudo) {
|
|
return;
|
|
}
|
|
|
|
RefPtr<const ComputedStyle> computedStyle =
|
|
GetCleanComputedStyleForElement(&aElement, *pseudo);
|
|
if (!computedStyle) {
|
|
// This can fail for elements that are not in the document or
|
|
// if the document they're in doesn't have a presshell. Bail out.
|
|
return;
|
|
}
|
|
|
|
if (aIncludeVisitedStyle) {
|
|
if (const auto* styleIfVisited = computedStyle->GetStyleIfVisited()) {
|
|
computedStyle = styleIfVisited;
|
|
}
|
|
}
|
|
|
|
GetCSSRulesFromComputedValues(aElement, computedStyle, aWithStartingStyle,
|
|
aResult);
|
|
}
|
|
|
|
/* static */
|
|
uint32_t InspectorUtils::GetRuleLine(GlobalObject& aGlobal, css::Rule& aRule) {
|
|
uint32_t line = aRule.GetLineNumber();
|
|
if (StyleSheet* sheet = aRule.GetStyleSheet()) {
|
|
if (auto* link = LinkStyle::FromNodeOrNull(sheet->GetOwnerNode())) {
|
|
line += link->GetLineNumber();
|
|
}
|
|
}
|
|
return line;
|
|
}
|
|
|
|
/* static */
|
|
uint32_t InspectorUtils::GetRuleColumn(GlobalObject& aGlobal,
|
|
css::Rule& aRule) {
|
|
return aRule.GetColumnNumber();
|
|
}
|
|
|
|
/* static */
|
|
uint32_t InspectorUtils::GetRelativeRuleLine(GlobalObject& aGlobal,
|
|
css::Rule& aRule) {
|
|
// Rule lines are 0-based, but inspector wants 1-based.
|
|
return aRule.GetLineNumber() + 1;
|
|
}
|
|
|
|
void InspectorUtils::GetRuleIndex(GlobalObject& aGlobal, css::Rule& aRule,
|
|
nsTArray<uint32_t>& aResult) {
|
|
css::Rule* currentRule = &aRule;
|
|
|
|
do {
|
|
css::Rule* parentRule = currentRule->GetParentRule();
|
|
dom::CSSRuleList* ruleList = nullptr;
|
|
|
|
if (parentRule) {
|
|
if (parentRule->IsGroupRule()) {
|
|
ruleList = static_cast<css::GroupRule*>(parentRule)->CssRules();
|
|
} else if (parentRule->Type() == StyleCssRuleType::Keyframes) {
|
|
ruleList = static_cast<CSSKeyframesRule*>(parentRule)->CssRules();
|
|
} else {
|
|
MOZ_ASSERT_UNREACHABLE("Unknown parent rule type?");
|
|
}
|
|
} else if (StyleSheet* sheet = currentRule->GetStyleSheet()) {
|
|
ruleList = sheet->GetCssRulesInternal();
|
|
}
|
|
|
|
if (!ruleList) {
|
|
return;
|
|
}
|
|
|
|
bool found = false;
|
|
for (uint32_t i = 0, len = ruleList->Length(); i < len; ++i) {
|
|
css::Rule* rule = ruleList->Item(i);
|
|
if (currentRule == rule) {
|
|
found = true;
|
|
aResult.InsertElementAt(0, i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
return;
|
|
}
|
|
|
|
currentRule = parentRule;
|
|
} while (currentRule);
|
|
}
|
|
|
|
/* static */
|
|
bool InspectorUtils::HasRulesModifiedByCSSOM(GlobalObject& aGlobal,
|
|
StyleSheet& aSheet) {
|
|
return aSheet.HasModifiedRulesForDevtools();
|
|
}
|
|
|
|
static uint32_t CollectAtRules(ServoCSSRuleList& aRuleList,
|
|
Sequence<OwningNonNull<css::Rule>>& aResult) {
|
|
uint32_t len = aRuleList.Length();
|
|
uint32_t ruleCount = len;
|
|
for (uint32_t i = 0; i < len; ++i) {
|
|
css::Rule* rule = aRuleList.GetRule(i);
|
|
// This collect rules we want to display in Devtools Style Editor toolbar.
|
|
// When adding a new StyleCssRuleType, put it in the "default" list, and
|
|
// file a new bug with
|
|
// https://bugzilla.mozilla.org/enter_bug.cgi?product=DevTools&component=Style%20Editor&short_desc=Consider%20displaying%20new%20XXX%20rule%20type%20in%20at-rules%20sidebar
|
|
// so the DevTools team gets notified and can decide if it should be
|
|
// displayed.
|
|
switch (rule->Type()) {
|
|
case StyleCssRuleType::CustomMedia:
|
|
case StyleCssRuleType::Media:
|
|
case StyleCssRuleType::Supports:
|
|
case StyleCssRuleType::LayerBlock:
|
|
case StyleCssRuleType::PositionTry:
|
|
case StyleCssRuleType::Property:
|
|
case StyleCssRuleType::Container: {
|
|
(void)aResult.AppendElement(OwningNonNull(*rule), fallible);
|
|
break;
|
|
}
|
|
case StyleCssRuleType::Style:
|
|
case StyleCssRuleType::Import:
|
|
case StyleCssRuleType::Document:
|
|
case StyleCssRuleType::LayerStatement:
|
|
case StyleCssRuleType::FontFace:
|
|
case StyleCssRuleType::Page:
|
|
case StyleCssRuleType::Keyframes:
|
|
case StyleCssRuleType::Keyframe:
|
|
case StyleCssRuleType::Margin:
|
|
case StyleCssRuleType::Namespace:
|
|
case StyleCssRuleType::CounterStyle:
|
|
case StyleCssRuleType::FontFeatureValues:
|
|
case StyleCssRuleType::FontPaletteValues:
|
|
case StyleCssRuleType::Scope:
|
|
case StyleCssRuleType::StartingStyle:
|
|
case StyleCssRuleType::NestedDeclarations:
|
|
case StyleCssRuleType::AppearanceBase:
|
|
case StyleCssRuleType::ViewTransition:
|
|
break;
|
|
}
|
|
|
|
if (rule->IsGroupRule()) {
|
|
ruleCount += CollectAtRules(
|
|
*static_cast<css::GroupRule*>(rule)->CssRules(), aResult);
|
|
}
|
|
}
|
|
return ruleCount;
|
|
}
|
|
|
|
void InspectorUtils::GetStyleSheetRuleCountAndAtRules(
|
|
GlobalObject& aGlobal, StyleSheet& aSheet,
|
|
InspectorStyleSheetRuleCountAndAtRulesResult& aResult) {
|
|
aResult.mRuleCount =
|
|
CollectAtRules(*aSheet.GetCssRulesInternal(), aResult.mAtRules);
|
|
}
|
|
|
|
/* static */
|
|
bool InspectorUtils::IsInheritedProperty(GlobalObject& aGlobalObject,
|
|
Document& aDocument,
|
|
const nsACString& aPropertyName) {
|
|
return Servo_Property_IsInherited(aDocument.EnsureStyleSet().RawData(),
|
|
&aPropertyName);
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::GetCSSPropertyNames(GlobalObject& aGlobalObject,
|
|
const PropertyNamesOptions& aOptions,
|
|
nsTArray<nsString>& aResult) {
|
|
CSSEnabledState enabledState = aOptions.mIncludeExperimentals
|
|
? CSSEnabledState::IgnoreEnabledState
|
|
: CSSEnabledState::ForAllContent;
|
|
|
|
auto appendProperty = [enabledState, &aResult](uint32_t prop) {
|
|
NonCustomCSSPropertyId cssProp = NonCustomCSSPropertyId(prop);
|
|
if (nsCSSProps::IsEnabled(cssProp, enabledState)) {
|
|
aResult.AppendElement(
|
|
NS_ConvertASCIItoUTF16(nsCSSProps::GetStringValue(cssProp)));
|
|
}
|
|
};
|
|
|
|
uint32_t prop = 0;
|
|
for (; prop < eCSSProperty_COUNT_no_shorthands; ++prop) {
|
|
if (!nsCSSProps::PropHasFlags(NonCustomCSSPropertyId(prop),
|
|
CSSPropFlags::Inaccessible)) {
|
|
appendProperty(prop);
|
|
}
|
|
}
|
|
|
|
if (aOptions.mIncludeShorthands) {
|
|
for (; prop < eCSSProperty_COUNT; ++prop) {
|
|
appendProperty(prop);
|
|
}
|
|
}
|
|
|
|
if (aOptions.mIncludeAliases) {
|
|
for (prop = eCSSProperty_COUNT; prop < eCSSProperty_COUNT_with_aliases;
|
|
++prop) {
|
|
appendProperty(prop);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::GetCSSPropertyPrefs(GlobalObject& aGlobalObject,
|
|
nsTArray<PropertyPref>& aResult) {
|
|
for (const auto* src = nsCSSProps::kPropertyPrefTable;
|
|
src->mPropId != eCSSProperty_UNKNOWN; src++) {
|
|
PropertyPref& dest = *aResult.AppendElement();
|
|
dest.mName.Assign(
|
|
NS_ConvertASCIItoUTF16(nsCSSProps::GetStringValue(src->mPropId)));
|
|
dest.mPref.AssignASCII(src->mPref);
|
|
}
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::GetSubpropertiesForCSSProperty(GlobalObject& aGlobal,
|
|
const nsACString& aProperty,
|
|
nsTArray<nsString>& aResult,
|
|
ErrorResult& aRv) {
|
|
NonCustomCSSPropertyId propertyId = nsCSSProps::LookupProperty(aProperty);
|
|
|
|
if (propertyId == eCSSProperty_UNKNOWN) {
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
if (propertyId == eCSSPropertyExtra_variable) {
|
|
aResult.AppendElement(NS_ConvertUTF8toUTF16(aProperty));
|
|
return;
|
|
}
|
|
|
|
if (!nsCSSProps::IsShorthand(propertyId)) {
|
|
nsString* name = aResult.AppendElement();
|
|
CopyASCIItoUTF16(nsCSSProps::GetStringValue(propertyId), *name);
|
|
return;
|
|
}
|
|
|
|
for (const NonCustomCSSPropertyId* props =
|
|
nsCSSProps::SubpropertyEntryFor(propertyId);
|
|
*props != eCSSProperty_UNKNOWN; ++props) {
|
|
nsString* name = aResult.AppendElement();
|
|
CopyASCIItoUTF16(nsCSSProps::GetStringValue(*props), *name);
|
|
}
|
|
}
|
|
|
|
/* static */
|
|
bool InspectorUtils::CssPropertyIsShorthand(GlobalObject& aGlobalObject,
|
|
const nsACString& aProperty,
|
|
ErrorResult& aRv) {
|
|
bool found;
|
|
bool isShorthand = Servo_Property_IsShorthand(&aProperty, &found);
|
|
if (!found) {
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
}
|
|
return isShorthand;
|
|
}
|
|
|
|
// This should match the constants in specified_value_info.rs
|
|
//
|
|
// Once we can use bitflags in consts, we can also cbindgen that and use them
|
|
// here instead.
|
|
static uint8_t ToServoCssType(InspectorPropertyType aType) {
|
|
switch (aType) {
|
|
case InspectorPropertyType::Color:
|
|
return 1;
|
|
case InspectorPropertyType::Gradient:
|
|
return 1 << 1;
|
|
case InspectorPropertyType::Timing_function:
|
|
return 1 << 2;
|
|
default:
|
|
MOZ_ASSERT_UNREACHABLE("Unknown property type?");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
bool InspectorUtils::Supports(GlobalObject&, const nsACString& aDeclaration,
|
|
const SupportsOptions& aOptions) {
|
|
StyleCssSupportsParams params{
|
|
.origin =
|
|
aOptions.mUserAgent ? StyleOrigin::UserAgent : StyleOrigin::Author,
|
|
.url_context = aOptions.mChrome ? StyleCssSupportsUrlContext::Chrome
|
|
: StyleCssSupportsUrlContext::Default,
|
|
.quirks = aOptions.mQuirks
|
|
? nsCompatibility::eCompatibility_NavQuirks
|
|
: nsCompatibility::eCompatibility_FullStandards,
|
|
};
|
|
return Servo_CSSSupports(&aDeclaration, ¶ms,
|
|
/* aUrlData = */ nullptr);
|
|
}
|
|
|
|
bool InspectorUtils::CssPropertySupportsType(GlobalObject& aGlobalObject,
|
|
const nsACString& aProperty,
|
|
InspectorPropertyType aType,
|
|
ErrorResult& aRv) {
|
|
bool found;
|
|
bool result =
|
|
Servo_Property_SupportsType(&aProperty, ToServoCssType(aType), &found);
|
|
if (!found) {
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
return false;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::GetCSSValuesForProperty(GlobalObject& aGlobalObject,
|
|
const nsACString& aProperty,
|
|
nsTArray<nsString>& aResult,
|
|
ErrorResult& aRv) {
|
|
bool found;
|
|
Servo_Property_GetCSSValuesForProperty(&aProperty, &found, &aResult);
|
|
if (!found) {
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
}
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::GetCSSWideKeywords(GlobalObject& aGlobalObject,
|
|
nsTArray<nsString>& aResult) {
|
|
Servo_Property_GetCSSWideKeywords(&aResult);
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::RgbToColorName(GlobalObject&, uint8_t aR, uint8_t aG,
|
|
uint8_t aB, nsACString& aColorName) {
|
|
Servo_SlowRgbToColorName(aR, aG, aB, &aColorName);
|
|
}
|
|
|
|
void InspectorUtils::RgbToNearestColorName(GlobalObject&, float aR, float aG,
|
|
float aB,
|
|
InspectorNearestColor& aResult) {
|
|
bool exact = Servo_SlowRgbToNearestColorName(
|
|
aR, aG, aB, StyleColorSpace::Srgb, &aResult.mColorName);
|
|
aResult.mExact = exact;
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::RgbToHsv(GlobalObject&, float aR, float aG, float aB,
|
|
nsTArray<float>& aResult) {
|
|
StyleAbsoluteColor input{
|
|
.components = StyleColorComponents{aR, aG, aB},
|
|
.alpha = 1.0,
|
|
.color_space = StyleColorSpace::Srgb,
|
|
};
|
|
StyleAbsoluteColor result = input.ToColorSpace(StyleColorSpace::Hwb);
|
|
float h = result.components._0 / 360.0f;
|
|
float v = 1 - result.components._2 / 100.0f;
|
|
float s = 0;
|
|
if (v != 0.0) {
|
|
s = 1 - (result.components._1 / 100.0f) / v;
|
|
}
|
|
aResult = {h, s, v};
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::HsvToRgb(GlobalObject&, float aH, float aS, float aV,
|
|
nsTArray<float>& aResult) {
|
|
float h = aH * 360;
|
|
float w = aV * (1 - aS) * 100;
|
|
float b = (1 - aV) * 100;
|
|
StyleAbsoluteColor input{
|
|
.components = StyleColorComponents{h, w, b},
|
|
.alpha = 1.0,
|
|
.color_space = StyleColorSpace::Hwb,
|
|
};
|
|
StyleAbsoluteColor result = input.ToColorSpace(StyleColorSpace::Srgb);
|
|
aResult = {result.components._0, result.components._1, result.components._2};
|
|
}
|
|
|
|
/* static */
|
|
float InspectorUtils::RelativeLuminance(GlobalObject&, float aR, float aG,
|
|
float aB) {
|
|
return RelativeLuminanceUtils::Compute(aR, aG, aB);
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::ColorToRGBA(GlobalObject& aGlobal,
|
|
const nsACString& aColorString,
|
|
Nullable<InspectorRGBATuple>& aResult) {
|
|
const auto* styleData = [&]() -> const StylePerDocumentStyleData* {
|
|
nsCOMPtr<nsIGlobalObject> global =
|
|
do_QueryInterface(aGlobal.GetAsSupports());
|
|
if (!global) {
|
|
return nullptr;
|
|
}
|
|
auto* win = global->GetAsInnerWindow();
|
|
if (!win) {
|
|
return nullptr;
|
|
}
|
|
Document* doc = win->GetExtantDoc();
|
|
if (!doc) {
|
|
return nullptr;
|
|
}
|
|
PresShell* ps = doc->GetPresShell();
|
|
if (!ps) {
|
|
return nullptr;
|
|
}
|
|
return ps->StyleSet()->RawData();
|
|
}();
|
|
|
|
nscolor color = NS_RGB(0, 0, 0);
|
|
if (!ServoCSSParser::ComputeColor(styleData, NS_RGB(0, 0, 0), aColorString,
|
|
&color)) {
|
|
aResult.SetNull();
|
|
return;
|
|
}
|
|
|
|
InspectorRGBATuple& tuple = aResult.SetValue();
|
|
tuple.mR = NS_GET_R(color);
|
|
tuple.mG = NS_GET_G(color);
|
|
tuple.mB = NS_GET_B(color);
|
|
tuple.mA = nsStyleUtil::ColorComponentToFloat(NS_GET_A(color));
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::ColorTo(GlobalObject&, const nsACString& aFromColor,
|
|
const nsACString& aToColorSpace,
|
|
Nullable<InspectorColorToResult>& aResult) {
|
|
nsCString resultColor;
|
|
nsTArray<float> resultComponents;
|
|
bool resultAdjusted = false;
|
|
|
|
if (!ServoCSSParser::ColorTo(aFromColor, aToColorSpace, &resultColor,
|
|
&resultComponents, &resultAdjusted)) {
|
|
aResult.SetNull();
|
|
return;
|
|
}
|
|
|
|
auto& result = aResult.SetValue();
|
|
result.mColor.AssignASCII(resultColor);
|
|
result.mComponents = std::move(resultComponents);
|
|
result.mAdjusted = resultAdjusted;
|
|
}
|
|
|
|
/* static */
|
|
bool InspectorUtils::IsValidCSSColor(GlobalObject& aGlobalObject,
|
|
const nsACString& aColorString) {
|
|
return ServoCSSParser::IsValidCSSColor(aColorString);
|
|
}
|
|
|
|
/* static */
|
|
bool InspectorUtils::IsValidCSSImage(GlobalObject& aGlobalObject,
|
|
const nsACString& aImageString) {
|
|
return ServoCSSParser::IsValidCSSImage(aImageString);
|
|
}
|
|
|
|
/* static */
|
|
bool InspectorUtils::SetContentState(GlobalObject& aGlobalObject,
|
|
Element& aElement, uint64_t aState,
|
|
ErrorResult& aRv) {
|
|
RefPtr<EventStateManager> esm =
|
|
inLayoutUtils::GetEventStateManagerFor(aElement);
|
|
ElementState state(aState);
|
|
if (!esm || !EventStateManager::ManagesState(state)) {
|
|
aRv.Throw(NS_ERROR_INVALID_ARG);
|
|
return false;
|
|
}
|
|
return esm->SetContentState(&aElement, state);
|
|
}
|
|
|
|
/* static */
|
|
bool InspectorUtils::RemoveContentState(GlobalObject& aGlobalObject,
|
|
Element& aElement, uint64_t aState,
|
|
bool aClearActiveDocument,
|
|
ErrorResult& aRv) {
|
|
RefPtr<EventStateManager> esm =
|
|
inLayoutUtils::GetEventStateManagerFor(aElement);
|
|
ElementState state(aState);
|
|
if (!esm || !EventStateManager::ManagesState(state)) {
|
|
aRv.Throw(NS_ERROR_INVALID_ARG);
|
|
return false;
|
|
}
|
|
|
|
bool result = esm->SetContentState(nullptr, state);
|
|
|
|
if (aClearActiveDocument && state == ElementState::ACTIVE) {
|
|
EventStateManager* activeESM = static_cast<EventStateManager*>(
|
|
EventStateManager::GetActiveEventStateManager());
|
|
if (activeESM == esm) {
|
|
EventStateManager::ClearGlobalActiveContent(nullptr);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* static */
|
|
uint64_t InspectorUtils::GetContentState(GlobalObject& aGlobalObject,
|
|
Element& aElement) {
|
|
// NOTE: if this method is removed,
|
|
// please remove GetInternalValue from ElementState
|
|
return aElement.State().GetInternalValue();
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::GetUsedFontFaces(GlobalObject& aGlobalObject,
|
|
nsRange& aRange, uint32_t aMaxRanges,
|
|
bool aSkipCollapsedWhitespace,
|
|
nsLayoutUtils::UsedFontFaceList& aResult,
|
|
ErrorResult& aRv) {
|
|
nsresult rv =
|
|
aRange.GetUsedFontFaces(aResult, aMaxRanges, aSkipCollapsedWhitespace);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
aRv.Throw(rv);
|
|
}
|
|
}
|
|
|
|
static ElementState GetStatesForPseudoClass(const nsAString& aStatePseudo) {
|
|
if (aStatePseudo.IsEmpty() || aStatePseudo[0] != u':') {
|
|
return ElementState();
|
|
}
|
|
NS_ConvertUTF16toUTF8 statePseudo(Substring(aStatePseudo, 1));
|
|
return ElementState(Servo_PseudoClass_GetStates(&statePseudo));
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::GetCSSPseudoElementNames(GlobalObject& aGlobalObject,
|
|
nsTArray<nsString>& aResult) {
|
|
const auto kPseudoCount = static_cast<size_t>(PseudoStyleType::MAX);
|
|
for (size_t i = 0; i < kPseudoCount; ++i) {
|
|
PseudoStyleType type = static_cast<PseudoStyleType>(i);
|
|
if (type == PseudoStyleType::NotPseudo ||
|
|
!Servo_PseudoStyleType_EnabledForAllContent(type)) {
|
|
continue;
|
|
}
|
|
auto& string = *aResult.AppendElement();
|
|
// Use two semi-colons (though internally we use one).
|
|
string.Append(u':');
|
|
const nsStaticAtom* atom = PseudoStyle::GetAtom(type);
|
|
string.Append(nsDependentAtomString(atom));
|
|
}
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::AddPseudoClassLock(GlobalObject& aGlobalObject,
|
|
Element& aElement,
|
|
const nsAString& aPseudoClass,
|
|
bool aEnabled) {
|
|
ElementState state = GetStatesForPseudoClass(aPseudoClass);
|
|
if (state.IsEmpty()) {
|
|
return;
|
|
}
|
|
|
|
aElement.LockStyleStates(state, aEnabled);
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::RemovePseudoClassLock(GlobalObject& aGlobal,
|
|
Element& aElement,
|
|
const nsAString& aPseudoClass) {
|
|
ElementState state = GetStatesForPseudoClass(aPseudoClass);
|
|
if (state.IsEmpty()) {
|
|
return;
|
|
}
|
|
|
|
aElement.UnlockStyleStates(state);
|
|
}
|
|
|
|
/* static */
|
|
bool InspectorUtils::HasPseudoClassLock(GlobalObject& aGlobalObject,
|
|
Element& aElement,
|
|
const nsAString& aPseudoClass) {
|
|
ElementState state = GetStatesForPseudoClass(aPseudoClass);
|
|
if (state.IsEmpty()) {
|
|
return false;
|
|
}
|
|
|
|
ElementState locks = aElement.LockedStyleStates().mLocks;
|
|
return locks.HasAllStates(state);
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::ClearPseudoClassLocks(GlobalObject& aGlobalObject,
|
|
Element& aElement) {
|
|
aElement.ClearStyleStateLocks();
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::ParseStyleSheet(GlobalObject& aGlobalObject,
|
|
StyleSheet& aSheet,
|
|
const nsACString& aInput,
|
|
ErrorResult& aRv) {
|
|
aSheet.ReparseSheet(aInput, aRv);
|
|
}
|
|
|
|
bool InspectorUtils::IsCustomElementName(GlobalObject&, const nsAString& aName,
|
|
const nsAString& aNamespaceURI) {
|
|
if (aName.IsEmpty()) {
|
|
return false;
|
|
}
|
|
|
|
int32_t namespaceID;
|
|
nsNameSpaceManager::GetInstance()->RegisterNameSpace(aNamespaceURI,
|
|
namespaceID);
|
|
|
|
RefPtr<nsAtom> nameElt = NS_Atomize(aName);
|
|
return nsContentUtils::IsCustomElementName(nameElt, namespaceID);
|
|
}
|
|
|
|
bool InspectorUtils::IsElementThemed(GlobalObject&, Element& aElement) {
|
|
// IsThemed will check if the native theme supports the widget using
|
|
// ThemeSupportsWidget which in turn will check that the widget is not
|
|
// already styled by content through nsNativeTheme::IsWidgetStyled. We
|
|
// assume that if the native theme styles the widget and the author did not
|
|
// override the appropriate styles, the theme will provide focus styling.
|
|
nsIFrame* frame = aElement.GetPrimaryFrame(FlushType::Frames);
|
|
return frame && frame->IsThemed();
|
|
}
|
|
|
|
bool InspectorUtils::IsUsedColorSchemeDark(GlobalObject&, Element& aElement) {
|
|
nsIFrame* frame = aElement.GetPrimaryFrame(FlushType::Frames);
|
|
return frame && LookAndFeel::ColorSchemeForFrame(frame) == ColorScheme::Dark;
|
|
}
|
|
|
|
Element* InspectorUtils::ContainingBlockOf(GlobalObject&, Element& aElement) {
|
|
nsIFrame* frame = aElement.GetPrimaryFrame(FlushType::Frames);
|
|
if (!frame) {
|
|
return nullptr;
|
|
}
|
|
nsIFrame* cb = frame->GetContainingBlock();
|
|
if (!cb) {
|
|
return nullptr;
|
|
}
|
|
return Element::FromNodeOrNull(cb->GetContent());
|
|
}
|
|
|
|
bool InspectorUtils::IsBlockContainer(GlobalObject&, Element& aElement) {
|
|
nsIFrame* frame = aElement.GetPrimaryFrame(FlushType::Frames);
|
|
if (!frame) {
|
|
return false;
|
|
}
|
|
if (frame->IsBlockFrameOrSubclass()) {
|
|
return true;
|
|
}
|
|
// ScrollContainerFrame::GetContentInsertionFrame() jumps across the block and
|
|
// returns the ruby inside (because it calls GetContentInsertionFrame on its
|
|
// own). So we have to account for that here.
|
|
if (auto* sc = frame->GetScrollTargetFrame()) {
|
|
if (sc->GetScrolledFrame()->IsBlockFrameOrSubclass()) {
|
|
return true;
|
|
}
|
|
}
|
|
if (nsIFrame* inner = frame->GetContentInsertionFrame()) {
|
|
if (inner->IsBlockFrameOrSubclass()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void InspectorUtils::GetBlockLineCounts(GlobalObject& aGlobal,
|
|
Element& aElement,
|
|
Nullable<nsTArray<uint32_t>>& aResult) {
|
|
nsBlockFrame* block =
|
|
do_QueryFrame(aElement.GetPrimaryFrame(FlushType::Layout));
|
|
if (!block) {
|
|
aResult.SetNull();
|
|
return;
|
|
}
|
|
|
|
// If CSS columns were specified on the actual block element (rather than an
|
|
// ancestor block, GetPrimaryFrame will return its ColumnSetWrapperFrame, and
|
|
// we need to drill down to the actual block that contains the lines.
|
|
if (block->IsColumnSetWrapperFrame()) {
|
|
nsIFrame* firstChild = block->PrincipalChildList().FirstChild();
|
|
if (!firstChild->IsColumnSetFrame()) {
|
|
aResult.SetNull();
|
|
return;
|
|
}
|
|
block = do_QueryFrame(firstChild->PrincipalChildList().FirstChild());
|
|
if (!block || block->GetContent() != &aElement) {
|
|
aResult.SetNull();
|
|
return;
|
|
}
|
|
}
|
|
|
|
nsTArray<uint32_t> result;
|
|
do {
|
|
result.AppendElement(block->Lines().size());
|
|
block = static_cast<nsBlockFrame*>(block->GetNextInFlow());
|
|
} while (block);
|
|
|
|
aResult.SetValue(std::move(result));
|
|
}
|
|
|
|
static bool FrameHasSpecifiedSize(const nsIFrame* aFrame) {
|
|
auto wm = aFrame->GetWritingMode();
|
|
|
|
const nsStylePosition* stylePos = aFrame->StylePosition();
|
|
const auto anchorResolutionParams = AnchorPosResolutionParams::From(aFrame);
|
|
|
|
return stylePos->ISize(wm, anchorResolutionParams)->IsLengthPercentage() ||
|
|
stylePos->BSize(wm, anchorResolutionParams)->IsLengthPercentage();
|
|
}
|
|
|
|
static bool IsFrameOutsideOfAncestor(const nsIFrame* aFrame,
|
|
const nsIFrame* aAncestorFrame,
|
|
const nsRect& aAncestorRect) {
|
|
nsRect frameRectInAncestorSpace = nsLayoutUtils::TransformFrameRectToAncestor(
|
|
aFrame, aFrame->ScrollableOverflowRect(), RelativeTo{aAncestorFrame},
|
|
nullptr, nullptr, false, nullptr);
|
|
|
|
// We use nsRect::SaturatingUnionEdges because it correctly handles the case
|
|
// of a zero-width or zero-height frame, which we still want to consider as
|
|
// contributing to the union.
|
|
nsRect unionizedRect =
|
|
frameRectInAncestorSpace.SaturatingUnionEdges(aAncestorRect);
|
|
|
|
// If frameRectInAncestorSpace is inside aAncestorRect then union of
|
|
// frameRectInAncestorSpace and aAncestorRect should be equal to aAncestorRect
|
|
// hence if it is equal, then false should be returned.
|
|
|
|
return !(unionizedRect == aAncestorRect);
|
|
}
|
|
|
|
static void AddOverflowingChildrenOfElement(const nsIFrame* aFrame,
|
|
const nsIFrame* aAncestorFrame,
|
|
const nsRect& aRect,
|
|
SimpleContentList& aList) {
|
|
MOZ_ASSERT(aFrame, "we assume the passed-in frame is non-null");
|
|
for (const auto& childList : aFrame->ChildLists()) {
|
|
for (const nsIFrame* child : childList.mList) {
|
|
// We want to identify if the child or any of its children have a
|
|
// frame that is outside of aAncestorFrame. Ideally, child would have
|
|
// a frame rect that encompasses all of its children, but this is not
|
|
// guaranteed by the frame tree. So instead we first check other
|
|
// conditions that indicate child is an interesting frame:
|
|
//
|
|
// 1) child has a specified size
|
|
// 2) none of child's children are implicated
|
|
//
|
|
// If either of these conditions are true, we *then* check if child's
|
|
// frame is outside of aAncestorFrame, and if so, we add child's content
|
|
// to aList.
|
|
|
|
if (FrameHasSpecifiedSize(child) &&
|
|
IsFrameOutsideOfAncestor(child, aAncestorFrame, aRect)) {
|
|
aList.MaybeAppendElement(child->GetContent());
|
|
continue;
|
|
}
|
|
|
|
uint32_t currListLength = aList.Length();
|
|
AddOverflowingChildrenOfElement(child, aAncestorFrame, aRect, aList);
|
|
|
|
// If child is a leaf node, length of aList should remain same after
|
|
// calling AddOverflowingChildrenOfElement on it.
|
|
if (currListLength == aList.Length() &&
|
|
IsFrameOutsideOfAncestor(child, aAncestorFrame, aRect)) {
|
|
aList.MaybeAppendElement(child->GetContent());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
already_AddRefed<dom::NodeList> InspectorUtils::GetOverflowingChildrenOfElement(
|
|
GlobalObject& aGlobal, Element& aElement) {
|
|
auto list = MakeRefPtr<SimpleContentList>(&aElement);
|
|
const ScrollContainerFrame* scrollContainerFrame =
|
|
aElement.GetScrollContainerFrame();
|
|
// Element must be a ScrollContainerFrame.
|
|
if (!scrollContainerFrame) {
|
|
return list.forget();
|
|
}
|
|
|
|
auto scrollPortRect = scrollContainerFrame->GetScrollPortRect();
|
|
const nsIFrame* scrolledFrame = scrollContainerFrame->GetScrolledFrame();
|
|
AddOverflowingChildrenOfElement(scrolledFrame, scrollContainerFrame,
|
|
scrollPortRect, *list);
|
|
return list.forget();
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::GetRegisteredCssHighlights(GlobalObject& aGlobalObject,
|
|
Document& aDocument,
|
|
bool aActiveOnly,
|
|
nsTArray<nsString>& aResult) {
|
|
for (auto const& iter : aDocument.HighlightRegistry().HighlightsOrdered()) {
|
|
const RefPtr<nsAtom>& highlightName = iter.first();
|
|
const RefPtr<Highlight>& highlight = iter.second();
|
|
if (!aActiveOnly || highlight->Size() > 0) {
|
|
aResult.AppendElement(highlightName->GetUTF16String());
|
|
}
|
|
}
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::GetCSSRegisteredProperties(
|
|
GlobalObject& aGlobalObject, Document& aDocument,
|
|
nsTArray<InspectorCSSPropertyDefinition>& aResult) {
|
|
nsTArray<StylePropDef> result;
|
|
|
|
ServoStyleSet& styleSet = aDocument.EnsureStyleSet();
|
|
// Update the rules before looking up @property rules.
|
|
styleSet.UpdateStylistIfNeeded();
|
|
|
|
Servo_GetRegisteredCustomProperties(styleSet.RawData(), &result);
|
|
for (const auto& propDef : result) {
|
|
InspectorCSSPropertyDefinition& property = *aResult.AppendElement();
|
|
|
|
// Servo does not include the "--" prefix in the property definition name.
|
|
// Add it back as it's easier for DevTools to handle them _with_ "--".
|
|
property.mName.AssignLiteral("--");
|
|
property.mName.Append(nsAtomCString(propDef.name.AsAtom()));
|
|
property.mSyntax.Append(propDef.syntax);
|
|
property.mInherits = propDef.inherits;
|
|
if (propDef.has_initial_value) {
|
|
property.mInitialValue.Append(propDef.initial_value);
|
|
} else {
|
|
property.mInitialValue.SetIsVoid(true);
|
|
}
|
|
property.mFromJS = propDef.from_js;
|
|
}
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::GetCSSRegisteredProperty(
|
|
GlobalObject& aGlobalObject, Document& aDocument, const nsACString& aName,
|
|
Nullable<InspectorCSSPropertyDefinition>& aResult) {
|
|
StylePropDef result{StyleAtom(NS_Atomize(aName))};
|
|
|
|
// Update the rules before looking up @property rules.
|
|
ServoStyleSet& styleSet = aDocument.EnsureStyleSet();
|
|
styleSet.UpdateStylistIfNeeded();
|
|
|
|
if (!Servo_GetRegisteredCustomProperty(styleSet.RawData(), &aName, &result)) {
|
|
aResult.SetNull();
|
|
return;
|
|
}
|
|
|
|
InspectorCSSPropertyDefinition& propDef = aResult.SetValue();
|
|
|
|
// Servo does not include the "--" prefix in the property definition name.
|
|
// Add it back as it's easier for DevTools to handle them _with_ "--".
|
|
propDef.mName.AssignLiteral("--");
|
|
propDef.mName.Append(nsAtomCString(result.name.AsAtom()));
|
|
propDef.mSyntax.Append(result.syntax);
|
|
propDef.mInherits = result.inherits;
|
|
if (result.has_initial_value) {
|
|
propDef.mInitialValue.Append(result.initial_value);
|
|
} else {
|
|
propDef.mInitialValue.SetIsVoid(true);
|
|
}
|
|
propDef.mFromJS = result.from_js;
|
|
}
|
|
|
|
/* static */
|
|
bool InspectorUtils::ValueMatchesSyntax(GlobalObject&, Document& aDocument,
|
|
const nsACString& aValue,
|
|
const nsACString& aSyntax) {
|
|
return Servo_Value_Matches_Syntax(&aValue, &aSyntax,
|
|
aDocument.DefaultStyleAttrURLData());
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::GetRuleBodyText(GlobalObject&,
|
|
const nsACString& aInitialText,
|
|
nsACString& aBodyText) {
|
|
Servo_GetRuleBodyText(&aInitialText, &aBodyText);
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::ReplaceBlockRuleBodyTextInStylesheet(
|
|
GlobalObject&, const nsACString& aStyleSheetText, uint32_t aLine,
|
|
uint32_t aColumn, const nsACString& aNewBodyText,
|
|
nsACString& aNewStyleSheetText) {
|
|
Servo_ReplaceBlockRuleBodyTextInStylesheetText(
|
|
&aStyleSheetText, aLine, aColumn, &aNewBodyText, &aNewStyleSheetText);
|
|
}
|
|
|
|
void InspectorUtils::SetVerticalClipping(GlobalObject&,
|
|
BrowsingContext* aContext,
|
|
mozilla::CSSCoord aOffset) {
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
|
if (!aContext) {
|
|
return;
|
|
}
|
|
|
|
CanonicalBrowsingContext* canonical = aContext->Canonical();
|
|
if (!canonical) {
|
|
return;
|
|
}
|
|
|
|
BrowserParent* parent = canonical->GetBrowserParent();
|
|
if (!parent) {
|
|
return;
|
|
}
|
|
const LayoutDeviceToCSSScale scale = parent->GetLayoutDeviceToCSSScale();
|
|
const mozilla::LayoutDeviceCoord layoutOffset = aOffset / scale;
|
|
const mozilla::ScreenCoord screenOffset = ViewAs<ScreenPixel>(
|
|
layoutOffset, PixelCastJustification::LayoutDeviceIsScreenForBounds);
|
|
const mozilla::ScreenIntCoord offset = screenOffset.Rounded();
|
|
parent->DynamicToolbarOffsetChanged(offset);
|
|
|
|
RefPtr<nsIWidget> widget = canonical->GetParentProcessWidgetContaining();
|
|
if (!widget) {
|
|
return;
|
|
}
|
|
widget->DynamicToolbarOffsetChanged(offset);
|
|
}
|
|
|
|
void InspectorUtils::SetDynamicToolbarMaxHeight(GlobalObject&,
|
|
BrowsingContext* aContext,
|
|
mozilla::CSSCoord aHeight) {
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
|
if (!aContext) {
|
|
return;
|
|
}
|
|
|
|
CanonicalBrowsingContext* canonical = aContext->Canonical();
|
|
if (!canonical) {
|
|
return;
|
|
}
|
|
|
|
BrowserParent* parent = canonical->GetBrowserParent();
|
|
if (!parent) {
|
|
return;
|
|
}
|
|
|
|
const LayoutDeviceToCSSScale scale = parent->GetLayoutDeviceToCSSScale();
|
|
const mozilla::LayoutDeviceCoord layoutOffset = aHeight / scale;
|
|
const mozilla::ScreenCoord screenOffset = ViewAs<ScreenPixel>(
|
|
layoutOffset, PixelCastJustification::LayoutDeviceIsScreenForBounds);
|
|
const mozilla::ScreenIntCoord height = screenOffset.Rounded();
|
|
parent->DynamicToolbarMaxHeightChanged(height);
|
|
}
|
|
|
|
uint16_t InspectorUtils::GetGridContainerType(GlobalObject&,
|
|
Element& aElement) {
|
|
auto* f = nsGridContainerFrame::GetGridContainerFrame(
|
|
aElement.GetPrimaryFrame(FlushType::Frames));
|
|
if (!f) {
|
|
return InspectorUtils_Binding::GRID_NONE;
|
|
}
|
|
uint16_t result = InspectorUtils_Binding::GRID_CONTAINER;
|
|
if (f->IsRowSubgrid()) {
|
|
result |= InspectorUtils_Binding::GRID_SUBGRID_ROW;
|
|
}
|
|
if (f->IsColSubgrid()) {
|
|
result |= InspectorUtils_Binding::GRID_SUBGRID_COL;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void InspectorUtils::GetAnchorFor(GlobalObject&, Element& aElement,
|
|
const nsAString& aName,
|
|
Nullable<InspectorAnchorElement>& aResult) {
|
|
auto* frame = aElement.GetPrimaryFrame(FlushType::Frames);
|
|
if (!frame || !frame->IsAbsolutelyPositioned()) {
|
|
return;
|
|
}
|
|
|
|
nsIFrame* anchor = nullptr;
|
|
RefPtr<nsAtom> name;
|
|
ScopedNameRef scopedName{nullptr, StyleCascadeLevel::Default()};
|
|
InspectorAnchorType type = InspectorAnchorType::Explicit;
|
|
if (aName.IsEmpty()) {
|
|
// Anchor name, otherwise implicit anchor.
|
|
const auto& positionAnchor = frame->StylePosition()->mPositionAnchor;
|
|
if (positionAnchor.value.IsIdent()) {
|
|
scopedName = {positionAnchor.value.AsIdent().AsAtom(),
|
|
positionAnchor.scope};
|
|
} else {
|
|
auto implicit = AnchorPositioningUtils::GetAnchorPosImplicitAnchor(frame);
|
|
anchor = implicit.mAnchorFrame;
|
|
if (!anchor) {
|
|
return;
|
|
}
|
|
switch (implicit.mKind) {
|
|
case AnchorPositioningUtils::ImplicitAnchorKind::None:
|
|
break;
|
|
case AnchorPositioningUtils::ImplicitAnchorKind::Popover:
|
|
type = InspectorAnchorType::Popover;
|
|
break;
|
|
case AnchorPositioningUtils::ImplicitAnchorKind::PseudoElement:
|
|
type = InspectorAnchorType::Pseudo_element;
|
|
break;
|
|
}
|
|
auto& result = aResult.SetValue();
|
|
result.mElement = *anchor->GetContent()->AsElement();
|
|
result.mType = type;
|
|
return;
|
|
}
|
|
} else {
|
|
// TODO(emilio): Allow looking up names from other trees.
|
|
name = NS_Atomize(aName);
|
|
scopedName = {name, StyleCascadeLevel::Default()};
|
|
}
|
|
anchor = frame->PresShell()->GetAnchorPosAnchor(scopedName, frame);
|
|
if (!anchor) {
|
|
return;
|
|
}
|
|
auto& result = aResult.SetValue();
|
|
result.mElement = *anchor->GetContent()->AsElement();
|
|
result.mType = type;
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::GetAnchorNamesFor(GlobalObject& aGlobalObject,
|
|
Element& aElement,
|
|
nsTArray<nsString>& aResult) {
|
|
auto* frame = aElement.GetPrimaryFrame(FlushType::Frames);
|
|
if (!frame || !frame->IsAbsolutelyPositioned()) {
|
|
return;
|
|
}
|
|
|
|
frame->PresShell()->CollectAnchorNames(frame, aResult);
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::GetComputationStepsSupportedCSSFunctions(
|
|
GlobalObject& aGlobalObject, nsTArray<nsCString>& aResult) {
|
|
Servo_GetComputationStepsSupportedCSSFunctions(&aResult);
|
|
}
|
|
|
|
/* static */
|
|
void InspectorUtils::GetComputationSteps(GlobalObject& aGlobalObject,
|
|
const nsAString& aExpression,
|
|
Element& aElement,
|
|
const nsAString& aPseudo,
|
|
nsTArray<nsString>& aResult) {
|
|
Document* doc = aElement.GetComposedDoc();
|
|
if (!doc) {
|
|
return;
|
|
}
|
|
|
|
auto pseudo = PseudoStyleRequest::Parse(
|
|
aPseudo, aElement.OwnerDoc()->DefaultStyleAttrURLData());
|
|
if (!pseudo) {
|
|
return;
|
|
}
|
|
|
|
RefPtr<const ComputedStyle> computedStyle =
|
|
GetCleanComputedStyleForElement(&aElement, *pseudo);
|
|
if (!computedStyle) {
|
|
// This can fail for elements that are not in the document or
|
|
// if the document they're in doesn't have a presshell. Bail out.
|
|
return;
|
|
}
|
|
|
|
Servo_GetComputationSteps(&aExpression, &aElement, pseudo->mType,
|
|
computedStyle, doc->EnsureStyleSet().RawData(),
|
|
&aResult);
|
|
}
|
|
|
|
} // namespace mozilla::dom
|