Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4)

Side by Side Diff: Source/core/html/forms/InputType.cpp

Issue 136783006: Upgrade stepUp()/stepDown() implementation to match spec. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Code review improvements Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « Source/core/html/forms/InputType.h ('k') | Source/core/html/forms/StepRange.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org) 3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2001 Dirk Mueller (mueller@kde.org) 4 * (C) 2001 Dirk Mueller (mueller@kde.org)
5 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All r ights reserved. 5 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All r ights reserved.
6 * (C) 2006 Alexey Proskuryakov (ap@nypop.com) 6 * (C) 2006 Alexey Proskuryakov (ap@nypop.com)
7 * Copyright (C) 2007 Samuel Weinig (sam@webkit.org) 7 * Copyright (C) 2007 Samuel Weinig (sam@webkit.org)
8 * Copyright (C) 2009, 2010, 2011, 2012 Google Inc. All rights reserved. 8 * Copyright (C) 2009, 2010, 2011, 2012 Google Inc. All rights reserved.
9 * Copyright (C) 2012 Samsung Electronics. All rights reserved. 9 * Copyright (C) 2012 Samsung Electronics. All rights reserved.
10 * 10 *
(...skipping 776 matching lines...) Expand 10 before | Expand all | Expand 10 after
787 unsigned InputType::height() const 787 unsigned InputType::height() const
788 { 788 {
789 return 0; 789 return 0;
790 } 790 }
791 791
792 unsigned InputType::width() const 792 unsigned InputType::width() const
793 { 793 {
794 return 0; 794 return 0;
795 } 795 }
796 796
797 void InputType::applyStep(int count, AnyStepHandling anyStepHandling, TextFieldE ventBehavior eventBehavior, ExceptionState& exceptionState) 797 void InputType::applyStep(const Decimal& current, int count, AnyStepHandling any StepHandling, TextFieldEventBehavior eventBehavior, ExceptionState& exceptionSta te)
798 { 798 {
799 StepRange stepRange(createStepRange(anyStepHandling)); 799 StepRange stepRange(createStepRange(anyStepHandling));
800 if (!stepRange.hasStep()) { 800 if (!stepRange.hasStep()) {
801 exceptionState.throwDOMException(InvalidStateError, "This form element d oes not have an allowed value step."); 801 exceptionState.throwDOMException(InvalidStateError, "This form element d oes not have an allowed value step.");
802 return; 802 return;
803 } 803 }
804 804
805 const Decimal current = parseToNumberOrNaN(element().value()); 805 const Decimal step = stepRange.step();
806 if (!current.isFinite()) { 806 const AtomicString& stepString = element().fastGetAttribute(stepAttr);
807 exceptionState.throwDOMException(InvalidStateError, ExceptionMessages::n otAFiniteNumber(current, "form element's current value")); 807 if (!equalIgnoringCase(stepString, "any") && stepRange.stepMismatch(current) ) {
808 return; 808 // Snap-to-step / clamping steps
809 // If the current value is not matched to step value:
810 // - The value should be the larger matched value nearest to 0 if count > 0
811 // e.g. <input type=number value=3 min=-100 step=3> -> 5
812 // - The value should be the smaller matched value nearest to 0 if count < 0
813 // e.g. <input type=number value=3 min=-100 step=3> -> 2
814 //
815
816 ASSERT(!step.isZero());
817 Decimal newValue;
818 const Decimal base = stepRange.stepBase();
819 if (count < 0)
820 newValue = base + ((current - base) / step).floor() * step;
821 else if (count > 0)
822 newValue = base + ((current - base) / step).ceiling() * step;
823 else
824 newValue = current;
825
826 if (newValue < stepRange.minimum())
827 newValue = stepRange.minimum();
828 if (newValue > stepRange.maximum())
829 newValue = stepRange.maximum();
830
831 setValueAsDecimal(newValue, count == 1 || count == -1 ? DispatchChangeEv ent : DispatchNoEvent, IGNORE_EXCEPTION);
832 if (count > 1) {
833 applyStep(newValue, count - 1, AnyIsDefaultStep, DispatchChangeEvent , IGNORE_EXCEPTION);
tkent 2014/02/05 08:33:24 Please add EventQueueScope in this function. setVa
sof 2014/02/05 08:42:51 Thanks, done.
834 return;
835 }
836 if (count < -1) {
837 applyStep(newValue, count + 1, AnyIsDefaultStep, DispatchChangeEvent , IGNORE_EXCEPTION);
838 return;
839 }
840 } else {
841 Decimal newValue = current + stepRange.step() * count;
842
843 if (!equalIgnoringCase(stepString, "any"))
844 newValue = stepRange.alignValueForStep(current, newValue);
845
846 if (newValue > stepRange.maximum())
847 newValue = newValue - stepRange.step();
848 else if (newValue < stepRange.minimum())
849 newValue = newValue + stepRange.step();
850
851 setValueAsDecimal(newValue, eventBehavior, exceptionState);
809 } 852 }
810 Decimal newValue = current + stepRange.step() * count;
811 if (!newValue.isFinite()) {
812 exceptionState.throwDOMException(InvalidStateError, ExceptionMessages::n otAFiniteNumber(newValue, "form element's new value"));
813 return;
814 }
815
816 const Decimal acceptableErrorValue = stepRange.acceptableError();
817 if (newValue - stepRange.minimum() < -acceptableErrorValue) {
818 exceptionState.throwDOMException(InvalidStateError, "The form element's new value (" + newValue.toString() + ") would be lower than the minimum (" + ste pRange.minimum().toString() + "), and snapping to the minimum would exceed the a mount of acceptible error.");
819 return;
820 }
821 if (newValue < stepRange.minimum())
822 newValue = stepRange.minimum();
823
824 const AtomicString& stepString = element().fastGetAttribute(stepAttr);
825 if (!equalIgnoringCase(stepString, "any"))
826 newValue = stepRange.alignValueForStep(current, newValue);
827
828 if (newValue - stepRange.maximum() > acceptableErrorValue) {
829 exceptionState.throwDOMException(InvalidStateError, "The form element's new value (" + newValue.toString() + ") would be higher than the maximum (" + st epRange.maximum().toString() + "), and snapping to the maximum would exceed the amount of acceptible error.");
830 return;
831 }
832 if (newValue > stepRange.maximum())
833 newValue = stepRange.maximum();
834
835 setValueAsDecimal(newValue, eventBehavior, exceptionState);
836
837 if (AXObjectCache* cache = element().document().existingAXObjectCache()) 853 if (AXObjectCache* cache = element().document().existingAXObjectCache())
838 cache->postNotification(&element(), AXObjectCache::AXValueChanged, true) ; 854 cache->postNotification(&element(), AXObjectCache::AXValueChanged, true) ;
839 } 855 }
840 856
841 bool InputType::getAllowedValueStep(Decimal* step) const 857 bool InputType::getAllowedValueStep(Decimal* step) const
842 { 858 {
843 StepRange stepRange(createStepRange(RejectAny)); 859 StepRange stepRange(createStepRange(RejectAny));
844 *step = stepRange.step(); 860 *step = stepRange.step();
845 return stepRange.hasStep(); 861 return stepRange.hasStep();
846 } 862 }
847 863
848 StepRange InputType::createStepRange(AnyStepHandling) const 864 StepRange InputType::createStepRange(AnyStepHandling) const
849 { 865 {
850 ASSERT_NOT_REACHED(); 866 ASSERT_NOT_REACHED();
851 return StepRange(); 867 return StepRange();
852 } 868 }
853 869
854 void InputType::stepUp(int n, ExceptionState& exceptionState) 870 void InputType::stepUp(int n, ExceptionState& exceptionState)
855 { 871 {
856 if (!isSteppable()) { 872 if (!isSteppable()) {
857 exceptionState.throwDOMException(InvalidStateError, "This form element i s not steppable."); 873 exceptionState.throwDOMException(InvalidStateError, "This form element i s not steppable.");
858 return; 874 return;
859 } 875 }
860 applyStep(n, RejectAny, DispatchNoEvent, exceptionState); 876 const Decimal current = parseToNumber(element().value(), 0);
877 applyStep(current, n, RejectAny, DispatchNoEvent, exceptionState);
861 } 878 }
862 879
863 void InputType::stepUpFromRenderer(int n) 880 void InputType::stepUpFromRenderer(int n)
864 { 881 {
865 // The differences from stepUp()/stepDown(): 882 // The only difference from stepUp()/stepDown() is the extra treatment
883 // of the current value before applying the step:
866 // 884 //
867 // Difference 1: the current value
868 // If the current value is not a number, including empty, the current value is assumed as 0. 885 // If the current value is not a number, including empty, the current value is assumed as 0.
869 // * If 0 is in-range, and matches to step value 886 // * If 0 is in-range, and matches to step value
870 // - The value should be the +step if n > 0 887 // - The value should be the +step if n > 0
871 // - The value should be the -step if n < 0 888 // - The value should be the -step if n < 0
872 // If -step or +step is out of range, new value should be 0. 889 // If -step or +step is out of range, new value should be 0.
873 // * If 0 is smaller than the minimum value 890 // * If 0 is smaller than the minimum value
874 // - The value should be the minimum value for any n 891 // - The value should be the minimum value for any n
875 // * If 0 is larger than the maximum value 892 // * If 0 is larger than the maximum value
876 // - The value should be the maximum value for any n 893 // - The value should be the maximum value for any n
877 // * If 0 is in-range, but not matched to step value 894 // * If 0 is in-range, but not matched to step value
878 // - The value should be the larger matched value nearest to 0 if n > 0 895 // - The value should be the larger matched value nearest to 0 if n > 0
879 // e.g. <input type=number min=-100 step=3> -> 2 896 // e.g. <input type=number min=-100 step=3> -> 2
880 // - The value should be the smaler matched value nearest to 0 if n < 0 897 // - The value should be the smaler matched value nearest to 0 if n < 0
881 // e.g. <input type=number min=-100 step=3> -> -1 898 // e.g. <input type=number min=-100 step=3> -> -1
882 // As for date/datetime-local/month/time/week types, the current value is assumed as "the current local date/time". 899 // As for date/datetime-local/month/time/week types, the current value is assumed as "the current local date/time".
883 // As for datetime type, the current value is assumed as "the current date /time in UTC". 900 // As for datetime type, the current value is assumed as "the current date /time in UTC".
884 // If the current value is smaller than the minimum value: 901 // If the current value is smaller than the minimum value:
885 // - The value should be the minimum value if n > 0 902 // - The value should be the minimum value if n > 0
886 // - Nothing should happen if n < 0 903 // - Nothing should happen if n < 0
887 // If the current value is larger than the maximum value: 904 // If the current value is larger than the maximum value:
888 // - The value should be the maximum value if n < 0 905 // - The value should be the maximum value if n < 0
889 // - Nothing should happen if n > 0 906 // - Nothing should happen if n > 0
890 // 907 //
891 // Difference 2: clamping steps
892 // If the current value is not matched to step value:
893 // - The value should be the larger matched value nearest to 0 if n > 0
894 // e.g. <input type=number value=3 min=-100 step=3> -> 5
895 // - The value should be the smaler matched value nearest to 0 if n < 0
896 // e.g. <input type=number value=3 min=-100 step=3> -> 2
897 //
898 // n is assumed as -n if step < 0. 908 // n is assumed as -n if step < 0.
899 909
900 ASSERT(isSteppable()); 910 ASSERT(isSteppable());
901 if (!isSteppable()) 911 if (!isSteppable())
902 return; 912 return;
903 ASSERT(n); 913 ASSERT(n);
904 if (!n) 914 if (!n)
905 return; 915 return;
906 916
907 StepRange stepRange(createStepRange(AnyIsDefaultStep)); 917 StepRange stepRange(createStepRange(AnyIsDefaultStep));
908 918
909 // FIXME: Not any changes after stepping, even if it is an invalid value, ma y be better. 919 // FIXME: Not any changes after stepping, even if it is an invalid value, ma y be better.
910 // (e.g. Stepping-up for <input type="number" value="foo" step="any" /> => " foo") 920 // (e.g. Stepping-up for <input type="number" value="foo" step="any" /> => " foo")
911 if (!stepRange.hasStep()) 921 if (!stepRange.hasStep())
912 return; 922 return;
913 923
914 EventQueueScope scope; 924 EventQueueScope scope;
915 const Decimal step = stepRange.step(); 925 const Decimal step = stepRange.step();
916 926
917 int sign; 927 int sign;
918 if (step > 0) 928 if (step > 0)
919 sign = n; 929 sign = n;
920 else if (step < 0) 930 else if (step < 0)
921 sign = -n; 931 sign = -n;
922 else 932 else
923 sign = 0; 933 sign = 0;
924 934
925 String currentStringValue = element().value(); 935 Decimal current = parseToNumberOrNaN(element().value());
926 Decimal current = parseToNumberOrNaN(currentStringValue);
927 if (!current.isFinite()) { 936 if (!current.isFinite()) {
928 current = defaultValueForStepUp(); 937 current = defaultValueForStepUp();
929 const Decimal nextDiff = step * n; 938 const Decimal nextDiff = step * n;
930 if (current < stepRange.minimum() - nextDiff) 939 if (current < stepRange.minimum() - nextDiff)
931 current = stepRange.minimum() - nextDiff; 940 current = stepRange.minimum() - nextDiff;
932 if (current > stepRange.maximum() - nextDiff) 941 if (current > stepRange.maximum() - nextDiff)
933 current = stepRange.maximum() - nextDiff; 942 current = stepRange.maximum() - nextDiff;
934 setValueAsDecimal(current, DispatchNoEvent, IGNORE_EXCEPTION); 943 setValueAsDecimal(current, DispatchNoEvent, IGNORE_EXCEPTION);
935 } 944 }
936 if ((sign > 0 && current < stepRange.minimum()) || (sign < 0 && current > st epRange.maximum())) { 945 if ((sign > 0 && current < stepRange.minimum()) || (sign < 0 && current > st epRange.maximum())) {
937 setValueAsDecimal(sign > 0 ? stepRange.minimum() : stepRange.maximum(), DispatchChangeEvent, IGNORE_EXCEPTION); 946 setValueAsDecimal(sign > 0 ? stepRange.minimum() : stepRange.maximum(), DispatchChangeEvent, IGNORE_EXCEPTION);
938 } else { 947 return;
939 if (stepMismatch(element().value())) {
940 ASSERT(!step.isZero());
941 const Decimal base = stepRange.stepBase();
942 Decimal newValue;
943 if (sign < 0)
944 newValue = base + ((current - base) / step).floor() * step;
945 else if (sign > 0)
946 newValue = base + ((current - base) / step).ceiling() * step;
947 else
948 newValue = current;
949
950 if (newValue < stepRange.minimum())
951 newValue = stepRange.minimum();
952 if (newValue > stepRange.maximum())
953 newValue = stepRange.maximum();
954
955 setValueAsDecimal(newValue, n == 1 || n == -1 ? DispatchChangeEvent : DispatchNoEvent, IGNORE_EXCEPTION);
956 if (n > 1)
957 applyStep(n - 1, AnyIsDefaultStep, DispatchChangeEvent, IGNORE_E XCEPTION);
958 else if (n < -1)
959 applyStep(n + 1, AnyIsDefaultStep, DispatchChangeEvent, IGNORE_E XCEPTION);
960 } else {
961 applyStep(n, AnyIsDefaultStep, DispatchChangeEvent, IGNORE_EXCEPTION );
962 }
963 } 948 }
949 applyStep(current, n, AnyIsDefaultStep, DispatchChangeEvent, IGNORE_EXCEPTIO N);
964 } 950 }
965 951
966 void InputType::countUsageIfVisible(UseCounter::Feature feature) const 952 void InputType::countUsageIfVisible(UseCounter::Feature feature) const
967 { 953 {
968 if (RenderStyle* style = element().renderStyle()) { 954 if (RenderStyle* style = element().renderStyle()) {
969 if (style->visibility() != HIDDEN) 955 if (style->visibility() != HIDDEN)
970 UseCounter::count(element().document(), feature); 956 UseCounter::count(element().document(), feature);
971 } 957 }
972 } 958 }
973 959
974 Decimal InputType::findStepBase(const Decimal& defaultValue) const 960 Decimal InputType::findStepBase(const Decimal& defaultValue) const
975 { 961 {
976 Decimal stepBase = parseToNumber(element().fastGetAttribute(minAttr), Decima l::nan()); 962 Decimal stepBase = parseToNumber(element().fastGetAttribute(minAttr), Decima l::nan());
977 if (!stepBase.isFinite()) 963 if (!stepBase.isFinite())
978 stepBase = parseToNumber(element().fastGetAttribute(valueAttr), defaultV alue); 964 stepBase = parseToNumber(element().fastGetAttribute(valueAttr), defaultV alue);
979 return stepBase; 965 return stepBase;
980 } 966 }
981 967
982 StepRange InputType::createStepRange(AnyStepHandling anyStepHandling, const Deci mal& stepBaseDefault, const Decimal& minimumDefault, const Decimal& maximumDefau lt, const StepRange::StepDescription& stepDescription) const 968 StepRange InputType::createStepRange(AnyStepHandling anyStepHandling, const Deci mal& stepBaseDefault, const Decimal& minimumDefault, const Decimal& maximumDefau lt, const StepRange::StepDescription& stepDescription) const
983 { 969 {
984 const Decimal stepBase = findStepBase(stepBaseDefault); 970 const Decimal stepBase = findStepBase(stepBaseDefault);
985 const Decimal minimum = parseToNumber(element().fastGetAttribute(minAttr), m inimumDefault); 971 const Decimal minimum = parseToNumber(element().fastGetAttribute(minAttr), m inimumDefault);
986 const Decimal maximum = parseToNumber(element().fastGetAttribute(maxAttr), m aximumDefault); 972 const Decimal maximum = parseToNumber(element().fastGetAttribute(maxAttr), m aximumDefault);
987 const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element().fastGetAttribute(stepAttr)); 973 const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element().fastGetAttribute(stepAttr));
988 return StepRange(stepBase, minimum, maximum, step, stepDescription); 974 return StepRange(stepBase, minimum, maximum, step, stepDescription);
989 } 975 }
990 976
991 } // namespace WebCore 977 } // namespace WebCore
OLDNEW
« no previous file with comments | « Source/core/html/forms/InputType.h ('k') | Source/core/html/forms/StepRange.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698