зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1496242 - Part I, Simplify nsIDateTimeInputArea interface r=mconley,smaug
This patch simplifies the nsIDateTimeInputArea interface, implemented by the datetimebox bindings, to a point that is easier to convert it to dispatch events, by doing the following: - hasBadInput() is re-implemented in C++ in nsIDateTimeControlFrame since C++ needs the return value synchronously. - SetValueFromPicker() and SetPickerState() are avoided completed since they are simply called by HTMLInputElement methods exposed to the frame script. They are avoided by having the frame script access the NAC and call the nsIDateTimeInputArea methods directly. - Merge setEditAttribute() and removeEditAttribute() to updateEditAttributes() which takes no arguments, and have the method access the attribute values by reading the values from <input>. This patch is a scaled-down version of the patch proposed in bug 1456833. The event approach is only usable in UA Widget version of datetimebox because there is no way to avoid leaking events to the document without Shadow DOM. Differential Revision: https://phabricator.services.mozilla.com/D9056 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
f75b36a5b7
Коммит
3856b5e646
|
@ -2220,30 +2220,13 @@ void HTMLInputElement::GetDateTimeInputBoxValue(DateTimeValue& aValue)
|
|||
aValue = *mDateTimeInputBoxValue;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLInputElement::UpdateDateTimeInputBox(const DateTimeValue& aValue)
|
||||
Element* HTMLInputElement::GetDateTimeBoxElement()
|
||||
{
|
||||
if (NS_WARN_IF(!IsDateTimeInputType(mType))) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
|
||||
if (frame) {
|
||||
frame->SetValueFromPicker(aValue);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HTMLInputElement::SetDateTimePickerState(bool aOpen)
|
||||
{
|
||||
if (NS_WARN_IF(!IsDateTimeInputType(mType))) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
|
||||
if (frame) {
|
||||
frame->SetPickerState(aOpen);
|
||||
return frame->GetInputAreaContent()->AsElement();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -846,8 +846,12 @@ public:
|
|||
* know the current state of the picker or to update the input box on changes.
|
||||
*/
|
||||
void GetDateTimeInputBoxValue(DateTimeValue& aValue);
|
||||
void UpdateDateTimeInputBox(const DateTimeValue& aValue);
|
||||
void SetDateTimePickerState(bool aOpen);
|
||||
|
||||
/*
|
||||
* This allows chrome JavaScript to dispatch event to the inner datetimebox
|
||||
* anonymous element or access nsIDateTimeInputArea implmentation.
|
||||
*/
|
||||
Element* GetDateTimeBoxElement();
|
||||
|
||||
/*
|
||||
* The following functions are called from datetime input box XBL to control
|
||||
|
|
|
@ -35,25 +35,15 @@ interface nsIDateTimeInputArea : nsISupports
|
|||
*/
|
||||
void blurInnerTextBox();
|
||||
|
||||
/**
|
||||
* Called from DOM/Layout to know whether the current entered value is valid.
|
||||
*/
|
||||
boolean hasBadInput();
|
||||
|
||||
/**
|
||||
* Set the current state of the picker, true if it's opened, false otherwise.
|
||||
*/
|
||||
void setPickerState(in boolean isOpen);
|
||||
|
||||
/**
|
||||
* Set the attribute of the inner text boxes. Only "tabindex", "readonly",
|
||||
* and "disabled" are allowed.
|
||||
* Update the attribute of the inner text boxes by copying the attribute value
|
||||
* from the input. Only values set to "tabindex", "readonly",
|
||||
* and "disabled" attributes are copied.
|
||||
*/
|
||||
void setEditAttribute(in AString name, in AString value);
|
||||
|
||||
/**
|
||||
* Remove the attribute of the inner text boxes. Only "tabindex", "readonly",
|
||||
* and "disabled" are allowed.
|
||||
*/
|
||||
void removeEditAttribute(in AString name);
|
||||
void updateEditAttributes();
|
||||
};
|
||||
|
|
|
@ -242,10 +242,7 @@ partial interface HTMLInputElement {
|
|||
DateTimeValue getDateTimeInputBoxValue();
|
||||
|
||||
[Pref="dom.forms.datetime", ChromeOnly]
|
||||
void updateDateTimeInputBox(optional DateTimeValue value);
|
||||
|
||||
[Pref="dom.forms.datetime", ChromeOnly]
|
||||
void setDateTimePickerState(boolean open);
|
||||
readonly attribute Element? dateTimeBoxElement;
|
||||
|
||||
[Pref="dom.forms.datetime", ChromeOnly,
|
||||
BinaryName="getMinimumAsDouble"]
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "nsContentCreatorFunctions.h"
|
||||
#include "mozilla/dom/HTMLInputElement.h"
|
||||
#include "mozilla/dom/MutationEventBinding.h"
|
||||
#include "nsDOMTokenList.h"
|
||||
#include "nsNodeInfoManager.h"
|
||||
#include "nsIDateTimeInputArea.h"
|
||||
#include "nsIObserverService.h"
|
||||
|
@ -73,47 +74,6 @@ nsDateTimeControlFrame::OnMinMaxStepAttrChanged()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDateTimeControlFrame::SetValueFromPicker(const DateTimeValue& aValue)
|
||||
{
|
||||
nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
|
||||
do_QueryInterface(mInputAreaContent);
|
||||
if (inputAreaContent) {
|
||||
AutoJSAPI api;
|
||||
if (!api.Init(mContent->OwnerDoc()->GetScopeObject())) {
|
||||
return;
|
||||
}
|
||||
|
||||
JSObject* wrapper = mContent->GetWrapper();
|
||||
if (!wrapper) {
|
||||
return;
|
||||
}
|
||||
|
||||
JSObject* scope = xpc::GetXBLScope(api.cx(), wrapper);
|
||||
AutoJSAPI jsapi;
|
||||
if (!scope || !jsapi.Init(scope)) {
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> jsValue(jsapi.cx());
|
||||
if (!ToJSValue(jsapi.cx(), aValue, &jsValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
inputAreaContent->SetValueFromPicker(jsValue);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDateTimeControlFrame::SetPickerState(bool aOpen)
|
||||
{
|
||||
nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
|
||||
do_QueryInterface(mInputAreaContent);
|
||||
if (inputAreaContent) {
|
||||
inputAreaContent->SetPickerState(aOpen);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDateTimeControlFrame::HandleFocusEvent()
|
||||
{
|
||||
|
@ -137,15 +97,26 @@ nsDateTimeControlFrame::HandleBlurEvent()
|
|||
bool
|
||||
nsDateTimeControlFrame::HasBadInput()
|
||||
{
|
||||
nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
|
||||
do_QueryInterface(mInputAreaContent);
|
||||
// Incomplete field does not imply bad input.
|
||||
Element* editWrapperElement = mInputAreaContent->GetComposedDoc()->
|
||||
GetAnonymousElementByAttribute(mInputAreaContent,
|
||||
nsGkAtoms::anonid, NS_LITERAL_STRING("edit-wrapper"));
|
||||
|
||||
bool result = false;
|
||||
if (inputAreaContent) {
|
||||
inputAreaContent->HasBadInput(&result);
|
||||
for (Element* child = editWrapperElement->GetFirstElementChild(); child; child = child->GetNextElementSibling()) {
|
||||
if (child->ClassList()->Contains(NS_LITERAL_STRING("datetime-edit-field"))) {
|
||||
nsAutoString value;
|
||||
child->GetAttr(kNameSpaceID_None, nsGkAtoms::value, value);
|
||||
if (value.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
// All fields are available but input element's value is empty implies
|
||||
// it has been sanitized.
|
||||
nsAutoString value;
|
||||
HTMLInputElement::FromNode(mContent)->GetValue(value, CallerType::System);
|
||||
return value.IsEmpty();
|
||||
}
|
||||
|
||||
nscoord
|
||||
|
@ -338,30 +309,6 @@ nsDateTimeControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
|
|||
NS_TrustedNewXULElement(getter_AddRefs(mInputAreaContent), nodeInfo.forget());
|
||||
aElements.AppendElement(mInputAreaContent);
|
||||
|
||||
nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
|
||||
do_QueryInterface(mInputAreaContent);
|
||||
if (inputAreaContent) {
|
||||
// Propogate our tabindex.
|
||||
nsAutoString tabIndexStr;
|
||||
if (mContent->AsElement()->GetAttr(kNameSpaceID_None,
|
||||
nsGkAtoms::tabindex,
|
||||
tabIndexStr)) {
|
||||
inputAreaContent->SetEditAttribute(NS_LITERAL_STRING("tabindex"),
|
||||
tabIndexStr);
|
||||
}
|
||||
|
||||
// Propagate our readonly state.
|
||||
nsAutoString readonly;
|
||||
if (mContent->AsElement()->GetAttr(kNameSpaceID_None,
|
||||
nsGkAtoms::readonly,
|
||||
readonly)) {
|
||||
inputAreaContent->SetEditAttribute(NS_LITERAL_STRING("readonly"),
|
||||
readonly);
|
||||
}
|
||||
|
||||
SyncDisabledState();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -383,14 +330,7 @@ nsDateTimeControlFrame::SyncDisabledState()
|
|||
if (!inputAreaContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
EventStates eventStates = mContent->AsElement()->State();
|
||||
if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) {
|
||||
inputAreaContent->SetEditAttribute(NS_LITERAL_STRING("disabled"),
|
||||
EmptyString());
|
||||
} else {
|
||||
inputAreaContent->RemoveEditAttribute(NS_LITERAL_STRING("disabled"));
|
||||
}
|
||||
inputAreaContent->UpdateEditAttributes();
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -421,20 +361,8 @@ nsDateTimeControlFrame::AttributeChanged(int32_t aNameSpaceID,
|
|||
&nsIDateTimeInputArea::NotifyInputElementValueChanged));
|
||||
}
|
||||
} else {
|
||||
if (aModType == MutationEvent_Binding::REMOVAL) {
|
||||
if (inputAreaContent) {
|
||||
nsAtomString name(aAttribute);
|
||||
inputAreaContent->RemoveEditAttribute(name);
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(aModType == MutationEvent_Binding::ADDITION ||
|
||||
aModType == MutationEvent_Binding::MODIFICATION);
|
||||
if (inputAreaContent) {
|
||||
nsAtomString name(aAttribute);
|
||||
nsAutoString value;
|
||||
contentAsInputElem->GetAttr(aNameSpaceID, aAttribute, value);
|
||||
inputAreaContent->SetEditAttribute(name, value);
|
||||
}
|
||||
if (inputAreaContent) {
|
||||
inputAreaContent->UpdateEditAttributes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,12 +74,12 @@ public:
|
|||
nsresult AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute,
|
||||
int32_t aModType) override;
|
||||
|
||||
nsIContent* GetInputAreaContent() const { return mInputAreaContent; }
|
||||
|
||||
void OnValueChanged();
|
||||
void OnMinMaxStepAttrChanged();
|
||||
void SetValueFromPicker(const DateTimeValue& aValue);
|
||||
void HandleFocusEvent();
|
||||
void HandleBlurEvent();
|
||||
void SetPickerState(bool aOpen);
|
||||
bool HasBadInput();
|
||||
|
||||
private:
|
||||
|
|
|
@ -30,7 +30,9 @@ class DateTimePickerChild extends ActorChild {
|
|||
*/
|
||||
close() {
|
||||
this.removeListeners();
|
||||
this._inputElement.setDateTimePickerState(false);
|
||||
if (this._inputElement.dateTimeBoxElement instanceof Ci.nsIDateTimeInputArea) {
|
||||
this._inputElement.dateTimeBoxElement.setPickerState(false);
|
||||
}
|
||||
this._inputElement = null;
|
||||
}
|
||||
|
||||
|
@ -89,7 +91,9 @@ class DateTimePickerChild extends ActorChild {
|
|||
break;
|
||||
}
|
||||
case "FormDateTime:PickerValueChanged": {
|
||||
this._inputElement.updateDateTimeInputBox(aMessage.data);
|
||||
if (this._inputElement.dateTimeBoxElement instanceof Ci.nsIDateTimeInputArea) {
|
||||
this._inputElement.dateTimeBoxElement.setValueFromPicker(aMessage.data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -118,7 +122,11 @@ class DateTimePickerChild extends ActorChild {
|
|||
}
|
||||
|
||||
this._inputElement = aEvent.originalTarget;
|
||||
this._inputElement.setDateTimePickerState(true);
|
||||
|
||||
if (this._inputElement.dateTimeBoxElement instanceof Ci.nsIDateTimeInputArea) {
|
||||
this._inputElement.dateTimeBoxElement.setPickerState(true);
|
||||
}
|
||||
|
||||
this.addListeners();
|
||||
|
||||
let value = this._inputElement.getDateTimeInputBoxValue();
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
this.mYearPageUpDownInterval = 10;
|
||||
|
||||
this.buildEditFields();
|
||||
this.updateEditAttributes();
|
||||
|
||||
if (this.mInputElement.value) {
|
||||
this.setFieldsFromInputValue();
|
||||
|
@ -385,7 +386,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
aField.setAttribute("rawValue", value);
|
||||
aField.setAttribute("value", value);
|
||||
|
||||
// Display formatted value based on locale.
|
||||
let minDigits = aField.getAttribute("mindigits");
|
||||
|
@ -1080,7 +1081,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
aField.setAttribute("rawValue", value);
|
||||
aField.setAttribute("value", value);
|
||||
|
||||
let minDigits = aField.getAttribute("mindigits");
|
||||
let formatted = value.toLocaleString(this.mLocales, {
|
||||
|
@ -1120,6 +1121,7 @@
|
|||
}
|
||||
|
||||
this.mDayPeriodField.textContent = aValue;
|
||||
this.mDayPeriodField.setAttribute("value", aValue);
|
||||
this.updateResetButtonVisibility();
|
||||
]]>
|
||||
</body>
|
||||
|
@ -1274,7 +1276,6 @@
|
|||
});
|
||||
this.mInputElement.removeEventListener("click", this,
|
||||
{ mozSystemGroup: true });
|
||||
|
||||
this.mInputElement = null;
|
||||
]]>
|
||||
</destructor>
|
||||
|
@ -1324,6 +1325,12 @@
|
|||
field.readOnly = this.mInputElement.readOnly;
|
||||
field.setAttribute("aria-label", aLabel);
|
||||
|
||||
// Used to store the non-formatted value, cleared when value is
|
||||
// cleared.
|
||||
// nsDateTimeControlFrame::HasBadInput() will read this to decide
|
||||
// if the input has value.
|
||||
field.setAttribute("value", "");
|
||||
|
||||
if (aIsNumeric) {
|
||||
field.classList.add("numeric");
|
||||
// Maximum value allowed.
|
||||
|
@ -1335,9 +1342,6 @@
|
|||
// Used to store what the user has already typed in the field,
|
||||
// cleared when value is cleared and when field is blurred.
|
||||
field.setAttribute("typeBuffer", "");
|
||||
// Used to store the non-formatted number, clered when value is
|
||||
// cleared.
|
||||
field.setAttribute("rawValue", "");
|
||||
// Minimum digits to display, padded with leading 0s.
|
||||
field.setAttribute("mindigits", aMinDigits);
|
||||
// Maximum length for the field, will be advance to the next field
|
||||
|
@ -1389,6 +1393,7 @@
|
|||
child.classList.contains("datetime-edit-field")) {
|
||||
this.mLastFocusedField = child;
|
||||
child.focus();
|
||||
this.log("focused");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1444,25 +1449,6 @@
|
|||
</body>
|
||||
</method>
|
||||
|
||||
<method name="hasBadInput">
|
||||
<body>
|
||||
<![CDATA[
|
||||
// Incomplete field does not imply bad input.
|
||||
if (this.isAnyFieldEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// All fields are available but input element's value is empty implies
|
||||
// it has been sanitized.
|
||||
if (!this.mInputElement.value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="advanceToNextField">
|
||||
<parameter name="aReverse"/>
|
||||
<body>
|
||||
|
@ -1500,17 +1486,10 @@
|
|||
</body>
|
||||
</method>
|
||||
|
||||
<method name="setEditAttribute">
|
||||
<parameter name="aName"/>
|
||||
<parameter name="aValue"/>
|
||||
<method name="updateEditAttributes">
|
||||
<body>
|
||||
<![CDATA[
|
||||
this.log("setAttribute: " + aName + "=" + aValue);
|
||||
|
||||
if (aName != "tabindex" && aName != "disabled" &&
|
||||
aName != "readonly") {
|
||||
return;
|
||||
}
|
||||
this.log("updateEditAttributes");
|
||||
|
||||
let editRoot =
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "edit-wrapper");
|
||||
|
@ -1518,54 +1497,18 @@
|
|||
for (let child = editRoot.firstChild; child; child = child.nextSibling) {
|
||||
if ((child instanceof HTMLSpanElement) &&
|
||||
child.classList.contains("datetime-edit-field")) {
|
||||
// "disabled" and "readonly" must be set as attributes because they
|
||||
// are not defined properties of HTMLSpanElement, and the stylesheet
|
||||
// checks the literal string attribute values.
|
||||
child.setAttribute("disabled", this.mInputElement.disabled);
|
||||
child.setAttribute("readonly", this.mInputElement.readOnly);
|
||||
|
||||
switch (aName) {
|
||||
case "tabindex":
|
||||
child.setAttribute(aName, aValue);
|
||||
break;
|
||||
case "disabled": {
|
||||
let value = this.mInputElement.disabled;
|
||||
child.setAttribute("disabled", value);
|
||||
child.disabled = value;
|
||||
break;
|
||||
}
|
||||
case "readonly": {
|
||||
let value = this.mInputElement.readOnly;
|
||||
child.setAttribute("readonly", value);
|
||||
child.readOnly = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
// Set property as well for convenience.
|
||||
child.disabled = this.mInputElement.disabled;
|
||||
child.readOnly = this.mInputElement.readOnly;
|
||||
|
||||
<method name="removeEditAttribute">
|
||||
<parameter name="aName"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
this.log("removeAttribute: " + aName);
|
||||
|
||||
if (aName != "tabindex" && aName != "disabled" &&
|
||||
aName != "readonly") {
|
||||
return;
|
||||
}
|
||||
|
||||
let editRoot =
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "edit-wrapper");
|
||||
|
||||
for (let child = editRoot.firstChild; child; child = child.nextSibling) {
|
||||
if ((child instanceof HTMLSpanElement) &&
|
||||
child.classList.contains("datetime-edit-field")) {
|
||||
child.removeAttribute(aName);
|
||||
// Update property as well.
|
||||
if (aName == "readonly") {
|
||||
child.readOnly = false;
|
||||
} else if (aName == "disabled") {
|
||||
child.disabled = false;
|
||||
}
|
||||
// tabIndex works on all elements
|
||||
child.tabIndex = this.mInputElement.tabIndex;
|
||||
}
|
||||
}
|
||||
]]>
|
||||
|
@ -1587,7 +1530,7 @@
|
|||
return undefined;
|
||||
}
|
||||
|
||||
let value = aField.getAttribute("rawValue");
|
||||
let value = aField.getAttribute("value");
|
||||
// Avoid returning 0 when field is empty.
|
||||
return (this.isEmpty(value) ? undefined : Number(value));
|
||||
]]>
|
||||
|
@ -1599,9 +1542,9 @@
|
|||
<body>
|
||||
<![CDATA[
|
||||
aField.textContent = aField.placeholder;
|
||||
aField.setAttribute("value", "");
|
||||
if (aField.classList.contains("numeric")) {
|
||||
aField.setAttribute("typeBuffer", "");
|
||||
aField.setAttribute("rawValue", "");
|
||||
}
|
||||
this.updateResetButtonVisibility();
|
||||
]]>
|
||||
|
|
Загрузка…
Ссылка в новой задаче