/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 "TextControlElement.h" #include "mozilla/IMEContentObserver.h" #include "mozilla/IMEStateManager.h" #include "mozilla/TextControlState.h" #include "mozilla/TextEditor.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/ShadowRoot.h" #include "nsIFormControl.h" #include "nsTextNode.h" using namespace mozilla::dom; namespace mozilla { static RefPtr MakeAnonElement(Document& aDoc, PseudoStyleType aPseudoType, nsAtom* aTag = nsGkAtoms::div) { MOZ_ASSERT(aPseudoType != PseudoStyleType::NotPseudo); RefPtr element = aDoc.CreateHTMLElement(aTag); element->SetPseudoElementType(aPseudoType); if (aPseudoType == PseudoStyleType::MozTextControlEditingRoot) { // Make our root node editable element->SetFlags(NODE_IS_EDITABLE); } else { // The text control's accessible takes care of the placeholder etc for us, // all our pseudo-elements other than the root should not show up in the // a11y tree. element->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_hidden, u"true"_ns, false); } return element; } RefPtr MakePlaceholderOrPreview(Document& aDoc, PseudoStyleType aPseudoType, const nsAString& aValue) { RefPtr el = MakeAnonElement(aDoc, aPseudoType); RefPtr text = aDoc.CreateTextNode(aValue); el->AppendChildTo(text, false, IgnoreErrors()); return el; } Element* TextControlElement::FindShadowPseudo(PseudoStyleType aType) const { auto* sr = GetShadowRoot(); if (!sr) { return nullptr; } for (auto* child = sr->GetFirstChild(); child; child = child->GetNextSibling()) { auto* el = Element::FromNode(child); if (el->GetPseudoElementType() == aType) { return el; } } return nullptr; } void TextControlElement::GetPreviewValue(nsAString& aValue) { Element* existing = FindShadowPseudo(PseudoStyleType::MozTextControlPreview); if (!existing) { return; } auto* text = Text::FromNodeOrNull(existing->GetFirstChild()); if (NS_WARN_IF(!text)) { return; } text->GetData(aValue); } void TextControlElement::SetPreviewValue(const nsAString& aValue) { RefPtr sr = GetShadowRoot(); if (!sr) { return; } RefPtr existing = FindShadowPseudo(PseudoStyleType::MozTextControlPreview); if (aValue.IsEmpty()) { if (existing) { existing->Remove(); } return; } if (existing) { RefPtr text = Text::FromNodeOrNull(existing->GetFirstChild()); if (NS_WARN_IF(!text)) { return; } text->SetData(aValue, IgnoreErrors()); return; } // Preview goes before the root (and after placeholder if present). RefPtr editingRoot = FindShadowPseudo(PseudoStyleType::MozTextControlEditingRoot); if (NS_WARN_IF(!editingRoot)) { // This can happen if we get called on e.g. a datetimebox or so. return; } RefPtr preview = MakePlaceholderOrPreview( *OwnerDoc(), PseudoStyleType::MozTextControlPreview, aValue); sr->InsertChildBefore(preview, editingRoot, /* aNotify = */ true, IgnoreErrors()); } static void ProcessPlaceholder(nsAString& aValue, bool aTextArea) { if (aTextArea) { //