diff options
author | miguelg@chromium.org <miguelg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-07 21:14:07 +0000 |
---|---|---|
committer | miguelg@chromium.org <miguelg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-07 21:14:07 +0000 |
commit | 1ff42797b769734346eb363a63a2766bf52a641f (patch) | |
tree | 95a4077b34af1fda4a3e1ffd38d81b5b7cf5df9d | |
parent | a06f53ede6b24f5c8b521059bb0d1594787c75d5 (diff) | |
download | chromium_src-1ff42797b769734346eb363a63a2766bf52a641f.zip chromium_src-1ff42797b769734346eb363a63a2766bf52a641f.tar.gz chromium_src-1ff42797b769734346eb363a63a2766bf52a641f.tar.bz2 |
Move Android Date/Time parsing to the renderer (C++ and ICU) instead of the current parsing that happens in the browser (on java using java libraries)
BUG=143540
Review URL: https://chromiumcodereview.appspot.com/12191005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@181364 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | content/browser/android/date_time_chooser_android.cc | 35 | ||||
-rw-r--r-- | content/browser/android/date_time_chooser_android.h | 11 | ||||
-rw-r--r-- | content/browser/renderer_host/ime_adapter_android.cc | 20 | ||||
-rw-r--r-- | content/browser/renderer_host/ime_adapter_android.h | 4 | ||||
-rw-r--r-- | content/browser/web_contents/web_contents_impl.cc | 7 | ||||
-rw-r--r-- | content/browser/web_contents/web_contents_impl.h | 4 | ||||
-rw-r--r-- | content/common/view_messages.h | 18 | ||||
-rw-r--r-- | content/content_renderer.gypi | 2 | ||||
-rw-r--r-- | content/content_tests.gypi | 1 | ||||
-rw-r--r-- | content/public/android/java/src/org/chromium/content/browser/DateTimeChooserAndroid.java | 25 | ||||
-rw-r--r-- | content/public/android/java/src/org/chromium/content/browser/InputDialogContainer.java | 142 | ||||
-rw-r--r-- | content/renderer/date_time_formatter.cc | 193 | ||||
-rw-r--r-- | content/renderer/date_time_formatter.h | 65 | ||||
-rw-r--r-- | content/renderer/date_time_formatter_unittest.cc | 149 | ||||
-rw-r--r-- | content/renderer/renderer_date_time_picker.cc | 53 | ||||
-rw-r--r-- | content/renderer/renderer_date_time_picker.h | 5 |
16 files changed, 572 insertions, 162 deletions
diff --git a/content/browser/android/date_time_chooser_android.cc b/content/browser/android/date_time_chooser_android.cc index b5851b7..95bf2f3 100644 --- a/content/browser/android/date_time_chooser_android.cc +++ b/content/browser/android/date_time_chooser_android.cc @@ -23,7 +23,8 @@ class DateTimeChooserAndroid::DateTimeIPCSender : public: explicit DateTimeIPCSender(RenderViewHost* sender); virtual ~DateTimeIPCSender() {} - void ReplaceDateTime(string16 text); + void ReplaceDateTime(int dialog_type, + int year, int month, int day, int hour, int minute, int second); void CancelDialog(); private: @@ -36,8 +37,17 @@ DateTimeChooserAndroid::DateTimeIPCSender::DateTimeIPCSender( } void DateTimeChooserAndroid::DateTimeIPCSender::ReplaceDateTime( - string16 text) { - Send(new ViewMsg_ReplaceDateTime(routing_id(), text)); + int dialog_type, + int year, int month, int day, int hour, int minute, int second) { + ViewHostMsg_DateTimeDialogValue_Params value; + value.year = year; + value.month = month; + value.day = day; + value.hour = hour; + value.minute = minute; + value.second = second; + value.dialog_type = dialog_type; + Send(new ViewMsg_ReplaceDateTime(routing_id(), value)); } void DateTimeChooserAndroid::DateTimeIPCSender::CancelDialog() { @@ -65,25 +75,26 @@ void DateTimeChooserAndroid::InitializeDateInputTypes( } void DateTimeChooserAndroid::ReplaceDateTime( - JNIEnv* env, jobject, jstring text) { - string16 text16 = ConvertJavaStringToUTF16(env, text); - communicator_->ReplaceDateTime(text16); + JNIEnv* env, jobject, int dialog_type, + int year, int month, int day, int hour, int minute, int second) { + sender_->ReplaceDateTime( + dialog_type, year, month, day, hour, minute, second); } void DateTimeChooserAndroid::CancelDialog(JNIEnv* env, jobject) { - communicator_->CancelDialog(); + sender_->CancelDialog(); } void DateTimeChooserAndroid::ShowDialog( ContentViewCore* content, RenderViewHost* sender, - int type, const std::string& value) { - communicator_.reset(new DateTimeIPCSender(sender)); + int type, int year, int month, int day, + int hour, int minute, int second) { + sender_.reset(new DateTimeIPCSender(sender)); JNIEnv* env = AttachCurrentThread(); - base::android::ScopedJavaLocalRef<jstring> java_value = - ConvertUTF8ToJavaString(env, value); j_date_time_chooser_.Reset(Java_DateTimeChooserAndroid_createDateTimeChooser( env, content->GetJavaObject().obj(), - reinterpret_cast<intptr_t>(this), java_value.obj(), type)); + reinterpret_cast<intptr_t>(this), + type, year, month, day, hour, minute, second)); } // ---------------------------------------------------------------------------- diff --git a/content/browser/android/date_time_chooser_android.h b/content/browser/android/date_time_chooser_android.h index 787b0ca..c33c5c4 100644 --- a/content/browser/android/date_time_chooser_android.h +++ b/content/browser/android/date_time_chooser_android.h @@ -24,10 +24,13 @@ class DateTimeChooserAndroid { // DateTimeChooser implementation: void ShowDialog(ContentViewCore* content, RenderViewHost* sender, - int type, const std::string& value); + int type, int year, int month, int day, + int hour, int minute, int second); - // Replaces the current value with the one passed in text. - void ReplaceDateTime(JNIEnv* env, jobject, jstring text); + // Replaces the current value with the one passed the different fields + void ReplaceDateTime(JNIEnv* env, jobject, jint dialog_type, + jint year, jint month, jint day, + jint hour, jint minute, jint second); // Closes the dialog without propagating any changes. void CancelDialog(JNIEnv* env, jobject); @@ -42,7 +45,7 @@ class DateTimeChooserAndroid { private: class DateTimeIPCSender; - scoped_ptr<DateTimeIPCSender> communicator_; + scoped_ptr<DateTimeIPCSender> sender_; base::android::ScopedJavaGlobalRef<jobject> j_date_time_chooser_; DISALLOW_COPY_AND_ASSIGN(DateTimeChooserAndroid); diff --git a/content/browser/renderer_host/ime_adapter_android.cc b/content/browser/renderer_host/ime_adapter_android.cc index e431438..ee622bd 100644 --- a/content/browser/renderer_host/ime_adapter_android.cc +++ b/content/browser/renderer_host/ime_adapter_android.cc @@ -172,26 +172,6 @@ void ImeAdapterAndroid::CancelComposition() { Java_ImeAdapter_cancelComposition(AttachCurrentThread(), java_ime_adapter_); } -void ImeAdapterAndroid::ReplaceDateTime(JNIEnv* env, jobject, jstring text) { - RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From( - rwhva_->GetRenderWidgetHost()); - if (!rwhi) - return; - - string16 text16 = ConvertJavaStringToUTF16(env, text); - rwhi->Send(new ViewMsg_ReplaceDateTime(rwhi->GetRoutingID(), text16)); -} - - -void ImeAdapterAndroid::CancelDialog(JNIEnv* env, jobject) { - RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From( - rwhva_->GetRenderWidgetHost()); - if (!rwhi) - return; - - rwhi->Send(new ViewMsg_CancelDateTimeDialog(rwhi->GetRoutingID())); -} - void ImeAdapterAndroid::SetEditableSelectionOffsets(JNIEnv*, jobject, int start, int end) { RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From( diff --git a/content/browser/renderer_host/ime_adapter_android.h b/content/browser/renderer_host/ime_adapter_android.h index 13072a1..5932189 100644 --- a/content/browser/renderer_host/ime_adapter_android.h +++ b/content/browser/renderer_host/ime_adapter_android.h @@ -50,10 +50,6 @@ class ImeAdapterAndroid { // Called from native -> java void CancelComposition(); - // Native methods related to dialog like types - void CancelDialog(JNIEnv*, jobject); - void ReplaceDateTime(JNIEnv*, jobject, jstring text); - private: RenderWidgetHostViewAndroid* rwhva_; jobject java_ime_adapter_; diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index 51ecab4..4029272 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc @@ -2355,9 +2355,12 @@ void WebContentsImpl::OnFindMatchRectsReply( delegate_->FindMatchRectsReply(this, version, rects, active_rect); } -void WebContentsImpl::OnOpenDateTimeDialog(int type, const std::string& value) { +void WebContentsImpl::OnOpenDateTimeDialog( + const ViewHostMsg_DateTimeDialogValue_Params& value) { date_time_chooser_->ShowDialog( - GetContentNativeView(), GetRenderViewHost(), type, value); + GetContentNativeView(), GetRenderViewHost(), value.dialog_type, + value.year, value.month, value.day, value.hour, + value.minute, value.second); } #endif diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h index 6568854..d8ab2a2 100644 --- a/content/browser/web_contents/web_contents_impl.h +++ b/content/browser/web_contents/web_contents_impl.h @@ -31,6 +31,7 @@ struct BrowserPluginHostMsg_CreateGuest_Params; struct BrowserPluginHostMsg_ResizeGuest_Params; +struct ViewHostMsg_DateTimeDialogValue_Params; struct ViewMsg_PostMessage_Params; namespace webkit_glue { @@ -562,7 +563,8 @@ class CONTENT_EXPORT WebContentsImpl const std::vector<gfx::RectF>& rects, const gfx::RectF& active_rect); - void OnOpenDateTimeDialog(int type, const std::string& value); + void OnOpenDateTimeDialog( + const ViewHostMsg_DateTimeDialogValue_Params& value); #endif void OnCrashedPlugin(const base::FilePath& plugin_path, base::ProcessId plugin_pid); diff --git a/content/common/view_messages.h b/content/common/view_messages.h index e4edc01..3e6d785 100644 --- a/content/common/view_messages.h +++ b/content/common/view_messages.h @@ -360,6 +360,16 @@ IPC_STRUCT_BEGIN(ViewHostMsg_TextInputState_Params) IPC_STRUCT_MEMBER(bool, show_ime_if_needed) IPC_STRUCT_END() +IPC_STRUCT_BEGIN(ViewHostMsg_DateTimeDialogValue_Params) + IPC_STRUCT_MEMBER(int, dialog_type) + IPC_STRUCT_MEMBER(int, year) + IPC_STRUCT_MEMBER(int, month) + IPC_STRUCT_MEMBER(int, day) + IPC_STRUCT_MEMBER(int, hour) + IPC_STRUCT_MEMBER(int, minute) + IPC_STRUCT_MEMBER(int, second) +IPC_STRUCT_END() + IPC_STRUCT_BEGIN(ViewHostMsg_SelectionBounds_Params) IPC_STRUCT_MEMBER(gfx::Rect, anchor_rect) IPC_STRUCT_MEMBER(WebKit::WebTextDirection, anchor_dir) @@ -924,7 +934,8 @@ IPC_MESSAGE_ROUTED0(ViewMsg_Delete) IPC_MESSAGE_ROUTED0(ViewMsg_SelectAll) // Replaces a date time input field. -IPC_MESSAGE_ROUTED1(ViewMsg_ReplaceDateTime, string16 /* text */) +IPC_MESSAGE_ROUTED1(ViewMsg_ReplaceDateTime, + ViewHostMsg_DateTimeDialogValue_Params /* value */) IPC_MESSAGE_ROUTED0(ViewMsg_Unselect) @@ -1999,9 +2010,8 @@ IPC_MESSAGE_ROUTED1(ViewHostMsg_TakeFocus, bool /* reverse */) // Required for opening a date/time dialog -IPC_MESSAGE_ROUTED2(ViewHostMsg_OpenDateTimeDialog, - int /* type */, - std::string /* value */) +IPC_MESSAGE_ROUTED1(ViewHostMsg_OpenDateTimeDialog, + ViewHostMsg_DateTimeDialogValue_Params /* value */) // Required for updating text input state. IPC_MESSAGE_ROUTED1(ViewHostMsg_TextInputStateChanged, diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi index 55f754c..b08783c 100644 --- a/content/content_renderer.gypi +++ b/content/content_renderer.gypi @@ -227,6 +227,8 @@ 'renderer/browser_plugin/browser_plugin_compositing_helper.cc', 'renderer/browser_plugin/browser_plugin_constants.cc', 'renderer/browser_plugin/browser_plugin_constants.h', + 'renderer/date_time_formatter.cc', + 'renderer/date_time_formatter.h', 'renderer/render_process.h', 'renderer/render_process_impl.cc', 'renderer/render_process_impl.h', diff --git a/content/content_tests.gypi b/content/content_tests.gypi index 569adedc..1a1978a 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -369,6 +369,7 @@ 'renderer/active_notification_tracker_unittest.cc', 'renderer/android/email_detector_unittest.cc', 'renderer/android/phone_number_detector_unittest.cc', + 'renderer/date_time_formatter_unittest.cc', 'renderer/disambiguation_popup_helper_unittest.cc', 'renderer/gpu/input_event_filter_unittest.cc', 'renderer/hyphenator/hyphenator_unittest.cc', diff --git a/content/public/android/java/src/org/chromium/content/browser/DateTimeChooserAndroid.java b/content/public/android/java/src/org/chromium/content/browser/DateTimeChooserAndroid.java index 385b2fb..7fc146b 100644 --- a/content/public/android/java/src/org/chromium/content/browser/DateTimeChooserAndroid.java +++ b/content/public/android/java/src/org/chromium/content/browser/DateTimeChooserAndroid.java @@ -25,8 +25,12 @@ class DateTimeChooserAndroid { new InputDialogContainer.InputActionDelegate() { @Override - public void replaceDateTime(String text) { - nativeReplaceDateTime(mNativeDateTimeChooserAndroid, text); + public void replaceDateTime( + int dialogType, + int year, int month, int day, int hour, int minute, int second) { + nativeReplaceDateTime(mNativeDateTimeChooserAndroid, + dialogType, + year, month, day, hour, minute, second); } @Override @@ -36,18 +40,22 @@ class DateTimeChooserAndroid { }); } - private void showDialog(int dialogType, String text) { - mInputDialogContainer.showDialog(text, dialogType); + private void showDialog(int dialogType, int year, int month, int monthDay, + int hour, int minute, int second) { + mInputDialogContainer.showDialog(dialogType, year, month, monthDay, + hour, minute, second); } @CalledByNative private static DateTimeChooserAndroid createDateTimeChooser( ContentViewCore contentViewCore, - int nativeDateTimeChooserAndroid, String text, int dialogType) { + int nativeDateTimeChooserAndroid, int dialogType, + int year, int month, int day, + int hour, int minute, int second) { DateTimeChooserAndroid chooser = new DateTimeChooserAndroid( contentViewCore.getContext(), nativeDateTimeChooserAndroid); - chooser.showDialog(dialogType, text); + chooser.showDialog(dialogType, year, month, day, hour, minute, second); return chooser; } @@ -59,6 +67,9 @@ class DateTimeChooserAndroid { textInputTypeDateTimeLocal, textInputTypeMonth, textInputTypeTime); } - private native void nativeReplaceDateTime(int nativeDateTimeChooserAndroid, String text); + private native void nativeReplaceDateTime(int nativeDateTimeChooserAndroid, + int dialogType, + int year, int month, int day, int hour, int minute, int second); + private native void nativeCancelDialog(int nativeDateTimeChooserAndroid); } diff --git a/content/public/android/java/src/org/chromium/content/browser/InputDialogContainer.java b/content/public/android/java/src/org/chromium/content/browser/InputDialogContainer.java index 590e87d..f40d54e 100644 --- a/content/public/android/java/src/org/chromium/content/browser/InputDialogContainer.java +++ b/content/public/android/java/src/org/chromium/content/browser/InputDialogContainer.java @@ -24,13 +24,15 @@ import org.chromium.content.R; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.Calendar; import java.util.Date; class InputDialogContainer { interface InputActionDelegate { void cancelDateTimeDialog(); - void replaceDateTime(String dateTime); + void replaceDateTime(int dialogType, + int year, int month, int day, int hour, int minute, int second); } // Default values used in Time representations of selected date/time before formatting. @@ -50,13 +52,6 @@ class InputDialogContainer { private static final String HTML_DATE_TIME_LOCAL_FORMAT = "%Y-%m-%dT%H:%M"; private static final String HTML_MONTH_FORMAT = "%Y-%m"; - // Date formats as accepted by SimpleDateFormat. - private static final String PARSE_DATE_FORMAT = "yyyy-MM-dd"; - private static final String PARSE_TIME_FORMAT = "HH:mm"; - private static final String PARSE_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm'Z'"; - private static final String PARSE_DATE_TIME_LOCAL_FORMAT = "yyyy-MM-dd'T'HH:mm"; - private static final String PARSE_MONTH_FORMAT = "yyyy-MM"; - private static int sTextInputTypeDate; private static int sTextInputTypeDateTime; private static int sTextInputTypeDateTimeLocal; @@ -91,24 +86,40 @@ class InputDialogContainer { mInputActionDelegate = inputActionDelegate; } - void showDialog(String text, int textInputType) { + private Time normalizeTime(int year, int month, int monthDay, + int hour, int minute, int second) { + Time result = new Time(); + if (year == 0 && month == 0 && monthDay == 0 && hour == 0 && + minute == 0 && second == 0) { + Calendar cal = Calendar.getInstance(); + result.set(cal.get(Calendar.SECOND), cal.get(Calendar.MINUTE), + cal.get(Calendar.HOUR), cal.get(Calendar.DATE), + cal.get(Calendar.MONTH), cal.get(Calendar.YEAR)); + } else { + result.set(second, minute, hour, monthDay, month, year); + } + return result; + } + + void showDialog(final int dialogType, int year, int month, int monthDay, + int hour, int minute, int second) { if (isDialogShowing()) mDialog.dismiss(); - Time time = parse(text, textInputType); - if (textInputType == sTextInputTypeDate) { - mDialog = new DatePickerDialog(mContext, new DateListener(), + Time time = normalizeTime(year, month, monthDay, hour, minute, second); + if (dialogType == sTextInputTypeDate) { + mDialog = new DatePickerDialog(mContext, new DateListener(dialogType), time.year, time.month, time.monthDay); - } else if (textInputType == sTextInputTypeTime) { - mDialog = new TimePickerDialog(mContext, new TimeListener(), + } else if (dialogType == sTextInputTypeTime) { + mDialog = new TimePickerDialog(mContext, new TimeListener(dialogType), time.hour, time.minute, DateFormat.is24HourFormat(mContext)); - } else if (textInputType == sTextInputTypeDateTime || - textInputType == sTextInputTypeDateTimeLocal) { + } else if (dialogType == sTextInputTypeDateTime || + dialogType == sTextInputTypeDateTimeLocal) { mDialog = new DateTimePickerDialog(mContext, - new DateTimeListener(textInputType == sTextInputTypeDateTimeLocal), + new DateTimeListener(dialogType), time.year, time.month, time.monthDay, time.hour, time.minute, DateFormat.is24HourFormat(mContext)); - } else if (textInputType == sTextInputTypeMonth) { - mDialog = new MonthPickerDialog(mContext, new MonthListener(), + } else if (dialogType == sTextInputTypeMonth) { + mDialog = new MonthPickerDialog(mContext, new MonthListener(dialogType), time.year, time.month); } @@ -132,7 +143,7 @@ class InputDialogContainer { @Override public void onClick(DialogInterface dialog, int which) { mDialogAlreadyDismissed = true; - mInputActionDelegate.replaceDateTime(""); + mInputActionDelegate.replaceDateTime(dialogType, 0, 0, 0, 0, 0, 0); } }); @@ -148,78 +159,47 @@ class InputDialogContainer { if (isDialogShowing()) mDialog.dismiss(); } - private static SimpleDateFormat getParseDateFormat(int textInputType) { - String formatString = null; - if (textInputType == sTextInputTypeDate) { - formatString = PARSE_DATE_FORMAT; - } else if (textInputType == sTextInputTypeTime) { - formatString = PARSE_TIME_FORMAT; - } else if (textInputType == sTextInputTypeDateTime) { - formatString = PARSE_DATE_TIME_FORMAT; - } else if (textInputType == sTextInputTypeDateTimeLocal) { - formatString = PARSE_DATE_TIME_LOCAL_FORMAT; - } else if (textInputType == sTextInputTypeMonth) { - formatString = PARSE_MONTH_FORMAT; - } - - if (formatString != null) { - return new SimpleDateFormat(formatString); - } - - return null; - } - - /** - * Parse the text String as a date or time according to the provided text input type. - */ - private static Time parse(String text, int textInputType) { - Time result = null; - if (!TextUtils.isEmpty(text)) { - try { - SimpleDateFormat format = getParseDateFormat(textInputType); - if (format != null) { - Date date = format.parse(text); - result = new Time(); - result.set(date.getTime()); - } - } catch (ParseException e) { - // Leave time as null. - } - } + private class DateListener implements OnDateSetListener { + private final int mDialogType; - if (result == null) { - result = new Time(); - result.setToNow(); + DateListener(int dialogType) { + mDialogType = dialogType; } - return result; - } - - private class DateListener implements OnDateSetListener { @Override public void onDateSet(DatePicker view, int year, int month, int monthDay) { if (!mDialogAlreadyDismissed) { - setFieldDateTimeValue(year, month, monthDay, HOUR_DEFAULT, MINUTE_DEFAULT, + setFieldDateTimeValue(mDialogType, + year, month, monthDay, HOUR_DEFAULT, MINUTE_DEFAULT, HTML_DATE_FORMAT); } } } private class TimeListener implements OnTimeSetListener { + private final int mDialogType; + + TimeListener(int dialogType) { + mDialogType = dialogType; + } + @Override public void onTimeSet(TimePicker view, int hourOfDay, int minute) { if (!mDialogAlreadyDismissed) { - setFieldDateTimeValue(YEAR_DEFAULT, MONTH_DEFAULT, MONTHDAY_DEFAULT, + setFieldDateTimeValue(mDialogType, + YEAR_DEFAULT, MONTH_DEFAULT, MONTHDAY_DEFAULT, hourOfDay, minute, HTML_TIME_FORMAT); } } } private class DateTimeListener implements OnDateTimeSetListener { - private boolean mLocal; + private final boolean mLocal; + private final int mDialogType; - public DateTimeListener(boolean local) { - mLocal = local; + public DateTimeListener(int dialogType) { + mLocal = dialogType == sTextInputTypeDateTimeLocal; + mDialogType = dialogType; } @Override @@ -227,34 +207,36 @@ class InputDialogContainer { int year, int month, int monthDay, int hourOfDay, int minute) { if (!mDialogAlreadyDismissed) { - setFieldDateTimeValue(year, month, monthDay, hourOfDay, minute, + setFieldDateTimeValue(mDialogType, year, month, monthDay, hourOfDay, minute, mLocal ? HTML_DATE_TIME_LOCAL_FORMAT : HTML_DATE_TIME_FORMAT); } } } private class MonthListener implements OnMonthSetListener { + private final int mDialogType; + + MonthListener(int dialogType) { + mDialogType = dialogType; + } + @Override public void onMonthSet(MonthPicker view, int year, int month) { if (!mDialogAlreadyDismissed) { - setFieldDateTimeValue(year, month, MONTHDAY_DEFAULT, + setFieldDateTimeValue(mDialogType, year, month, MONTHDAY_DEFAULT, HOUR_DEFAULT, MINUTE_DEFAULT, HTML_MONTH_FORMAT); } } } - private void setFieldDateTimeValue(int year, int month, int monthDay, int hourOfDay, + private void setFieldDateTimeValue(int dialogType, + int year, int month, int monthDay, int hourOfDay, int minute, String dateFormat) { // Prevents more than one callback being sent to the native // side when the dialog triggers multiple events. mDialogAlreadyDismissed = true; - Time time = new Time(); - time.year = year; - time.month = month; - time.monthDay = monthDay; - time.hour = hourOfDay; - time.minute = minute; - mInputActionDelegate.replaceDateTime(time.format(dateFormat)); + mInputActionDelegate.replaceDateTime(dialogType, + year, month, monthDay, hourOfDay, minute, 0 /* second */); } } diff --git a/content/renderer/date_time_formatter.cc b/content/renderer/date_time_formatter.cc new file mode 100644 index 0000000..eca5b97 --- /dev/null +++ b/content/renderer/date_time_formatter.cc @@ -0,0 +1,193 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/date_time_formatter.h" + +#include "base/string_util.h" +#include "third_party/WebKit/Source/Platform/chromium/public/WebCString.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebDateTimeChooserParams.h" +#include "third_party/icu/public/i18n/unicode/smpdtfmt.h" + + +namespace content { + +void DateTimeFormatter::CreatePatternMap() { + // Initialize all the UI elements with empty patterns, + // then fill in the ones that are actually date/time inputs and + // are implemented. + for (int i = 0 ; i <= ui::TEXT_INPUT_TYPE_MAX; ++i) { + patterns_[i] = ""; + } + patterns_[ui::TEXT_INPUT_TYPE_DATE] = "yyyy-MM-dd"; + patterns_[ui::TEXT_INPUT_TYPE_DATE_TIME] = "yyyy-MM-dd'T'HH:mm'Z'"; + patterns_[ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL] = "yyyy-MM-dd'T'HH:mm"; + patterns_[ui::TEXT_INPUT_TYPE_MONTH] = "yyyy-MM"; + patterns_[ui::TEXT_INPUT_TYPE_TIME] = "HH:mm"; +} + +DateTimeFormatter::DateTimeFormatter( + const WebKit::WebDateTimeChooserParams& source) + : formatted_string_(source.currentValue.utf8()) { + CreatePatternMap(); + ExtractType(source); + if (!ParseValues()) { + type_ = ui::TEXT_INPUT_TYPE_NONE; + ClearAll(); + LOG(WARNING) << "Problems parsing input <" << formatted_string_ << ">"; + } +} + +DateTimeFormatter::DateTimeFormatter( + ui::TextInputType type, + int year, int month, int day, int hour, int minute, int second) + : type_(type), + year_(year), + month_(month), + day_(day), + hour_(hour), + minute_(minute), + second_(second) { + CreatePatternMap(); + pattern_ = type_ > 0 && type_ <= ui::TEXT_INPUT_TYPE_MAX ? + &patterns_[type_] : &patterns_[ui::TEXT_INPUT_TYPE_NONE]; + + std::string patt; + pattern_->toUTF8String(patt); + formatted_string_ = FormatString(); +} + +DateTimeFormatter::~DateTimeFormatter() { +} + +int DateTimeFormatter::GetYear() const { + return year_; +} + +int DateTimeFormatter::GetMonth() const { + return month_; +} + +int DateTimeFormatter::GetDay() const { + return day_; +} + +int DateTimeFormatter::GetHour() const { + return hour_; +} + +int DateTimeFormatter::GetMinute() const { + return minute_; +} + +int DateTimeFormatter::GetSecond() const { + return second_; +} + +ui::TextInputType DateTimeFormatter::GetType() const { + return type_; +} + +const std::string& DateTimeFormatter::GetFormattedValue() const { + return formatted_string_; +} + +const std::string DateTimeFormatter::FormatString() const { + UErrorCode success = U_ZERO_ERROR; + if (year_ == 0 && month_ == 0 && day_ == 0 && + hour_ == 0 && minute_ == 0 && second_ == 0) { + return ""; + } + + std::string result; + const icu::GregorianCalendar calendar( + year_, month_, day_, hour_, minute_, second_, success); + if (success <= U_ZERO_ERROR) { + UDate time = calendar.getTime(success); + icu::SimpleDateFormat formatter(*pattern_, success); + icu::UnicodeString formattedTime; + formatter.format(time, formattedTime, success); + formattedTime.toUTF8String(result); + if (success <= U_ZERO_ERROR) + return result; + } + LOG(WARNING) << "Calendar not created: error " << success; + return ""; +} + +void DateTimeFormatter::ExtractType( + const WebKit::WebDateTimeChooserParams& source) { + switch (source.type) { + case WebKit::WebDateTimeInputTypeDate: + type_ = ui::TEXT_INPUT_TYPE_DATE; + break; + case WebKit::WebDateTimeInputTypeDateTime: + type_ = ui::TEXT_INPUT_TYPE_DATE_TIME; + break; + case WebKit::WebDateTimeInputTypeDateTimeLocal: + type_ = ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL; + break; + case WebKit::WebDateTimeInputTypeMonth: + type_ = ui::TEXT_INPUT_TYPE_MONTH; + break; + case WebKit::WebDateTimeInputTypeTime: + type_ = ui::TEXT_INPUT_TYPE_TIME; + break; + case WebKit::WebDateTimeInputTypeWeek: // Not implemented + case WebKit::WebDateTimeInputTypeNone: + default: + type_ = ui::TEXT_INPUT_TYPE_NONE; + } +} + +// Not all fields are defined in all configurations and ICU might store +// garbage if success <= U_ZERO_ERROR so the output is sanitized here. +int DateTimeFormatter::ExtractValue( + const icu::Calendar* calendar, UCalendarDateFields value) const { + UErrorCode success = U_ZERO_ERROR; + int result = calendar->get(value, success); + return (success <= U_ZERO_ERROR) ? result : 0; +} + +bool DateTimeFormatter::ParseValues() { + if (type_ == ui::TEXT_INPUT_TYPE_NONE) { + ClearAll(); + return false; + } + + if (formatted_string_.empty()) { + ClearAll(); + return true; + } + + UErrorCode success = U_ZERO_ERROR; + icu::UnicodeString icu_value = + icu::UnicodeString::fromUTF8(formatted_string_); + if (type_ > 0 && type_ <= ui::TEXT_INPUT_TYPE_MAX) { + const icu::UnicodeString pattern = patterns_[type_]; + icu::SimpleDateFormat formatter(pattern, success); + formatter.parse(icu_value, success); + if (success <= U_ZERO_ERROR) { + const icu::Calendar* cal = formatter.getCalendar(); + year_ = ExtractValue(cal, UCAL_YEAR); + month_ = ExtractValue(cal, UCAL_MONTH); + day_ = ExtractValue(cal, UCAL_DATE); + hour_ = ExtractValue(cal, UCAL_HOUR_OF_DAY); // 24h format + minute_ = ExtractValue(cal, UCAL_MINUTE); + second_ = ExtractValue(cal, UCAL_SECOND); + } + } + + return (success <= U_ZERO_ERROR); +} + +void DateTimeFormatter::ClearAll() { + year_ = 0; + month_ = 0; + day_ = 0; + hour_ = 0; + minute_ = 0; + second_ = 0; +} + +} // namespace content diff --git a/content/renderer/date_time_formatter.h b/content/renderer/date_time_formatter.h new file mode 100644 index 0000000..d8e0ce5 --- /dev/null +++ b/content/renderer/date_time_formatter.h @@ -0,0 +1,65 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_RENDERER_DATE_TIME_FORMATTER_H_ +#define CONTENT_RENDERER_DATE_TIME_FORMATTER_H_ + +#include "base/basictypes.h" +#include "content/common/content_export.h" +#include "third_party/icu/public/common/unicode/unistr.h" +#include "third_party/icu/public/i18n/unicode/gregocal.h" +#include "ui/base/ime/text_input_type.h" + +namespace WebKit { +struct WebDateTimeChooserParams; +} // namespace WebKit + +namespace content { + +// Converts between a text string representing a date/time and +// a set of year/month/day/hour/minute/second and vice versa. +// It is timezone agnostic. +class CONTENT_EXPORT DateTimeFormatter { + public: + explicit DateTimeFormatter(const WebKit::WebDateTimeChooserParams& source); + DateTimeFormatter( + ui::TextInputType type, + int year, int month, int day, int hour, int minute, int second); + ~DateTimeFormatter(); + + int GetYear() const; + int GetMonth() const; + int GetDay() const; + int GetHour() const; + int GetMinute() const; + int GetSecond() const; + ui::TextInputType GetType() const; + const std::string& GetFormattedValue() const; + + private: + void CreatePatternMap(); + bool ParseValues(); + const std::string FormatString() const; + int ExtractValue( + const icu::Calendar* calendar, UCalendarDateFields value) const; + void ExtractType(const WebKit::WebDateTimeChooserParams& source); + void ClearAll(); + + ui::TextInputType type_; + icu::UnicodeString patterns_[ui::TEXT_INPUT_TYPE_MAX + 1]; + int year_; + int month_; + int day_; + int hour_; + int minute_; + int second_; + const icu::UnicodeString* pattern_; + std::string formatted_string_; + + DISALLOW_COPY_AND_ASSIGN(DateTimeFormatter); +}; + +} // namespace content + +#endif // CONTENT_RENDERER_DATE_TIME_FORMATTER_H_ diff --git a/content/renderer/date_time_formatter_unittest.cc b/content/renderer/date_time_formatter_unittest.cc new file mode 100644 index 0000000..52bce89 --- /dev/null +++ b/content/renderer/date_time_formatter_unittest.cc @@ -0,0 +1,149 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <map> + +#include "content/renderer/date_time_formatter.h" +#include "content/renderer/renderer_date_time_picker.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebDateTimeChooserParams.h" +#include "third_party/icu/public/common/unicode/unistr.h" +#include "ui/base/ime/text_input_type.h" + +namespace content { + +class RendererDateTimePickerTest { +}; + +TEST(RendererDateTimePickerTest, TestParserValidStringInputs) { + WebKit::WebDateTimeChooserParams params; + params.currentValue = "2010-07"; + params.type = WebKit::WebDateTimeInputTypeMonth; + DateTimeFormatter sut(params); + EXPECT_EQ(2010, sut.GetYear()); + + // Month field is 0 based + EXPECT_EQ(6, sut.GetMonth()); + + // Month input defaults to the first day of the month (1 based) + EXPECT_EQ(1, sut.GetDay()); + EXPECT_EQ(0, sut.GetHour()); + EXPECT_EQ(0, sut.GetMinute()); + EXPECT_EQ(0, sut.GetSecond()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_MONTH, sut.GetType()); + + params.currentValue = "2012-05-25"; + params.type = WebKit::WebDateTimeInputTypeDate; + DateTimeFormatter sut2(params); + EXPECT_EQ(2012, sut2.GetYear()); + EXPECT_EQ(4, sut2.GetMonth()); + EXPECT_EQ(25, sut2.GetDay()); + EXPECT_EQ(0, sut2.GetHour()); + EXPECT_EQ(0, sut2.GetMinute()); + EXPECT_EQ(0, sut2.GetSecond()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_DATE, sut2.GetType()); + + params.currentValue = "2013-05-21T12:15"; + params.type = WebKit::WebDateTimeInputTypeDateTimeLocal; + DateTimeFormatter sut3(params); + EXPECT_EQ(2013, sut3.GetYear()); + EXPECT_EQ(4, sut3.GetMonth()); + EXPECT_EQ(21, sut3.GetDay()); + EXPECT_EQ(12, sut3.GetHour()); + EXPECT_EQ(15, sut3.GetMinute()); + EXPECT_EQ(0, sut3.GetSecond()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL, sut3.GetType()); +} + + +TEST(RendererDateTimePickerTest, TestParserInvalidStringInputs) { + + // Random non parsable text + WebKit::WebDateTimeChooserParams params; + params.currentValue = "<script injection"; + params.type = WebKit::WebDateTimeInputTypeMonth; + DateTimeFormatter sut(params); + EXPECT_EQ(0, sut.GetYear()); + EXPECT_EQ(0, sut.GetMonth()); + EXPECT_EQ(0, sut.GetDay()); + EXPECT_EQ(0, sut.GetHour()); + EXPECT_EQ(0, sut.GetMinute()); + EXPECT_EQ(0, sut.GetSecond()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, sut.GetType()); + + // unimplemented type + params.currentValue = "week 23"; + params.type = WebKit::WebDateTimeInputTypeWeek; // Not implemented + DateTimeFormatter sut2(params); + EXPECT_EQ(0, sut2.GetYear()); + EXPECT_EQ(0, sut2.GetMonth()); + EXPECT_EQ(0, sut2.GetDay()); + EXPECT_EQ(0, sut2.GetHour()); + EXPECT_EQ(0, sut2.GetMinute()); + EXPECT_EQ(0, sut2.GetSecond()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, sut2.GetType()); + + // type is a subset of pattern + params.currentValue = "2012-05-25"; + params.type = WebKit::WebDateTimeInputTypeDateTimeLocal; + DateTimeFormatter sut3(params); + EXPECT_EQ(0, sut3.GetYear()); + EXPECT_EQ(0, sut3.GetMonth()); + EXPECT_EQ(0, sut3.GetDay()); + EXPECT_EQ(0, sut3.GetHour()); + EXPECT_EQ(0, sut3.GetMinute()); + EXPECT_EQ(0, sut3.GetSecond()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, sut3.GetType()); + + // type is a superset of pattern + params.currentValue = "2013-05-21T12:15"; + params.type = WebKit::WebDateTimeInputTypeMonth; + DateTimeFormatter sut4(params); + EXPECT_EQ(2013, sut4.GetYear()); + EXPECT_EQ(4, sut4.GetMonth()); + EXPECT_EQ(1, sut4.GetDay()); + EXPECT_EQ(0, sut4.GetHour()); + EXPECT_EQ(0, sut4.GetMinute()); + EXPECT_EQ(0, sut4.GetSecond()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_MONTH, sut4.GetType()); +} + + +TEST(RendererDateTimePickerTest, TestParserValidDateInputs) { + DateTimeFormatter sut(ui::TEXT_INPUT_TYPE_MONTH, 2012, 11, 1, 0, 0, 0); + EXPECT_EQ("2012-12", sut.GetFormattedValue()); + + + DateTimeFormatter sut2(ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL, + 2013, 3, 23, 15, 47, 0); + EXPECT_EQ("2013-04-23T15:47", sut2.GetFormattedValue()); +} + +TEST(RendererDateTimePickerTest, TestParserInvalidDateInputs) { + DateTimeFormatter sut(ui::TEXT_INPUT_TYPE_WEEK, 2012, 2, 0, 0, 0, 0); + EXPECT_EQ("", sut.GetFormattedValue()); + + DateTimeFormatter sut2(ui::TEXT_INPUT_TYPE_NONE, 2013, 3, 23, 0, 0, 0); + EXPECT_EQ("", sut2.GetFormattedValue()); + + DateTimeFormatter sut3(ui::TEXT_INPUT_TYPE_NONE, 2013, 14, 32, 0, 0, 0); + EXPECT_EQ("", sut3.GetFormattedValue()); + + DateTimeFormatter sut4(ui::TEXT_INPUT_TYPE_DATE, 0, 0, 0, 0, 0, 0); + EXPECT_EQ("", sut4.GetFormattedValue()); + + DateTimeFormatter sut5(ui::TEXT_INPUT_TYPE_TIME, 0, 0, 0, 0, 0, 0); + EXPECT_EQ("", sut5.GetFormattedValue()); + + DateTimeFormatter sut6(ui::TEXT_INPUT_TYPE_PASSWORD, 23, 0, 0, 0, 5, 0); + EXPECT_EQ("", sut6.GetFormattedValue()); + + DateTimeFormatter sut7(ui::TEXT_INPUT_TYPE_MAX, 23, 0, 0, 0, 5, 0); + EXPECT_EQ("", sut7.GetFormattedValue()); + + DateTimeFormatter sut8( + static_cast<ui::TextInputType>(10000), 23, 0, 0, 0, 5, 0); + EXPECT_EQ("", sut8.GetFormattedValue()); +} +} // namespace content diff --git a/content/renderer/renderer_date_time_picker.cc b/content/renderer/renderer_date_time_picker.cc index fea94fa..f17ba35 100644 --- a/content/renderer/renderer_date_time_picker.cc +++ b/content/renderer/renderer_date_time_picker.cc @@ -6,50 +6,43 @@ #include "base/string_util.h" #include "content/common/view_messages.h" +#include "content/renderer/date_time_formatter.h" #include "content/renderer/render_view_impl.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebDateTimeChooserCompletion.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebDateTimeChooserParams.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebDateTimeInputType.h" -namespace content { - using WebKit::WebString; +namespace content { + RendererDateTimePicker::RendererDateTimePicker( RenderViewImpl* sender, const WebKit::WebDateTimeChooserParams& params, WebKit::WebDateTimeChooserCompletion* completion) : RenderViewObserver(sender), chooser_params_(params), - chooser_completion_(completion) { + chooser_completion_(completion){ } RendererDateTimePicker::~RendererDateTimePicker() { } -static ui::TextInputType ExtractType( - const WebKit::WebDateTimeChooserParams& source) { - - if (source.type == WebKit::WebDateTimeInputTypeDate) - return ui::TEXT_INPUT_TYPE_DATE; - if (source.type == WebKit::WebDateTimeInputTypeDateTime) - return ui::TEXT_INPUT_TYPE_DATE_TIME; - if (source.type == WebKit::WebDateTimeInputTypeDateTimeLocal) - return ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL; - if (source.type == WebKit::WebDateTimeInputTypeMonth) - return ui::TEXT_INPUT_TYPE_MONTH; - if (source.type == WebKit::WebDateTimeInputTypeTime) - return ui::TEXT_INPUT_TYPE_TIME; - if (source.type == WebKit::WebDateTimeInputTypeWeek) - return ui::TEXT_INPUT_TYPE_WEEK; - return ui::TEXT_INPUT_TYPE_NONE; -} - bool RendererDateTimePicker::Open() { - Send(new ViewHostMsg_OpenDateTimeDialog( - routing_id(), ExtractType(chooser_params_), - chooser_params_.currentValue.utf8())); + DateTimeFormatter parser(chooser_params_); + std::string test_s = chooser_params_.currentValue.utf8(); + + ViewHostMsg_DateTimeDialogValue_Params message; + message.year = parser.GetYear(); + message.month = parser.GetMonth(); + message.day = parser.GetDay(); + message.hour = parser.GetHour(); + message.minute = parser.GetMinute(); + message.second = parser.GetSecond(); + message.dialog_type = parser.GetType(); + + Send(new ViewHostMsg_OpenDateTimeDialog(routing_id(), message)); return true; } @@ -64,9 +57,17 @@ bool RendererDateTimePicker::OnMessageReceived( return handled; } -void RendererDateTimePicker::OnReplaceDateTime(const string16& new_date) { +void RendererDateTimePicker::OnReplaceDateTime( + const ViewHostMsg_DateTimeDialogValue_Params& value) { + + DateTimeFormatter formatter( + static_cast<ui::TextInputType>(value.dialog_type), + value.year, value.month, value.day, + value.hour, value.minute, value.second); + if (chooser_completion_) - chooser_completion_->didChooseValue(new_date); + chooser_completion_->didChooseValue(WebString::fromUTF8( + formatter.GetFormattedValue().c_str())); } void RendererDateTimePicker::OnCancel() { diff --git a/content/renderer/renderer_date_time_picker.h b/content/renderer/renderer_date_time_picker.h index 1a61227..23dcf68 100644 --- a/content/renderer/renderer_date_time_picker.h +++ b/content/renderer/renderer_date_time_picker.h @@ -8,7 +8,8 @@ #include "base/basictypes.h" #include "content/public/renderer/render_view_observer.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebDateTimeChooserParams.h" -#include "ui/base/ime/text_input_type.h" + +struct ViewHostMsg_DateTimeDialogValue_Params; namespace WebKit { class WebDateTimeChooserCompletion; @@ -28,7 +29,7 @@ class RendererDateTimePicker : public RenderViewObserver { bool Open(); private: - void OnReplaceDateTime(const string16& new_date); + void OnReplaceDateTime(const ViewHostMsg_DateTimeDialogValue_Params& value); void OnCancel(); // RenderViewObserver |