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

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: applyStep(): add missing EventQueueScope 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 EventQueueScope scope;
806 if (!current.isFinite()) { 806 const Decimal step = stepRange.step();
807 exceptionState.throwDOMException(InvalidStateError, ExceptionMessages::n otAFiniteNumber(current, "form element's current value"));
808 return;
809 }
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 807
824 const AtomicString& stepString = element().fastGetAttribute(stepAttr); 808 const AtomicString& stepString = element().fastGetAttribute(stepAttr);
825 if (!equalIgnoringCase(stepString, "any")) 809 if (!equalIgnoringCase(stepString, "any") && stepRange.stepMismatch(current) ) {
826 newValue = stepRange.alignValueForStep(current, newValue); 810 // Snap-to-step / clamping steps
811 // If the current value is not matched to step value:
812 // - The value should be the larger matched value nearest to 0 if count > 0
813 // e.g. <input type=number value=3 min=-100 step=3> -> 5
814 // - The value should be the smaller matched value nearest to 0 if count < 0
815 // e.g. <input type=number value=3 min=-100 step=3> -> 2
816 //
827 817
828 if (newValue - stepRange.maximum() > acceptableErrorValue) { 818 ASSERT(!step.isZero());
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."); 819 Decimal newValue;
830 return; 820 const Decimal base = stepRange.stepBase();
821 if (count < 0)
822 newValue = base + ((current - base) / step).floor() * step;
823 else if (count > 0)
824 newValue = base + ((current - base) / step).ceiling() * step;
825 else
826 newValue = current;
827
828 if (newValue < stepRange.minimum())
829 newValue = stepRange.minimum();
830 if (newValue > stepRange.maximum())
831 newValue = stepRange.maximum();
832
833 setValueAsDecimal(newValue, count == 1 || count == -1 ? DispatchChangeEv ent : DispatchNoEvent, IGNORE_EXCEPTION);
834 if (count > 1) {
835 applyStep(newValue, count - 1, AnyIsDefaultStep, DispatchChangeEvent , IGNORE_EXCEPTION);
836 return;
837 }
838 if (count < -1) {
839 applyStep(newValue, count + 1, AnyIsDefaultStep, DispatchChangeEvent , IGNORE_EXCEPTION);
840 return;
841 }
842 } else {
843 Decimal newValue = current + stepRange.step() * count;
844
845 if (!equalIgnoringCase(stepString, "any"))
846 newValue = stepRange.alignValueForStep(current, newValue);
847
848 if (newValue > stepRange.maximum())
849 newValue = newValue - stepRange.step();
850 else if (newValue < stepRange.minimum())
851 newValue = newValue + stepRange.step();
852
853 setValueAsDecimal(newValue, eventBehavior, exceptionState);
831 } 854 }
832 if (newValue > stepRange.maximum())
833 newValue = stepRange.maximum();
834
835 setValueAsDecimal(newValue, eventBehavior, exceptionState);
836
837 if (AXObjectCache* cache = element().document().existingAXObjectCache()) 855 if (AXObjectCache* cache = element().document().existingAXObjectCache())
838 cache->postNotification(&element(), AXObjectCache::AXValueChanged, true) ; 856 cache->postNotification(&element(), AXObjectCache::AXValueChanged, true) ;
839 } 857 }
840 858
841 bool InputType::getAllowedValueStep(Decimal* step) const 859 bool InputType::getAllowedValueStep(Decimal* step) const
842 { 860 {
843 StepRange stepRange(createStepRange(RejectAny)); 861 StepRange stepRange(createStepRange(RejectAny));
844 *step = stepRange.step(); 862 *step = stepRange.step();
845 return stepRange.hasStep(); 863 return stepRange.hasStep();
846 } 864 }
847 865
848 StepRange InputType::createStepRange(AnyStepHandling) const 866 StepRange InputType::createStepRange(AnyStepHandling) const
849 { 867 {
850 ASSERT_NOT_REACHED(); 868 ASSERT_NOT_REACHED();
851 return StepRange(); 869 return StepRange();
852 } 870 }
853 871
854 void InputType::stepUp(int n, ExceptionState& exceptionState) 872 void InputType::stepUp(int n, ExceptionState& exceptionState)
855 { 873 {
856 if (!isSteppable()) { 874 if (!isSteppable()) {
857 exceptionState.throwDOMException(InvalidStateError, "This form element i s not steppable."); 875 exceptionState.throwDOMException(InvalidStateError, "This form element i s not steppable.");
858 return; 876 return;
859 } 877 }
860 applyStep(n, RejectAny, DispatchNoEvent, exceptionState); 878 const Decimal current = parseToNumber(element().value(), 0);
879 applyStep(current, n, RejectAny, DispatchNoEvent, exceptionState);
861 } 880 }
862 881
863 void InputType::stepUpFromRenderer(int n) 882 void InputType::stepUpFromRenderer(int n)
864 { 883 {
865 // The differences from stepUp()/stepDown(): 884 // The only difference from stepUp()/stepDown() is the extra treatment
885 // of the current value before applying the step:
866 // 886 //
867 // Difference 1: the current value
868 // If the current value is not a number, including empty, the current value is assumed as 0. 887 // 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 888 // * If 0 is in-range, and matches to step value
870 // - The value should be the +step if n > 0 889 // - The value should be the +step if n > 0
871 // - The value should be the -step if n < 0 890 // - The value should be the -step if n < 0
872 // If -step or +step is out of range, new value should be 0. 891 // If -step or +step is out of range, new value should be 0.
873 // * If 0 is smaller than the minimum value 892 // * If 0 is smaller than the minimum value
874 // - The value should be the minimum value for any n 893 // - The value should be the minimum value for any n
875 // * If 0 is larger than the maximum value 894 // * If 0 is larger than the maximum value
876 // - The value should be the maximum value for any n 895 // - The value should be the maximum value for any n
877 // * If 0 is in-range, but not matched to step value 896 // * 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 897 // - 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 898 // 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 899 // - 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 900 // 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". 901 // 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". 902 // 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: 903 // If the current value is smaller than the minimum value:
885 // - The value should be the minimum value if n > 0 904 // - The value should be the minimum value if n > 0
886 // - Nothing should happen if n < 0 905 // - Nothing should happen if n < 0
887 // If the current value is larger than the maximum value: 906 // If the current value is larger than the maximum value:
888 // - The value should be the maximum value if n < 0 907 // - The value should be the maximum value if n < 0
889 // - Nothing should happen if n > 0 908 // - Nothing should happen if n > 0
890 // 909 //
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. 910 // n is assumed as -n if step < 0.
899 911
900 ASSERT(isSteppable()); 912 ASSERT(isSteppable());
901 if (!isSteppable()) 913 if (!isSteppable())
902 return; 914 return;
903 ASSERT(n); 915 ASSERT(n);
904 if (!n) 916 if (!n)
905 return; 917 return;
906 918
907 StepRange stepRange(createStepRange(AnyIsDefaultStep)); 919 StepRange stepRange(createStepRange(AnyIsDefaultStep));
908 920
909 // FIXME: Not any changes after stepping, even if it is an invalid value, ma y be better. 921 // 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") 922 // (e.g. Stepping-up for <input type="number" value="foo" step="any" /> => " foo")
911 if (!stepRange.hasStep()) 923 if (!stepRange.hasStep())
912 return; 924 return;
913 925
914 EventQueueScope scope; 926 EventQueueScope scope;
915 const Decimal step = stepRange.step(); 927 const Decimal step = stepRange.step();
916 928
917 int sign; 929 int sign;
918 if (step > 0) 930 if (step > 0)
919 sign = n; 931 sign = n;
920 else if (step < 0) 932 else if (step < 0)
921 sign = -n; 933 sign = -n;
922 else 934 else
923 sign = 0; 935 sign = 0;
924 936
925 String currentStringValue = element().value(); 937 Decimal current = parseToNumberOrNaN(element().value());
926 Decimal current = parseToNumberOrNaN(currentStringValue);
927 if (!current.isFinite()) { 938 if (!current.isFinite()) {
928 current = defaultValueForStepUp(); 939 current = defaultValueForStepUp();
929 const Decimal nextDiff = step * n; 940 const Decimal nextDiff = step * n;
930 if (current < stepRange.minimum() - nextDiff) 941 if (current < stepRange.minimum() - nextDiff)
931 current = stepRange.minimum() - nextDiff; 942 current = stepRange.minimum() - nextDiff;
932 if (current > stepRange.maximum() - nextDiff) 943 if (current > stepRange.maximum() - nextDiff)
933 current = stepRange.maximum() - nextDiff; 944 current = stepRange.maximum() - nextDiff;
934 setValueAsDecimal(current, DispatchNoEvent, IGNORE_EXCEPTION); 945 setValueAsDecimal(current, DispatchNoEvent, IGNORE_EXCEPTION);
935 } 946 }
936 if ((sign > 0 && current < stepRange.minimum()) || (sign < 0 && current > st epRange.maximum())) { 947 if ((sign > 0 && current < stepRange.minimum()) || (sign < 0 && current > st epRange.maximum())) {
937 setValueAsDecimal(sign > 0 ? stepRange.minimum() : stepRange.maximum(), DispatchChangeEvent, IGNORE_EXCEPTION); 948 setValueAsDecimal(sign > 0 ? stepRange.minimum() : stepRange.maximum(), DispatchChangeEvent, IGNORE_EXCEPTION);
938 } else { 949 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 } 950 }
951 applyStep(current, n, AnyIsDefaultStep, DispatchChangeEvent, IGNORE_EXCEPTIO N);
964 } 952 }
965 953
966 void InputType::countUsageIfVisible(UseCounter::Feature feature) const 954 void InputType::countUsageIfVisible(UseCounter::Feature feature) const
967 { 955 {
968 if (RenderStyle* style = element().renderStyle()) { 956 if (RenderStyle* style = element().renderStyle()) {
969 if (style->visibility() != HIDDEN) 957 if (style->visibility() != HIDDEN)
970 UseCounter::count(element().document(), feature); 958 UseCounter::count(element().document(), feature);
971 } 959 }
972 } 960 }
973 961
974 Decimal InputType::findStepBase(const Decimal& defaultValue) const 962 Decimal InputType::findStepBase(const Decimal& defaultValue) const
975 { 963 {
976 Decimal stepBase = parseToNumber(element().fastGetAttribute(minAttr), Decima l::nan()); 964 Decimal stepBase = parseToNumber(element().fastGetAttribute(minAttr), Decima l::nan());
977 if (!stepBase.isFinite()) 965 if (!stepBase.isFinite())
978 stepBase = parseToNumber(element().fastGetAttribute(valueAttr), defaultV alue); 966 stepBase = parseToNumber(element().fastGetAttribute(valueAttr), defaultV alue);
979 return stepBase; 967 return stepBase;
980 } 968 }
981 969
982 StepRange InputType::createStepRange(AnyStepHandling anyStepHandling, const Deci mal& stepBaseDefault, const Decimal& minimumDefault, const Decimal& maximumDefau lt, const StepRange::StepDescription& stepDescription) const 970 StepRange InputType::createStepRange(AnyStepHandling anyStepHandling, const Deci mal& stepBaseDefault, const Decimal& minimumDefault, const Decimal& maximumDefau lt, const StepRange::StepDescription& stepDescription) const
983 { 971 {
984 const Decimal stepBase = findStepBase(stepBaseDefault); 972 const Decimal stepBase = findStepBase(stepBaseDefault);
985 const Decimal minimum = parseToNumber(element().fastGetAttribute(minAttr), m inimumDefault); 973 const Decimal minimum = parseToNumber(element().fastGetAttribute(minAttr), m inimumDefault);
986 const Decimal maximum = parseToNumber(element().fastGetAttribute(maxAttr), m aximumDefault); 974 const Decimal maximum = parseToNumber(element().fastGetAttribute(maxAttr), m aximumDefault);
987 const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element().fastGetAttribute(stepAttr)); 975 const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element().fastGetAttribute(stepAttr));
988 return StepRange(stepBase, minimum, maximum, step, stepDescription); 976 return StepRange(stepBase, minimum, maximum, step, stepDescription);
989 } 977 }
990 978
991 } // namespace WebCore 979 } // 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