--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -2631,27 +2631,16 @@ HTMLInputElement::GetRootEditorNode()
nsTextEditorState* state = GetEditorState();
if (state) {
return state->GetRootNode();
}
return nullptr;
}
NS_IMETHODIMP_(Element*)
-HTMLInputElement::CreatePlaceholderNode()
-{
- nsTextEditorState* state = GetEditorState();
- if (state) {
- NS_ENSURE_SUCCESS(state->CreatePlaceholderNode(), nullptr);
- return state->GetPlaceholderNode();
- }
- return nullptr;
-}
-
-NS_IMETHODIMP_(Element*)
HTMLInputElement::GetPlaceholderNode()
{
nsTextEditorState* state = GetEditorState();
if (state) {
return state->GetPlaceholderNode();
}
return nullptr;
}
@@ -2672,27 +2661,16 @@ HTMLInputElement::GetPlaceholderVisibili
if (!state) {
return false;
}
return state->GetPlaceholderVisibility();
}
NS_IMETHODIMP_(Element*)
-HTMLInputElement::CreatePreviewNode()
-{
- nsTextEditorState* state = GetEditorState();
- if (state) {
- NS_ENSURE_SUCCESS(state->CreatePreviewNode(), nullptr);
- return state->GetPreviewNode();
- }
- return nullptr;
-}
-
-NS_IMETHODIMP_(Element*)
HTMLInputElement::GetPreviewNode()
{
nsTextEditorState* state = GetEditorState();
if (state) {
return state->GetPreviewNode();
}
return nullptr;
}
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -242,19 +242,17 @@ public:
NS_IMETHOD_(void) GetTextEditorValue(nsAString& aValue, bool aIgnoreWrap) const override;
NS_IMETHOD_(mozilla::TextEditor*) GetTextEditor() override;
NS_IMETHOD_(nsISelectionController*) GetSelectionController() override;
NS_IMETHOD_(nsFrameSelection*) GetConstFrameSelection() override;
NS_IMETHOD BindToFrame(nsTextControlFrame* aFrame) override;
NS_IMETHOD_(void) UnbindFromFrame(nsTextControlFrame* aFrame) override;
NS_IMETHOD CreateEditor() override;
NS_IMETHOD_(Element*) GetRootEditorNode() override;
- NS_IMETHOD_(Element*) CreatePlaceholderNode() override;
NS_IMETHOD_(Element*) GetPlaceholderNode() override;
- NS_IMETHOD_(Element*) CreatePreviewNode() override;
NS_IMETHOD_(Element*) GetPreviewNode() override;
NS_IMETHOD_(void) UpdateOverlayTextVisibility(bool aNotify) override;
NS_IMETHOD_(void) SetPreviewValue(const nsAString& aValue) override;
NS_IMETHOD_(void) GetPreviewValue(nsAString& aValue) override;
NS_IMETHOD_(void) EnablePreview() override;
NS_IMETHOD_(bool) IsPreviewEnabled() override;
NS_IMETHOD_(bool) GetPlaceholderVisibility() override;
NS_IMETHOD_(bool) GetPreviewVisibility() override;
--- a/dom/html/HTMLTextAreaElement.cpp
+++ b/dom/html/HTMLTextAreaElement.cpp
@@ -288,23 +288,16 @@ HTMLTextAreaElement::CreateEditor()
NS_IMETHODIMP_(Element*)
HTMLTextAreaElement::GetRootEditorNode()
{
return mState.GetRootNode();
}
NS_IMETHODIMP_(Element*)
-HTMLTextAreaElement::CreatePlaceholderNode()
-{
- NS_ENSURE_SUCCESS(mState.CreatePlaceholderNode(), nullptr);
- return mState.GetPlaceholderNode();
-}
-
-NS_IMETHODIMP_(Element*)
HTMLTextAreaElement::GetPlaceholderNode()
{
return mState.GetPlaceholderNode();
}
NS_IMETHODIMP_(void)
HTMLTextAreaElement::UpdateOverlayTextVisibility(bool aNotify)
{
@@ -313,23 +306,16 @@ HTMLTextAreaElement::UpdateOverlayTextVi
NS_IMETHODIMP_(bool)
HTMLTextAreaElement::GetPlaceholderVisibility()
{
return mState.GetPlaceholderVisibility();
}
NS_IMETHODIMP_(Element*)
-HTMLTextAreaElement::CreatePreviewNode()
-{
- NS_ENSURE_SUCCESS(mState.CreatePreviewNode(), nullptr);
- return mState.GetPreviewNode();
-}
-
-NS_IMETHODIMP_(Element*)
HTMLTextAreaElement::GetPreviewNode()
{
return mState.GetPreviewNode();
}
NS_IMETHODIMP_(void)
HTMLTextAreaElement::SetPreviewValue(const nsAString& aValue)
{
--- a/dom/html/HTMLTextAreaElement.h
+++ b/dom/html/HTMLTextAreaElement.h
@@ -98,19 +98,17 @@ public:
NS_IMETHOD_(void) GetTextEditorValue(nsAString& aValue, bool aIgnoreWrap) const override;
NS_IMETHOD_(mozilla::TextEditor*) GetTextEditor() override;
NS_IMETHOD_(nsISelectionController*) GetSelectionController() override;
NS_IMETHOD_(nsFrameSelection*) GetConstFrameSelection() override;
NS_IMETHOD BindToFrame(nsTextControlFrame* aFrame) override;
NS_IMETHOD_(void) UnbindFromFrame(nsTextControlFrame* aFrame) override;
NS_IMETHOD CreateEditor() override;
NS_IMETHOD_(Element*) GetRootEditorNode() override;
- NS_IMETHOD_(Element*) CreatePlaceholderNode() override;
NS_IMETHOD_(Element*) GetPlaceholderNode() override;
- NS_IMETHOD_(Element*) CreatePreviewNode() override;
NS_IMETHOD_(Element*) GetPreviewNode() override;
NS_IMETHOD_(void) UpdateOverlayTextVisibility(bool aNotify) override;
NS_IMETHOD_(bool) GetPlaceholderVisibility() override;
NS_IMETHOD_(bool) GetPreviewVisibility() override;
NS_IMETHOD_(void) SetPreviewValue(const nsAString& aValue) override;
NS_IMETHOD_(void) GetPreviewValue(nsAString& aValue) override;
NS_IMETHOD_(void) EnablePreview() override;
NS_IMETHOD_(bool) IsPreviewEnabled() override;
--- a/dom/html/nsITextControlElement.h
+++ b/dom/html/nsITextControlElement.h
@@ -134,31 +134,21 @@ public:
NS_IMETHOD CreateEditor() = 0;
/**
* Get the anonymous root node for the text control.
*/
NS_IMETHOD_(mozilla::dom::Element*) GetRootEditorNode() = 0;
/**
- * Create the placeholder anonymous node for the text control and returns it.
- */
- NS_IMETHOD_(mozilla::dom::Element*) CreatePlaceholderNode() = 0;
-
- /**
* Get the placeholder anonymous node for the text control.
*/
NS_IMETHOD_(mozilla::dom::Element*) GetPlaceholderNode() = 0;
/**
- * Create the preview anonymous node for the text control and returns it.
- */
- NS_IMETHOD_(mozilla::dom::Element*) CreatePreviewNode() = 0;
-
- /**
* Get the preview anonymous node for the text control.
*/
NS_IMETHOD_(mozilla::dom::Element*) GetPreviewNode() = 0;
/**
* Update preview value for the text control.
*/
NS_IMETHOD_(void) SetPreviewValue(const nsAString& aValue) = 0;
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -1181,31 +1181,48 @@ nsTextEditorState::Construct(nsITextCont
state->mEditorInitialized = false;
state->mInitializing = false;
state->mValueTransferInProgress = false;
state->mSelectionCached = true;
state->mSelectionRestoreEagerInit = false;
state->mPlaceholderVisibility = false;
state->mPreviewVisibility = false;
state->mIsCommittingComposition = false;
- state->ClearValueCache();
// When adding more member variable initializations here, add the same
// also to the constructor.
return state;
}
return new nsTextEditorState(aOwningElement);
}
nsTextEditorState::~nsTextEditorState()
{
MOZ_COUNT_DTOR(nsTextEditorState);
Clear();
}
+Element*
+nsTextEditorState::GetRootNode()
+{
+ return mBoundFrame ? mBoundFrame->GetRootNode() : nullptr;
+}
+
+Element*
+nsTextEditorState::GetPlaceholderNode()
+{
+ return mBoundFrame ? mBoundFrame->GetPlaceholderNode() : nullptr;
+}
+
+Element*
+nsTextEditorState::GetPreviewNode()
+{
+ return mBoundFrame ? mBoundFrame->GetPreviewNode() : nullptr;
+}
+
void
nsTextEditorState::Clear()
{
if (mBoundFrame) {
// Oops, we still have a frame!
// This should happen when the type of a text input control is being changed
// to something which is not a text control. In this case, we should pretend
// that a frame is being destroyed, and clean up after ourselves properly.
@@ -1220,29 +1237,23 @@ nsTextEditorState::Clear()
}
void nsTextEditorState::Unlink()
{
nsTextEditorState* tmp = this;
tmp->Clear();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelCon)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextEditor)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootNode)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mPlaceholderDiv)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreviewDiv)
}
void nsTextEditorState::Traverse(nsCycleCollectionTraversalCallback& cb)
{
nsTextEditorState* tmp = this;
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelCon)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextEditor)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootNode)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlaceholderDiv)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreviewDiv)
}
nsFrameSelection*
nsTextEditorState::GetConstFrameSelection() {
if (mSelCon)
return mSelCon->GetConstFrameSelection();
return nullptr;
}
@@ -1314,25 +1325,21 @@ nsTextEditorState::BindToFrame(nsTextCon
// binding to the frame.
nsAutoString currentValue;
if (mTextEditor) {
GetValue(currentValue, true);
}
mBoundFrame = aFrame;
- nsresult rv = CreateRootNode();
- NS_ENSURE_SUCCESS(rv, rv);
-
- nsIContent *rootNode = GetRootNode();
- rv = InitializeRootNode();
- NS_ENSURE_SUCCESS(rv, rv);
-
- nsIPresShell *shell = mBoundFrame->PresContext()->GetPresShell();
- NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
+ Element* rootNode = aFrame->GetRootNode();
+ MOZ_ASSERT(rootNode);
+
+ nsIPresShell* shell = aFrame->PresContext()->GetPresShell();
+ MOZ_ASSERT(shell);
// Create selection
RefPtr<nsFrameSelection> frameSel = new nsFrameSelection();
// Create a SelectionController
mSelCon = new nsTextInputSelectionImpl(frameSel, shell, rootNode);
MOZ_ASSERT(!mTextListener, "Should not overwrite the object");
mTextListener = new nsTextInputListener(mTextCtrlElement);
@@ -1408,18 +1415,16 @@ nsTextEditorState::PrepareEditor(const n
AutoHideSelectionChanges hideSelectionChanges(GetConstFrameSelection());
// Don't attempt to initialize recursively!
InitializationGuard guard(*this);
if (guard.IsInitializingRecursively()) {
return NS_ERROR_NOT_INITIALIZED;
}
- ClearValueCache();
-
// Note that we don't check mTextEditor here, because we might already have
// one around, in which case we don't create a new one, and we'll just tie
// the required machinery to it.
nsPresContext *presContext = mBoundFrame->PresContext();
nsIPresShell *shell = presContext->GetPresShell();
// Setup the editor flags
@@ -2096,26 +2101,25 @@ nsTextEditorState::DestroyEditor()
// notify the editor that we are going away
if (mEditorInitialized) {
if (mTextListener) {
mTextEditor->RemoveEditorObserver(mTextListener);
}
mTextEditor->PreDestroy(true);
mEditorInitialized = false;
}
- ClearValueCache();
}
void
nsTextEditorState::UnbindFromFrame(nsTextControlFrame* aFrame)
{
NS_ENSURE_TRUE_VOID(mBoundFrame);
// If it was, however, it should be unbounded from the same frame.
- NS_ASSERTION(!aFrame || aFrame == mBoundFrame, "Unbinding from the wrong frame");
+ MOZ_ASSERT(aFrame == mBoundFrame, "Unbinding from the wrong frame");
NS_ENSURE_TRUE_VOID(!aFrame || aFrame == mBoundFrame);
// If the editor is modified but nsIEditorObserver::EditAction() hasn't been
// called yet, we need to notify it here because editor may be destroyed
// before EditAction() is called if selection listener causes flushing layout.
if (mTextListener && mTextEditor && mEditorInitialized &&
mTextEditor->IsInEditAction()) {
mTextListener->EditAction();
@@ -2141,17 +2145,16 @@ nsTextEditorState::UnbindFromFrame(nsTex
uint32_t start = 0, end = 0;
IgnoredErrorResult rangeRv;
GetSelectionRange(&start, &end, rangeRv);
IgnoredErrorResult dirRv;
nsITextControlFrame::SelectionDirection direction =
GetSelectionDirection(dirRv);
- MOZ_ASSERT(aFrame == mBoundFrame);
SelectionProperties& props = GetSelectionProperties();
props.SetStart(start);
props.SetEnd(end);
props.SetDirection(direction);
HTMLInputElement* number = GetParentNumberControl(aFrame);
if (number) {
// If we are inside a number control, cache the selection on the
// parent control, because this text editor state will be destroyed
@@ -2236,176 +2239,24 @@ nsTextEditorState::UnbindFromFrame(nsTex
NS_LITERAL_STRING("keyup"),
TrustedEventsAtSystemGroupBubble());
}
mTextListener = nullptr;
}
mBoundFrame = nullptr;
- // Clear mRootNode so that we don't unexpectedly notify below.
- nsCOMPtr<Element> rootNode = mRootNode.forget();
// Now that we don't have a frame any more, store the value in the text buffer.
// The only case where we don't do this is if a value transfer is in progress.
if (!mValueTransferInProgress) {
bool success = SetValue(value, eSetValue_Internal);
// TODO Find something better to do if this fails...
NS_ENSURE_TRUE_VOID(success);
}
-
- if (rootNode && mMutationObserver) {
- rootNode->RemoveMutationObserver(mMutationObserver);
- mMutationObserver = nullptr;
- }
-
- // Unbind the anonymous content from the tree.
- // We actually hold a reference to the content nodes so that
- // they're not actually destroyed.
- aFrame->DestroyAnonymousContent(rootNode.forget());
- aFrame->DestroyAnonymousContent(mPlaceholderDiv.forget());
- aFrame->DestroyAnonymousContent(mPreviewDiv.forget());
-}
-
-nsresult
-nsTextEditorState::CreateRootNode()
-{
- MOZ_ASSERT(!mRootNode);
- MOZ_ASSERT(mBoundFrame);
-
- nsIPresShell *shell = mBoundFrame->PresContext()->GetPresShell();
- NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
-
- nsIDocument *doc = shell->GetDocument();
- NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
-
- // Now create a DIV and add it to the anonymous content child list.
- RefPtr<mozilla::dom::NodeInfo> nodeInfo;
- nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::div, nullptr,
- kNameSpaceID_XHTML,
- nsIDOMNode::ELEMENT_NODE);
-
- nsresult rv = NS_NewHTMLElement(getter_AddRefs(mRootNode), nodeInfo.forget(),
- NOT_FROM_PARSER);
- NS_ENSURE_SUCCESS(rv, rv);
-
- mMutationObserver = new nsAnonDivObserver(this);
- mRootNode->AddMutationObserver(mMutationObserver);
- return rv;
-}
-
-nsresult
-nsTextEditorState::InitializeRootNode()
-{
- // Make our root node editable
- mRootNode->SetFlags(NODE_IS_EDITABLE);
-
- // Set the necessary classes on the text control. We use class values
- // instead of a 'style' attribute so that the style comes from a user-agent
- // style sheet and is still applied even if author styles are disabled.
- nsAutoString classValue;
- classValue.AppendLiteral("anonymous-div");
- int32_t wrapCols = GetWrapCols();
- if (wrapCols > 0) {
- classValue.AppendLiteral(" wrap");
- }
- if (!IsSingleLineTextControl()) {
- // We can't just inherit the overflow because setting visible overflow will
- // crash when the number of lines exceeds the height of the textarea and
- // setting -moz-hidden-unscrollable overflow (NS_STYLE_OVERFLOW_CLIP)
- // doesn't paint the caret for some reason.
- const nsStyleDisplay* disp = mBoundFrame->StyleDisplay();
- if (disp->mOverflowX != NS_STYLE_OVERFLOW_VISIBLE &&
- disp->mOverflowX != NS_STYLE_OVERFLOW_CLIP) {
- classValue.AppendLiteral(" inherit-overflow");
- }
- classValue.AppendLiteral(" inherit-scroll-behavior");
- }
- nsresult rv = mRootNode->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
- classValue, false);
- NS_ENSURE_SUCCESS(rv, rv);
-
- return mBoundFrame->UpdateValueDisplay(false);
-}
-
-Element*
-nsTextEditorState::CreateEmptyDivNode()
-{
- MOZ_ASSERT(mBoundFrame);
-
- nsIPresShell *shell = mBoundFrame->PresContext()->GetPresShell();
- MOZ_ASSERT(shell);
-
- nsIDocument *doc = shell->GetDocument();
- MOZ_ASSERT(doc);
-
- nsNodeInfoManager* pNodeInfoManager = doc->NodeInfoManager();
- MOZ_ASSERT(pNodeInfoManager);
-
- Element *element;
-
- // Create a DIV
- RefPtr<mozilla::dom::NodeInfo> nodeInfo;
- nodeInfo = pNodeInfoManager->GetNodeInfo(nsGkAtoms::div, nullptr,
- kNameSpaceID_XHTML,
- nsIDOMNode::ELEMENT_NODE);
-
- element = NS_NewHTMLDivElement(nodeInfo.forget());
-
- // Create the text node for DIV
- RefPtr<nsTextNode> textNode = new nsTextNode(pNodeInfoManager);
- textNode->MarkAsMaybeModifiedFrequently();
-
- element->AppendChildTo(textNode, false);
-
- return element;
-}
-
-nsresult
-nsTextEditorState::CreatePlaceholderNode()
-{
-#ifdef DEBUG
- {
- nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
- if (content) {
- nsAutoString placeholderTxt;
- content->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder,
- placeholderTxt);
- nsContentUtils::RemoveNewlines(placeholderTxt);
- NS_ASSERTION(!placeholderTxt.IsEmpty(), "CreatePlaceholderNode() shouldn't \
-be called if @placeholder is the empty string when trimmed from line breaks");
- }
- }
-#endif // DEBUG
-
- NS_ENSURE_TRUE(!mPlaceholderDiv, NS_ERROR_UNEXPECTED);
-
- // Create a DIV for the placeholder
- // and add it to the anonymous content child list
- mPlaceholderDiv = CreateEmptyDivNode();
-
- // initialize the text
- UpdatePlaceholderText(false);
-
- return NS_OK;
-}
-
-nsresult
-nsTextEditorState::CreatePreviewNode()
-{
- NS_ENSURE_TRUE(!mPreviewDiv, NS_ERROR_UNEXPECTED);
-
- // Create a DIV for the preview
- // and add it to the anonymous content child list
- mPreviewDiv = CreateEmptyDivNode();
-
- mPreviewDiv->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
- NS_LITERAL_STRING("preview-div"), false);
-
- return NS_OK;
}
int32_t
nsTextEditorState::GetMaxLength()
{
nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
nsGenericHTMLElement* element =
nsGenericHTMLElement::FromContentOrNull(content);
@@ -2430,18 +2281,18 @@ nsTextEditorState::GetValue(nsAString& a
// been set to the editor yet.
if (mIsCommittingComposition) {
aValue = mValueBeingSet;
return;
}
if (mTextEditor && mBoundFrame &&
(mEditorInitialized || !IsSingleLineTextControl())) {
- if (aIgnoreWrap && !mCachedValue.IsVoid()) {
- aValue = mCachedValue;
+ if (aIgnoreWrap && !mBoundFrame->CachedValue().IsVoid()) {
+ aValue = mBoundFrame->CachedValue();
return;
}
aValue.Truncate(); // initialize out param
uint32_t flags = (nsIDocumentEncoder::OutputLFLineBreak |
nsIDocumentEncoder::OutputPreformatted |
nsIDocumentEncoder::OutputPersistNBSP |
@@ -2472,19 +2323,19 @@ nsTextEditorState::GetValue(nsAString& a
AutoNoJSAPI nojsapi;
mTextEditor->OutputToString(NS_LITERAL_STRING("text/plain"), flags,
aValue);
}
// Only when the result doesn't include line breaks caused by hard-wrap,
// mCacheValue should cache the value.
if (!(flags & nsIDocumentEncoder::OutputWrap)) {
- const_cast<nsTextEditorState*>(this)->SetValueCache(aValue);
+ mBoundFrame->CacheValue(aValue);
} else {
- const_cast<nsTextEditorState*>(this)->ClearValueCache();
+ mBoundFrame->ClearCachedValue();
}
} else {
if (!mTextCtrlElement->ValueChanged() || !mValue) {
mTextCtrlElement->GetDefaultValueFromContent(aValue);
} else {
aValue = *mValue;
}
}
@@ -2717,17 +2568,17 @@ nsTextEditorState::SetValue(const nsAStr
if (!mBoundFrame) {
return SetValue(newValue, aFlags & eSetValue_Notify);
}
return true;
}
// The new value never includes line breaks caused by hard-wrap.
// So, mCachedValue can always cache the new value.
- if (!SetValueCache(newValue, fallible)) {
+ if (!mBoundFrame->CacheValue(newValue, fallible)) {
return false;
}
}
}
} else {
if (!mValue) {
mValue.emplace();
}
@@ -2766,20 +2617,20 @@ nsTextEditorState::SetValue(const nsAStr
if (IsSelectionCached()) {
SelectionProperties& props = GetSelectionProperties();
props.SetIsDirty();
}
}
// If we've reached the point where the root node has been created, we
// can assume that it's safe to notify.
- ValueWasChanged(!!mRootNode);
+ ValueWasChanged(!!mBoundFrame);
}
- mTextCtrlElement->OnValueChanged(/* aNotify = */ !!mRootNode,
+ mTextCtrlElement->OnValueChanged(/* aNotify = */ !!mBoundFrame,
/* aWasInteractiveUserChange = */ false);
return true;
}
bool
nsTextEditorState::HasNonEmptyValue()
{
@@ -2820,59 +2671,42 @@ nsTextEditorState::InitializeKeyboardEve
void
nsTextEditorState::ValueWasChanged(bool aNotify)
{
UpdateOverlayTextVisibility(aNotify);
}
void
-nsTextEditorState::UpdatePlaceholderText(bool aNotify)
-{
- NS_ASSERTION(mPlaceholderDiv, "This function should not be called if "
- "mPlaceholderDiv isn't set");
-
- // If we don't have a placeholder div, there's nothing to do.
- if (!mPlaceholderDiv)
- return;
-
- nsAutoString placeholderValue;
-
- nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
- content->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder, placeholderValue);
- nsContentUtils::RemoveNewlines(placeholderValue);
- NS_ASSERTION(mPlaceholderDiv->GetFirstChild(), "placeholder div has no child");
- mPlaceholderDiv->GetFirstChild()->SetText(placeholderValue, aNotify);
-}
-
-void
nsTextEditorState::SetPreviewText(const nsAString& aValue, bool aNotify)
{
// If we don't have a preview div, there's nothing to do.
- if (!mPreviewDiv)
+ Element* previewDiv = GetPreviewNode();
+ if (!previewDiv)
return;
nsAutoString previewValue(aValue);
nsContentUtils::RemoveNewlines(previewValue);
- MOZ_ASSERT(mPreviewDiv->GetFirstChild(), "preview div has no child");
- mPreviewDiv->GetFirstChild()->SetText(previewValue, aNotify);
+ MOZ_ASSERT(previewDiv->GetFirstChild(), "preview div has no child");
+ previewDiv->GetFirstChild()->SetText(previewValue, aNotify);
UpdateOverlayTextVisibility(aNotify);
}
void
nsTextEditorState::GetPreviewText(nsAString& aValue)
{
// If we don't have a preview div, there's nothing to do.
- if (!mPreviewDiv)
+ Element* previewDiv = GetPreviewNode();
+ if (!previewDiv)
return;
- MOZ_ASSERT(mPreviewDiv->GetFirstChild(), "preview div has no child");
- const nsTextFragment *text = mPreviewDiv->GetFirstChild()->GetText();
+ MOZ_ASSERT(previewDiv->GetFirstChild(), "preview div has no child");
+ const nsTextFragment *text = previewDiv->GetFirstChild()->GetText();
aValue.Truncate();
text->AppendTo(aValue);
}
void
nsTextEditorState::UpdateOverlayTextVisibility(bool aNotify)
{
@@ -2904,46 +2738,8 @@ nsTextEditorState::HideSelectionIfBlurre
}
}
bool
nsTextEditorState::EditorHasComposition()
{
return mTextEditor && mTextEditor->IsIMEComposing();
}
-
-NS_IMPL_ISUPPORTS(nsAnonDivObserver, nsIMutationObserver)
-
-void
-nsAnonDivObserver::CharacterDataChanged(nsIDocument* aDocument,
- nsIContent* aContent,
- CharacterDataChangeInfo* aInfo)
-{
- mTextEditorState->ClearValueCache();
-}
-
-void
-nsAnonDivObserver::ContentAppended(nsIDocument* aDocument,
- nsIContent* aContainer,
- nsIContent* aFirstNewContent,
- int32_t /* unused */)
-{
- mTextEditorState->ClearValueCache();
-}
-
-void
-nsAnonDivObserver::ContentInserted(nsIDocument* aDocument,
- nsIContent* aContainer,
- nsIContent* aChild,
- int32_t /* unused */)
-{
- mTextEditorState->ClearValueCache();
-}
-
-void
-nsAnonDivObserver::ContentRemoved(nsIDocument* aDocument,
- nsIContent* aContainer,
- nsIContent* aChild,
- int32_t aIndexInContainer,
- nsIContent* aPreviousSibling)
-{
- mTextEditorState->ClearValueCache();
-}
--- a/dom/html/nsTextEditorState.h
+++ b/dom/html/nsTextEditorState.h
@@ -142,20 +142,18 @@ public:
void Traverse(nsCycleCollectionTraversalCallback& cb);
void Unlink();
void PrepareForReuse()
{
Unlink();
mValue.reset();
- mCachedValue.Truncate();
mValueBeingSet.Truncate();
mTextCtrlElement = nullptr;
- MOZ_ASSERT(!mMutationObserver);
}
mozilla::TextEditor* GetTextEditor();
nsISelectionController* GetSelectionController() const;
nsFrameSelection* GetConstFrameSelection();
nsresult BindToFrame(nsTextControlFrame* aFrame);
void UnbindFromFrame(nsTextControlFrame* aFrame);
nsresult PrepareEditor(const nsAString *aValue = nullptr);
@@ -195,29 +193,19 @@ public:
bool HasNonEmptyValue();
// The following methods are for textarea element to use whether default
// value or not.
// XXX We might have to add assertion when it is into editable,
// or reconsider fixing bug 597525 to remove these.
void EmptyValue() { if (mValue) mValue->Truncate(); }
bool IsEmpty() const { return mValue ? mValue->IsEmpty() : true; }
- nsresult CreatePlaceholderNode();
- nsresult CreatePreviewNode();
- mozilla::dom::Element* CreateEmptyDivNode();
-
- mozilla::dom::Element* GetRootNode() {
- return mRootNode;
- }
- mozilla::dom::Element* GetPlaceholderNode() {
- return mPlaceholderDiv;
- }
- mozilla::dom::Element* GetPreviewNode() {
- return mPreviewDiv;
- }
+ mozilla::dom::Element* GetRootNode();
+ mozilla::dom::Element* GetPlaceholderNode();
+ mozilla::dom::Element* GetPreviewNode();
bool IsSingleLineTextControl() const {
return mTextCtrlElement->IsSingleLineTextControl();
}
bool IsTextArea() const {
return mTextCtrlElement->IsTextArea();
}
bool IsPasswordTextControl() const {
@@ -234,54 +222,31 @@ public:
}
void UpdateOverlayTextVisibility(bool aNotify);
// placeholder methods
bool GetPlaceholderVisibility() {
return mPlaceholderVisibility;
}
- void UpdatePlaceholderText(bool aNotify);
// preview methods
void SetPreviewText(const nsAString& aValue, bool aNotify);
void GetPreviewText(nsAString& aValue);
bool GetPreviewVisibility() {
return mPreviewVisibility;
}
/**
* Get the maxlength attribute
* @param aMaxLength the value of the max length attr
* @returns false if attr not defined
*/
int32_t GetMaxLength();
- void ClearValueCache()
- {
- mCachedValue.SetIsVoid(true);
- MOZ_ASSERT(mCachedValue.IsEmpty());
- }
- void SetValueCache(const nsAString& aValue)
- {
- mCachedValue.Assign(aValue);
- MOZ_ASSERT(!mCachedValue.IsVoid());
- }
- MOZ_MUST_USE bool
- SetValueCache(const nsAString& aValue,
- const mozilla::fallible_t& aFallible)
- {
- if (!mCachedValue.Assign(aValue, aFallible)) {
- ClearValueCache();
- return false;
- }
- MOZ_ASSERT(!mCachedValue.IsVoid());
- return true;
- }
-
void HideSelectionIfBlurred();
struct SelectionProperties {
public:
SelectionProperties() : mStart(0), mEnd(0),
mDirection(nsITextControlFrame::eForward) {}
bool IsDefault() const
{
@@ -406,31 +371,29 @@ public:
uint32_t aEnd, mozilla::dom::SelectionMode aSelectMode,
mozilla::ErrorResult& aRv,
const mozilla::Maybe<uint32_t>& aSelectionStart =
mozilla::Nothing(),
const mozilla::Maybe<uint32_t>& aSelectionEnd =
mozilla::Nothing());
void UpdateEditableState(bool aNotify) {
- if (mRootNode) {
- mRootNode->UpdateEditableState(aNotify);
+ if (auto* root = GetRootNode()) {
+ root->UpdateEditableState(aNotify);
}
}
private:
friend class RestoreSelectionState;
// not copy constructible
nsTextEditorState(const nsTextEditorState&);
// not assignable
void operator= (const nsTextEditorState&);
- nsresult CreateRootNode();
-
void ValueWasChanged(bool aNotify);
void DestroyEditor();
void Clear();
nsresult InitializeRootNode();
void FinishedRestoringSelection();
@@ -463,33 +426,22 @@ private:
bool mGuardSet;
};
friend class InitializationGuard;
friend class PrepareEditorEvent;
// The text control element owns this object, and ensures that this object
// has a smaller lifetime.
nsITextControlElement* MOZ_NON_OWNING_REF mTextCtrlElement;
- // mSelCon is non-null while we have an mBoundFrame.
RefPtr<nsTextInputSelectionImpl> mSelCon;
RefPtr<RestoreSelectionState> mRestoringSelection;
RefPtr<mozilla::TextEditor> mTextEditor;
- nsCOMPtr<mozilla::dom::Element> mRootNode;
- nsCOMPtr<mozilla::dom::Element> mPlaceholderDiv;
- nsCOMPtr<mozilla::dom::Element> mPreviewDiv;
nsTextControlFrame* mBoundFrame;
RefPtr<nsTextInputListener> mTextListener;
mozilla::Maybe<nsString> mValue;
- RefPtr<nsAnonDivObserver> mMutationObserver;
- // Cache of the |.value| of <input> or <textarea> element without hard-wrap.
- // If its IsVoid() returns true, it doesn't cache |.value|.
- // Otherwise, it's cached when setting specific value or getting value from
- // TextEditor. Additionally, when contents in the anonymous <div> element
- // is modified, this is cleared.
- nsString mCachedValue;
// mValueBeingSet is available only while SetValue() is requesting to commit
// composition. I.e., this is valid only while mIsCommittingComposition is
// true. While active composition is being committed, GetValue() needs
// the latest value which is set by SetValue(). So, this is cache for that.
nsString mValueBeingSet;
SelectionProperties mSelectionProperties;
bool mEverInited; // Have we ever been initialized?
bool mEditorInitialized;
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -47,16 +47,17 @@
#include "mozilla/dom/HTMLTextAreaElement.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/MathAlgorithms.h"
#include "nsFrameSelection.h"
#define DEFAULT_COLUMN_WIDTH 20
using namespace mozilla;
+using namespace mozilla::dom;
nsIFrame*
NS_NewTextControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) nsTextControlFrame(aContext);
}
NS_IMPL_FRAMEARENA_HELPERS(nsTextControlFrame)
@@ -96,27 +97,44 @@ public:
}
bool EnteredMoreThanOnce() const { return !mFirstEntry; }
private:
nsTextControlFrame &mFrame;
bool mFirstEntry;
};
#endif
+class nsTextControlFrame::nsAnonDivObserver final : public nsStubMutationObserver
+{
+public:
+ explicit nsAnonDivObserver(nsTextControlFrame& aFrame)
+ : mFrame(aFrame) {}
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
+ NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
+ NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
+ NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
+
+private:
+ ~nsAnonDivObserver() {}
+ nsTextControlFrame& mFrame;
+};
+
nsTextControlFrame::nsTextControlFrame(nsStyleContext* aContext)
: nsContainerFrame(aContext, kClassID)
, mFirstBaseline(NS_INTRINSIC_WIDTH_UNKNOWN)
, mEditorHasBeenInitialized(false)
, mIsProcessing(false)
, mUsePlaceholder(false)
, mUsePreview(false)
#ifdef DEBUG
, mInEditorInitialization(false)
#endif
{
+ ClearCachedValue();
}
nsTextControlFrame::~nsTextControlFrame()
{
}
void
nsTextControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
@@ -128,16 +146,26 @@ nsTextControlFrame::DestroyFrom(nsIFrame
// Unbind the text editor state object from the frame. The editor will live
// on, but things like controllers will be released.
nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
NS_ASSERTION(txtCtrl, "Content not a text control element");
txtCtrl->UnbindFromFrame(this);
nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false);
+ if (mMutationObserver) {
+ mRootNode->RemoveMutationObserver(mMutationObserver);
+ mMutationObserver = nullptr;
+ }
+
+ // FIXME(emilio, bug 1400618): Do this after the child frames are destroyed.
+ DestroyAnonymousContent(mRootNode.forget());
+ DestroyAnonymousContent(mPlaceholderDiv.forget());
+ DestroyAnonymousContent(mPreviewDiv.forget());
+
nsContainerFrame::DestroyFrom(aDestructRoot);
}
LogicalSize
nsTextControlFrame::CalcIntrinsicSize(gfxContext* aRenderingContext,
WritingMode aWM,
float aFontSizeInflation) const
{
@@ -307,130 +335,210 @@ nsTextControlFrame::EnsureEditorInitiali
SetSelectionEndPoints(position, position);
}
}
NS_ENSURE_STATE(weakFrame.IsAlive());
return NS_OK;
}
+static already_AddRefed<Element>
+CreateEmptyDiv(const nsTextControlFrame& aOwnerFrame)
+{
+ nsIDocument* doc = aOwnerFrame.PresContext()->Document();
+ RefPtr<mozilla::dom::NodeInfo> nodeInfo =
+ doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::div, nullptr,
+ kNameSpaceID_XHTML,
+ nsIDOMNode::ELEMENT_NODE);
+
+ RefPtr<Element> element = NS_NewHTMLDivElement(nodeInfo.forget());
+ return element.forget();
+}
+
+static already_AddRefed<Element>
+CreateEmptyDivWithTextNode(const nsTextControlFrame& aOwnerFrame)
+{
+ RefPtr<Element> element = CreateEmptyDiv(aOwnerFrame);
+
+ // Create the text node for DIV
+ RefPtr<nsTextNode> textNode =
+ new nsTextNode(element->OwnerDoc()->NodeInfoManager());
+ textNode->MarkAsMaybeModifiedFrequently();
+
+ element->AppendChildTo(textNode, false);
+
+ return element.forget();
+}
+
nsresult
nsTextControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
{
- NS_ASSERTION(mContent, "We should have a content!");
+ MOZ_ASSERT(mContent, "We should have a content!");
AddStateBits(NS_FRAME_INDEPENDENT_SELECTION);
nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
- NS_ASSERTION(txtCtrl, "Content not a text control element");
+ MOZ_ASSERT(txtCtrl, "Content not a text control element");
- // Bind the frame to its text control
- nsresult rv = txtCtrl->BindToFrame(this);
+ nsresult rv = CreateRootNode();
NS_ENSURE_SUCCESS(rv, rv);
- nsIContent* rootNode = txtCtrl->GetRootEditorNode();
- NS_ENSURE_TRUE(rootNode, NS_ERROR_OUT_OF_MEMORY);
-
- if (!aElements.AppendElement(rootNode))
- return NS_ERROR_OUT_OF_MEMORY;
+ // Bind the frame to its text control
+ rv = txtCtrl->BindToFrame(this);
+ NS_ENSURE_SUCCESS(rv, rv);
- // Do we need a placeholder node?
- nsAutoString placeholderTxt;
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder,
- placeholderTxt);
- nsContentUtils::RemoveNewlines(placeholderTxt);
- mUsePlaceholder = !placeholderTxt.IsEmpty();
-
- // Create the placeholder anonymous content if needed.
- if (mUsePlaceholder) {
- Element* placeholderNode = txtCtrl->CreatePlaceholderNode();
- NS_ENSURE_TRUE(placeholderNode, NS_ERROR_OUT_OF_MEMORY);
-
- // Associate ::placeholder pseudo-element with the placeholder node.
- placeholderNode->SetPseudoElementType(CSSPseudoElementType::placeholder);
- aElements.AppendElement(placeholderNode);
-
+ aElements.AppendElement(mRootNode);
+ CreatePlaceholderIfNeeded();
+ if (mPlaceholderDiv) {
if (!IsSingleLineTextControl()) {
// For textareas, UpdateValueDisplay doesn't initialize the visibility
// status of the placeholder because it returns early, so we have to
// do that manually here.
txtCtrl->UpdateOverlayTextVisibility(true);
}
+ aElements.AppendElement(mPlaceholderDiv);
}
-
- mUsePreview = txtCtrl->IsPreviewEnabled();
-
- if (mUsePreview) {
- // Create the preview anonymous content if needed.
- Element* previewNode = txtCtrl->CreatePreviewNode();
- NS_ENSURE_TRUE(previewNode, NS_ERROR_OUT_OF_MEMORY);
-
- aElements.AppendElement(previewNode);
+ CreatePreviewIfNeeded();
+ if (mPreviewDiv) {
+ aElements.AppendElement(mPreviewDiv);
}
rv = UpdateValueDisplay(false);
NS_ENSURE_SUCCESS(rv, rv);
- // textareas are eagerly initialized
- bool initEagerly = !IsSingleLineTextControl();
- if (!initEagerly) {
- // Also, input elements which have a cached selection should get eager
- // editor initialization.
- nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
- NS_ASSERTION(txtCtrl, "Content not a text control element");
- initEagerly = txtCtrl->HasCachedSelection();
+ InitializeEagerlyIfNeeded();
+ return NS_OK;
+}
+
+bool
+nsTextControlFrame::ShouldInitializeEagerly() const
+{
+ // textareas are eagerly initialized.
+ if (!IsSingleLineTextControl()) {
+ return true;
}
- if (!initEagerly) {
- nsCOMPtr<nsIDOMHTMLElement> element = do_QueryInterface(txtCtrl);
- if (element) {
- // so are input text controls with spellcheck=true
- element->GetSpellcheck(&initEagerly);
+
+ // Also, input elements which have a cached selection should get eager
+ // editor initialization.
+ nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+ if (txtCtrl->HasCachedSelection()) {
+ return true;
+ }
+
+ // So do input text controls with spellcheck=true
+ if (auto* htmlElement = nsGenericHTMLElement::FromContent(mContent)) {
+ if (htmlElement->Spellcheck()) {
+ return true;
}
}
- if (initEagerly) {
- NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
- "Someone forgot a script blocker?");
- EditorInitializer* initializer = new EditorInitializer(this);
- SetProperty(TextControlInitializer(), initializer);
- nsContentUtils::AddScriptRunner(initializer);
- }
-
- return NS_OK;
+ return false;
}
void
-nsTextControlFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
- uint32_t aFilter)
+nsTextControlFrame::InitializeEagerlyIfNeeded()
+{
+ MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(),
+ "Someone forgot a script blocker?");
+ if (!ShouldInitializeEagerly()) {
+ return;
+ }
+
+ EditorInitializer* initializer = new EditorInitializer(this);
+ SetProperty(TextControlInitializer(), initializer);
+ nsContentUtils::AddScriptRunner(initializer);
+}
+
+nsresult
+nsTextControlFrame::CreateRootNode()
{
- // This can be called off-main-thread during Servo traversal, so we take care
- // to avoid QI-ing the DOM node.
- nsITextControlElement* txtCtrl = nullptr;
- nsIContent* content = GetContent();
- if (content->IsHTMLElement(nsGkAtoms::input)) {
- txtCtrl = static_cast<HTMLInputElement*>(content);
- } else if (content->IsHTMLElement(nsGkAtoms::textarea)) {
- txtCtrl = static_cast<HTMLTextAreaElement*>(content);
- } else {
- MOZ_CRASH("Unexpected content type for nsTextControlFrame");
+ MOZ_ASSERT(!mRootNode);
+
+ mRootNode = CreateEmptyDiv(*this);
+
+ mMutationObserver = new nsAnonDivObserver(*this);
+ mRootNode->AddMutationObserver(mMutationObserver);
+
+ // Make our root node editable
+ mRootNode->SetFlags(NODE_IS_EDITABLE);
+
+ // Set the necessary classes on the text control. We use class values instead
+ // of a 'style' attribute so that the style comes from a user-agent style
+ // sheet and is still applied even if author styles are disabled.
+ nsAutoString classValue;
+ classValue.AppendLiteral("anonymous-div");
+ if (GetWrapCols() > 0) {
+ classValue.AppendLiteral(" wrap");
}
- nsIContent* root = txtCtrl->GetRootEditorNode();
- if (root) {
- aElements.AppendElement(root);
+ if (!IsSingleLineTextControl()) {
+ // We can't just inherit the overflow because setting visible overflow will
+ // crash when the number of lines exceeds the height of the textarea and
+ // setting -moz-hidden-unscrollable overflow (NS_STYLE_OVERFLOW_CLIP)
+ // doesn't paint the caret for some reason.
+ const nsStyleDisplay* disp = StyleDisplay();
+ if (disp->mOverflowX != NS_STYLE_OVERFLOW_VISIBLE &&
+ disp->mOverflowX != NS_STYLE_OVERFLOW_CLIP) {
+ classValue.AppendLiteral(" inherit-overflow");
+ }
+ classValue.AppendLiteral(" inherit-scroll-behavior");
+ }
+ nsresult rv = mRootNode->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
+ classValue, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return UpdateValueDisplay(false);
+}
+
+void
+nsTextControlFrame::CreatePlaceholderIfNeeded()
+{
+ MOZ_ASSERT(!mPlaceholderDiv);
+
+ // Do we need a placeholder node?
+ nsAutoString placeholderTxt;
+ mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder, placeholderTxt);
+ nsContentUtils::RemoveNewlines(placeholderTxt);
+ mUsePlaceholder = !placeholderTxt.IsEmpty();
+
+ // Create the placeholder anonymous content if needed.
+ if (mUsePlaceholder) {
+ mPlaceholderDiv = CreateEmptyDivWithTextNode(*this);
+ // Associate ::placeholder pseudo-element with the placeholder node.
+ mPlaceholderDiv->SetPseudoElementType(CSSPseudoElementType::placeholder);
+ mPlaceholderDiv->GetFirstChild()->SetText(placeholderTxt, false);
+ }
+}
+
+void
+nsTextControlFrame::CreatePreviewIfNeeded()
+{
+ nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+ mUsePreview = txtCtrl->IsPreviewEnabled();
+
+ if (mUsePreview) {
+ mPreviewDiv = CreateEmptyDivWithTextNode(*this);
+ mPreviewDiv->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
+ NS_LITERAL_STRING("preview-div"), false);
+ }
+}
+
+void
+nsTextControlFrame::AppendAnonymousContentTo(
+ nsTArray<nsIContent*>& aElements,
+ uint32_t aFilter)
+{
+ aElements.AppendElement(mRootNode);
+
+ if (mPlaceholderDiv && !(aFilter & nsIContent::eSkipPlaceholderContent)) {
+ aElements.AppendElement(mPlaceholderDiv);
}
- nsIContent* placeholder = txtCtrl->GetPlaceholderNode();
- if (placeholder && !(aFilter & nsIContent::eSkipPlaceholderContent)) {
- aElements.AppendElement(placeholder);
- }
-
- nsIContent* preview = txtCtrl->GetPreviewNode();
- if (preview) {
- aElements.AppendElement(preview);
+ if (mPreviewDiv) {
+ aElements.AppendElement(mPreviewDiv);
}
}
nscoord
nsTextControlFrame::GetPrefISize(gfxContext* aRenderingContext)
{
nscoord result = 0;
DISPLAY_PREF_WIDTH(this, result);
@@ -1152,41 +1260,38 @@ nsTextControlFrame::SetValueChanged(bool
nsresult
nsTextControlFrame::UpdateValueDisplay(bool aNotify,
bool aBeforeEditorInit,
const nsAString *aValue)
{
if (!IsSingleLineTextControl()) // textareas don't use this
return NS_OK;
- nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
- NS_ASSERTION(txtCtrl, "Content not a text control element");
- nsIContent* rootNode = txtCtrl->GetRootEditorNode();
-
- NS_PRECONDITION(rootNode, "Must have a div content\n");
+ NS_PRECONDITION(mRootNode, "Must have a div content\n");
NS_PRECONDITION(!mEditorHasBeenInitialized,
"Do not call this after editor has been initialized");
- NS_ASSERTION(!mUsePlaceholder || txtCtrl->GetPlaceholderNode(),
+ NS_ASSERTION(!mUsePlaceholder || mPlaceholderDiv,
"A placeholder div must exist");
- nsIContent *textContent = rootNode->GetChildAt(0);
+ nsIContent* textContent = mRootNode->GetChildAt(0);
if (!textContent) {
// Set up a textnode with our value
RefPtr<nsTextNode> textNode =
new nsTextNode(mContent->NodeInfo()->NodeInfoManager());
textNode->MarkAsMaybeModifiedFrequently();
- NS_ASSERTION(textNode, "Must have textcontent!\n");
-
- rootNode->AppendChildTo(textNode, aNotify);
+ mRootNode->AppendChildTo(textNode, aNotify);
textContent = textNode;
}
NS_ENSURE_TRUE(textContent, NS_ERROR_UNEXPECTED);
+ nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+ MOZ_ASSERT(txtCtrl);
+
// Get the current value of the textfield from the content.
nsAutoString value;
if (aValue) {
value = *aValue;
} else {
txtCtrl->GetTextEditorValue(value, true);
}
@@ -1196,17 +1301,17 @@ nsTextControlFrame::UpdateValueDisplay(b
if ((mUsePlaceholder || mUsePreview) && !aBeforeEditorInit)
{
AutoWeakFrame weakFrame(this);
txtCtrl->UpdateOverlayTextVisibility(aNotify);
NS_ENSURE_STATE(weakFrame.IsAlive());
}
if (aBeforeEditorInit && value.IsEmpty()) {
- rootNode->RemoveChildAt(0, true);
+ mRootNode->RemoveChildAt(0, true);
return NS_OK;
}
if (!value.IsEmpty() && IsPasswordTextControl()) {
TextEditRules::FillBufWithPWChars(&value, value.Length());
}
return textContent->SetText(value, aNotify);
}
@@ -1356,8 +1461,50 @@ nsTextControlFrame::EditorInitializer::R
// bug 682684.
if (!mFrame) {
return NS_ERROR_FAILURE;
}
mFrame->FinishedInitializer();
return NS_OK;
}
+
+NS_IMPL_ISUPPORTS(nsTextControlFrame::nsAnonDivObserver, nsIMutationObserver)
+
+void
+nsTextControlFrame::nsAnonDivObserver::CharacterDataChanged(
+ nsIDocument* aDocument,
+ nsIContent* aContent,
+ CharacterDataChangeInfo* aInfo)
+{
+ mFrame.ClearCachedValue();
+}
+
+void
+nsTextControlFrame::nsAnonDivObserver::ContentAppended(
+ nsIDocument* aDocument,
+ nsIContent* aContainer,
+ nsIContent* aFirstNewContent,
+ int32_t /* unused */)
+{
+ mFrame.ClearCachedValue();
+}
+
+void
+nsTextControlFrame::nsAnonDivObserver::ContentInserted(
+ nsIDocument* aDocument,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ int32_t /* unused */)
+{
+ mFrame.ClearCachedValue();
+}
+
+void
+nsTextControlFrame::nsAnonDivObserver::ContentRemoved(
+ nsIDocument* aDocument,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ int32_t aIndexInContainer,
+ nsIContent* aPreviousSibling)
+{
+ mFrame.ClearCachedValue();
+}
--- a/layout/forms/nsTextControlFrame.h
+++ b/layout/forms/nsTextControlFrame.h
@@ -182,16 +182,28 @@ protected:
nsPresContext* aPresContext,
const ReflowInput& aReflowInput,
nsReflowStatus& aStatus,
ReflowOutput& aParentDesiredSize);
public: //for methods who access nsTextControlFrame directly
void SetValueChanged(bool aValueChanged);
+ mozilla::dom::Element* GetRootNode() const {
+ return mRootNode;
+ }
+
+ mozilla::dom::Element* GetPlaceholderNode() const {
+ return mPlaceholderDiv;
+ }
+
+ mozilla::dom::Element* GetPreviewNode() const {
+ return mPreviewDiv;
+ }
+
// called by the focus listener
nsresult MaybeBeginSecureKeyboardInput();
void MaybeEndSecureKeyboardInput();
#define DEFINE_TEXTCTRL_FORWARDER(type, name) \
type name() { \
nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); \
NS_ASSERTION(txtCtrl, "Content not a text control element"); \
@@ -328,17 +340,63 @@ private:
* nsITextControlElement::GetRootEditorNode on our content if you need that.
*/
nsresult GetRootNodeAndInitializeEditor(nsIDOMElement **aRootElement);
void FinishedInitializer() {
DeleteProperty(TextControlInitializer());
}
+ const nsAString& CachedValue() const
+ {
+ return mCachedValue;
+ }
+
+ void ClearCachedValue()
+ {
+ mCachedValue.SetIsVoid(true);
+ }
+
+ void CacheValue(const nsAString& aValue)
+ {
+ mCachedValue.Assign(aValue);
+ }
+
+ MOZ_MUST_USE bool
+ CacheValue(const nsAString& aValue, const mozilla::fallible_t& aFallible)
+ {
+ if (!mCachedValue.Assign(aValue, aFallible)) {
+ ClearCachedValue();
+ return false;
+ }
+ return true;
+ }
+
private:
+ class nsAnonDivObserver;
+
+ nsresult CreateRootNode();
+ void CreatePlaceholderIfNeeded();
+ void CreatePreviewIfNeeded();
+ bool ShouldInitializeEagerly() const;
+ void InitializeEagerlyIfNeeded();
+
+ RefPtr<mozilla::dom::Element> mRootNode;
+ RefPtr<mozilla::dom::Element> mPlaceholderDiv;
+ RefPtr<mozilla::dom::Element> mPreviewDiv;
+ RefPtr<nsAnonDivObserver> mMutationObserver;
+ // Cache of the |.value| of <input> or <textarea> element without hard-wrap.
+ // If its IsVoid() returns true, it doesn't cache |.value|.
+ // Otherwise, it's cached when setting specific value or getting value from
+ // TextEditor. Additionally, when contents in the anonymous <div> element
+ // is modified, this is cleared.
+ //
+ // FIXME(bug 1402545): Consider using an nsAutoString here.
+ nsString mCachedValue;
+
// Our first baseline, or NS_INTRINSIC_WIDTH_UNKNOWN if we have a pending
// Reflow.
nscoord mFirstBaseline;
// these packed bools could instead use the high order bits on mState, saving 4 bytes
bool mEditorHasBeenInitialized;
bool mIsProcessing;
// Keep track if we have asked a placeholder node creation.