diff --git a/content/html/content/src/HTMLInputElement.cpp b/content/html/content/src/HTMLInputElement.cpp
index a4ea3bdc2e0d..25918ec8f1ef 100644
--- a/content/html/content/src/HTMLInputElement.cpp
+++ b/content/html/content/src/HTMLInputElement.cpp
@@ -1128,6 +1128,7 @@ HTMLInputElement::HTMLInputElement(already_AddRefed& aNo
, mNumberControlSpinnerIsSpinning(false)
, mNumberControlSpinnerSpinsUp(false)
, mPickerRunning(false)
+ , mSelectionCached(true)
{
// We are in a type=text so we now we currenty need a nsTextEditorState.
mInputData.mState = new nsTextEditorState(this);
diff --git a/content/html/content/src/HTMLInputElement.h b/content/html/content/src/HTMLInputElement.h
index 4ae24b770f3c..ddc8a2ade518 100644
--- a/content/html/content/src/HTMLInputElement.h
+++ b/content/html/content/src/HTMLInputElement.h
@@ -22,12 +22,12 @@
#include "nsIContentPrefService2.h"
#include "mozilla/Decimal.h"
#include "nsContentUtils.h"
+#include "nsTextEditorState.h"
class nsDOMFileList;
class nsIRadioGroupContainer;
class nsIRadioGroupVisitor;
class nsIRadioVisitor;
-class nsTextEditorState;
namespace mozilla {
@@ -250,6 +250,28 @@ public:
void MaybeLoadImage();
+ void SetSelectionProperties(const nsTextEditorState::SelectionProperties& aProps)
+ {
+ MOZ_ASSERT(mType == NS_FORM_INPUT_NUMBER);
+ mSelectionCached = true;
+ mSelectionProperties = aProps;
+ }
+ bool IsSelectionCached() const
+ {
+ MOZ_ASSERT(mType == NS_FORM_INPUT_NUMBER);
+ return mSelectionCached;
+ }
+ void ClearSelectionCached()
+ {
+ MOZ_ASSERT(mType == NS_FORM_INPUT_NUMBER);
+ mSelectionCached = false;
+ }
+ nsTextEditorState::SelectionProperties& GetSelectionProperties()
+ {
+ MOZ_ASSERT(mType == NS_FORM_INPUT_NUMBER);
+ return mSelectionProperties;
+ }
+
// nsITimerCallback
NS_DECL_NSITIMERCALLBACK
@@ -1255,6 +1277,13 @@ protected:
*/
nsCOMPtr mProgressTimer;
+ /**
+ * The selection properties cache for number controls. This is needed because
+ * the number controls don't recycle their text field, so the normal cache in
+ * nsTextEditorState cannot do its job.
+ */
+ nsTextEditorState::SelectionProperties mSelectionProperties;
+
// Step scale factor values, for input types that have one.
static const Decimal kStepScaleFactorDate;
static const Decimal kStepScaleFactorNumberRange;
@@ -1295,6 +1324,7 @@ protected:
bool mNumberControlSpinnerIsSpinning : 1;
bool mNumberControlSpinnerSpinsUp : 1;
bool mPickerRunning : 1;
+ bool mSelectionCached : 1;
private:
static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
diff --git a/content/html/content/src/moz.build b/content/html/content/src/moz.build
index a793993d9894..1e45b0a13e87 100644
--- a/content/html/content/src/moz.build
+++ b/content/html/content/src/moz.build
@@ -8,6 +8,7 @@ EXPORTS += [
'HTMLPropertiesCollection.h',
'nsGenericHTMLElement.h',
'nsHTMLDNSPrefetch.h',
+ 'nsTextEditorState.h',
]
EXPORTS.mozilla.dom += [
diff --git a/content/html/content/src/nsTextEditorState.cpp b/content/html/content/src/nsTextEditorState.cpp
index 39e8d74c0029..c161258c5090 100644
--- a/content/html/content/src/nsTextEditorState.cpp
+++ b/content/html/content/src/nsTextEditorState.cpp
@@ -43,6 +43,8 @@
#include "nsIController.h"
#include "mozilla/TextEvents.h"
#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/HTMLInputElement.h"
+#include "nsNumberControlFrame.h"
using namespace mozilla;
using namespace mozilla::dom;
@@ -1423,7 +1425,8 @@ nsTextEditorState::PrepareEditor(const nsAString *aValue)
newEditor->AddEditorObserver(mTextListener);
// Restore our selection after being bound to a new frame
- if (mSelectionCached) {
+ HTMLInputElement* number = GetParentNumberControl(mBoundFrame);
+ if (number ? number->IsSelectionCached() : mSelectionCached) {
if (mRestoringSelection) // paranoia
mRestoringSelection->Revoke();
mRestoringSelection = new RestoreSelectionState(this, mBoundFrame);
@@ -1433,11 +1436,66 @@ nsTextEditorState::PrepareEditor(const nsAString *aValue)
}
// The selection cache is no longer going to be valid
- mSelectionCached = false;
+ if (number) {
+ number->ClearSelectionCached();
+ } else {
+ mSelectionCached = false;
+ }
return rv;
}
+bool
+nsTextEditorState::IsSelectionCached() const
+{
+ if (mBoundFrame) {
+ HTMLInputElement* number = GetParentNumberControl(mBoundFrame);
+ if (number) {
+ return number->IsSelectionCached();
+ }
+ }
+ return mSelectionCached;
+}
+
+nsTextEditorState::SelectionProperties&
+nsTextEditorState::GetSelectionProperties()
+{
+ if (mBoundFrame) {
+ HTMLInputElement* number = GetParentNumberControl(mBoundFrame);
+ if (number) {
+ return number->GetSelectionProperties();
+ }
+ }
+ return mSelectionProperties;
+}
+
+HTMLInputElement*
+nsTextEditorState::GetParentNumberControl(nsFrame* aFrame) const
+{
+ MOZ_ASSERT(aFrame);
+ nsIContent* content = aFrame->GetContent();
+ MOZ_ASSERT(content);
+ nsIContent* parent = content->GetParent();
+ if (!parent) {
+ return nullptr;
+ }
+ nsIContent* parentOfParent = parent->GetParent();
+ if (!parentOfParent) {
+ return nullptr;
+ }
+ HTMLInputElement* input = HTMLInputElement::FromContent(parentOfParent);
+ if (input) {
+ // This function might be called during frame reconstruction as a result
+ // of changing the input control's type from number to something else. In
+ // that situation, the type of the control has changed, but its frame has
+ // not been reconstructed yet. So we need to check the type of the input
+ // control in addition to the type of the frame.
+ return (input->GetType() == NS_FORM_INPUT_NUMBER) ? input : nullptr;
+ }
+
+ return nullptr;
+}
+
void
nsTextEditorState::DestroyEditor()
{
@@ -1478,10 +1536,21 @@ nsTextEditorState::UnbindFromFrame(nsTextControlFrame* aFrame)
// GetSelectionRange before calling DestroyEditor, and only if
// mEditorInitialized indicates that we actually have an editor available.
if (mEditorInitialized) {
- mBoundFrame->GetSelectionRange(&mSelectionProperties.mStart,
- &mSelectionProperties.mEnd,
- &mSelectionProperties.mDirection);
- mSelectionCached = true;
+ 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
+ // together with the native anonymous text control.
+ SelectionProperties props;
+ mBoundFrame->GetSelectionRange(&props.mStart, &props.mEnd,
+ &props.mDirection);
+ number->SetSelectionProperties(props);
+ } else {
+ mBoundFrame->GetSelectionRange(&mSelectionProperties.mStart,
+ &mSelectionProperties.mEnd,
+ &mSelectionProperties.mDirection);
+ mSelectionCached = true;
+ }
}
// Destroy our editor
diff --git a/content/html/content/src/nsTextEditorState.h b/content/html/content/src/nsTextEditorState.h
index 9a2fcc58080d..3e4f99292f68 100644
--- a/content/html/content/src/nsTextEditorState.h
+++ b/content/html/content/src/nsTextEditorState.h
@@ -23,6 +23,13 @@ class nsISelectionController;
class nsFrameSelection;
class nsIEditor;
class nsITextControlElement;
+class nsFrame;
+
+namespace mozilla {
+namespace dom {
+class HTMLInputElement;
+}
+}
/**
* nsTextEditorState is a class which is responsible for managing the state of
@@ -203,10 +210,8 @@ public:
nsITextControlFrame::SelectionDirection mDirection;
};
- bool IsSelectionCached() const { return mSelectionCached; }
- SelectionProperties& GetSelectionProperties() {
- return mSelectionProperties;
- }
+ bool IsSelectionCached() const;
+ SelectionProperties& GetSelectionProperties();
void WillInitEagerly() { mSelectionRestoreEagerInit = true; }
bool HasNeverInitializedBefore() const { return !mEverInited; }
@@ -235,6 +240,8 @@ private:
void FinishedRestoringSelection() { mRestoringSelection = nullptr; }
+ mozilla::dom::HTMLInputElement* GetParentNumberControl(nsFrame* aFrame) const;
+
class InitializationGuard {
public:
explicit InitializationGuard(nsTextEditorState& aState) :