summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormiguelg@chromium.org <miguelg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-07 21:14:07 +0000
committermiguelg@chromium.org <miguelg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-07 21:14:07 +0000
commit1ff42797b769734346eb363a63a2766bf52a641f (patch)
tree95a4077b34af1fda4a3e1ffd38d81b5b7cf5df9d
parenta06f53ede6b24f5c8b521059bb0d1594787c75d5 (diff)
downloadchromium_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.cc35
-rw-r--r--content/browser/android/date_time_chooser_android.h11
-rw-r--r--content/browser/renderer_host/ime_adapter_android.cc20
-rw-r--r--content/browser/renderer_host/ime_adapter_android.h4
-rw-r--r--content/browser/web_contents/web_contents_impl.cc7
-rw-r--r--content/browser/web_contents/web_contents_impl.h4
-rw-r--r--content/common/view_messages.h18
-rw-r--r--content/content_renderer.gypi2
-rw-r--r--content/content_tests.gypi1
-rw-r--r--content/public/android/java/src/org/chromium/content/browser/DateTimeChooserAndroid.java25
-rw-r--r--content/public/android/java/src/org/chromium/content/browser/InputDialogContainer.java142
-rw-r--r--content/renderer/date_time_formatter.cc193
-rw-r--r--content/renderer/date_time_formatter.h65
-rw-r--r--content/renderer/date_time_formatter_unittest.cc149
-rw-r--r--content/renderer/renderer_date_time_picker.cc53
-rw-r--r--content/renderer/renderer_date_time_picker.h5
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