Bug 1045270 - Part 1: Cache the selection state for the native anonymous text box inside number controls on the number control itself; r=smaug

This is needed because the text control and its nsTextEditorState will
die together with the text control's frame.
This commit is contained in:
Ehsan Akhgari 2014-07-31 20:25:31 -04:00
parent 6fb9de937d
commit 7667ad74a8
5 changed files with 119 additions and 11 deletions

View File

@ -1128,6 +1128,7 @@ HTMLInputElement::HTMLInputElement(already_AddRefed<mozilla::dom::NodeInfo>& 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);

View File

@ -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<nsITimer> 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,

View File

@ -8,6 +8,7 @@ EXPORTS += [
'HTMLPropertiesCollection.h',
'nsGenericHTMLElement.h',
'nsHTMLDNSPrefetch.h',
'nsTextEditorState.h',
]
EXPORTS.mozilla.dom += [

View File

@ -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

View File

@ -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) :