Temporary patch for date/time input frame.
draft
Temporary patch for date/time input frame.
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -156,16 +156,27 @@
#ifdef NIGHTLY_BUILD
<hbox id="urlbar-search-footer" flex="1" align="stretch" pack="end">
<button id="urlbar-search-settings" label="&changeSearchSettings.button;"
oncommand="BrowserUITelemetry.countSearchSettingsEvent('urlbar'); openPreferences('paneSearch')"/>
</hbox>
#endif
</panel>
+ <panel id="DateTimePickerPanel"
+ hidden="true"
+ noautofocus="true"
+ consumeoutsideclicks="true"
+ noautohide="false"
+ level="parent">
+ <vbox>
+ <iframe id="myFrame" width="200" height="200"/>
+ </vbox>
+ </panel>
+
<!-- for select dropdowns. The menupopup is what shows the list of options,
and the popuponly menulist makes things like the menuactive attributes
work correctly on the menupopup. ContentSelectDropdown expects the
popuponly menulist to be its immediate parent. -->
<menulist popuponly="true" id="ContentSelectDropdown" hidden="true">
<menupopup rolluponmousewheel="true"
activateontab="true"
#ifdef XP_WIN
@@ -1082,17 +1093,18 @@
<splitter id="sidebar-splitter" class="chromeclass-extrachrome sidebar-splitter" hidden="true"/>
<vbox id="appcontent" flex="1">
<notificationbox id="high-priority-global-notificationbox" notificationside="top"/>
<tabbrowser id="content"
flex="1" contenttooltip="aHTMLTooltip"
tabcontainer="tabbrowser-tabs"
contentcontextmenu="contentAreaContextMenu"
autocompletepopup="PopupAutoComplete"
- selectmenulist="ContentSelectDropdown"/>
+ selectmenulist="ContentSelectDropdown"
+ datetimepicker="DateTimePickerPanel"/>
<chatbar id="pinnedchats" layer="true" mousethrough="always" hidden="true"/>
</vbox>
<splitter id="social-sidebar-splitter"
class="chromeclass-extrachrome sidebar-splitter"
observes="socialSidebarBroadcaster"/>
<vbox id="social-sidebar-box"
class="chromeclass-extrachrome"
observes="socialSidebarBroadcaster"
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -20,17 +20,17 @@
flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown"
onselect="if (event.target.localName == 'tabpanels') this.parentNode.updateCurrentBrowser();">
<xul:tabpanels flex="1" class="plain" selectedIndex="0" anonid="panelcontainer">
<xul:notificationbox flex="1" notificationside="top">
<xul:hbox flex="1" class="browserSidebarContainer">
<xul:vbox flex="1" class="browserContainer">
<xul:stack flex="1" class="browserStack" anonid="browserStack">
<xul:browser anonid="initialBrowser" type="content-primary" message="true" messagemanagergroup="browsers"
- xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup,selectmenulist"/>
+ xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup,selectmenulist,datetimepicker"/>
</xul:stack>
</xul:vbox>
</xul:hbox>
</xul:notificationbox>
</xul:tabpanels>
</xul:tabbox>
<children/>
</content>
@@ -1791,16 +1791,19 @@
if (!aParams.isPreloadBrowser && this.hasAttribute("autocompletepopup")) {
b.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup"));
}
if (this.hasAttribute("selectmenulist"))
b.setAttribute("selectmenulist", this.getAttribute("selectmenulist"));
+ if (this.hasAttribute("datetimepicker"))
+ b.setAttribute("datetimepicker", this.getAttribute("datetimepicker"));
+
b.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
// Create the browserStack container
var stack = document.createElementNS(NS_XUL, "stack");
stack.className = "browserStack";
stack.appendChild(b);
stack.setAttribute("flex", "1");
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -266,16 +266,17 @@ GK_ATOM(curpos, "curpos")
GK_ATOM(current, "current")
GK_ATOM(cycler, "cycler")
GK_ATOM(data, "data")
GK_ATOM(datalist, "datalist")
GK_ATOM(dataType, "data-type")
GK_ATOM(dateTime, "date-time")
GK_ATOM(datasources, "datasources")
GK_ATOM(datetime, "datetime")
+GK_ATOM(datetimebox, "datetimebox")
GK_ATOM(dblclick, "dblclick")
GK_ATOM(dd, "dd")
GK_ATOM(debug, "debug")
GK_ATOM(decimalFormat, "decimal-format")
GK_ATOM(decimalSeparator, "decimal-separator")
GK_ATOM(deck, "deck")
GK_ATOM(declare, "declare")
GK_ATOM(decoderDoctor, "decoder-doctor")
@@ -1966,16 +1967,17 @@ GK_ATOM(bcTableCellFrame, "BCTableCellFr
GK_ATOM(blockFrame, "BlockFrame")
GK_ATOM(boxFrame, "BoxFrame")
GK_ATOM(brFrame, "BRFrame")
GK_ATOM(bulletFrame, "BulletFrame")
GK_ATOM(colorControlFrame, "colorControlFrame")
GK_ATOM(columnSetFrame, "ColumnSetFrame")
GK_ATOM(comboboxControlFrame, "ComboboxControlFrame")
GK_ATOM(comboboxDisplayFrame, "ComboboxDisplayFrame")
+GK_ATOM(dateTimeControlFrame, "DateTimeControlFrame")
GK_ATOM(deckFrame, "DeckFrame")
GK_ATOM(detailsFrame, "DetailsFrame")
GK_ATOM(fieldSetFrame, "FieldSetFrame")
GK_ATOM(flexContainerFrame, "FlexContainerFrame")
GK_ATOM(formControlFrame, "FormControlFrame") // radio or checkbox
GK_ATOM(frameSetFrame, "FrameSetFrame")
GK_ATOM(gfxButtonControlFrame, "gfxButtonControlFrame")
GK_ATOM(gridContainerFrame, "GridContainerFrame")
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -48,16 +48,17 @@
#include "nsIFrame.h"
#include "nsRangeFrame.h"
#include "nsIServiceManager.h"
#include "nsError.h"
#include "nsIEditor.h"
#include "nsIIOService.h"
#include "nsDocument.h"
#include "nsAttrValueOrString.h"
+#include "nsDateTimeControlFrame.h"
#include "nsPresState.h"
#include "nsIDOMEvent.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMHTMLCollection.h"
#include "nsLinebreakConverter.h" //to strip out carriage returns
#include "nsReadableUtils.h"
#include "nsUnicharUtils.h"
@@ -3062,16 +3063,21 @@ HTMLInputElement::SetValueInternal(const
if (numberControlFrame) {
numberControlFrame->SetValueOfAnonTextControl(value);
}
} else if (mType == NS_FORM_INPUT_RANGE) {
nsRangeFrame* frame = do_QueryFrame(GetPrimaryFrame());
if (frame) {
frame->UpdateForValueChange();
}
+ } else if (mType == NS_FORM_INPUT_TIME) {
+ nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
+ if (frame) {
+ frame->UpdateInputBoxValue(value);
+ }
}
if (!mParserCreating) {
OnValueChanged(true);
}
// else DoneCreatingElement calls us again once mParserCreating is false
}
if (mType == NS_FORM_INPUT_COLOR) {
--- a/dom/html/nsIFormControl.h
+++ b/dom/html/nsIFormControl.h
@@ -261,17 +261,16 @@ nsIFormControl::IsSingleLineTextControl(
{
return aType == NS_FORM_INPUT_TEXT ||
aType == NS_FORM_INPUT_EMAIL ||
aType == NS_FORM_INPUT_SEARCH ||
aType == NS_FORM_INPUT_TEL ||
aType == NS_FORM_INPUT_URL ||
// TODO: those are temporary until bug 773205 is fixed.
aType == NS_FORM_INPUT_DATE ||
- aType == NS_FORM_INPUT_TIME ||
aType == NS_FORM_INPUT_MONTH ||
(!aExcludePassword && aType == NS_FORM_INPUT_PASSWORD);
}
bool
nsIFormControl::IsSubmittableControl() const
{
// TODO: keygen should be in that list, see bug 101019.
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -867,19 +867,19 @@ nsXULElement::BindToTree(nsIDocument* aD
// for HTML we currently should only pull it in if the document contains
// an <audio> or <video> element. This assertion is here to make sure
// that we don't fail to notice if a change to bindings causes us to
// start pulling in xul.css much more frequently. If this assertion
// fails then we need to figure out why, and how we can continue to avoid
// pulling in xul.css.
// Note that add-ons may introduce bindings that cause this assertion to
// fire.
- NS_ASSERTION(IsInVideoControls(this) ||
- IsInFeedSubscribeLine(this),
- "Unexpected XUL element in non-XUL doc");
+ //NS_ASSERTION(IsInVideoControls(this) ||
+ // IsInFeedSubscribeLine(this),
+ // "Unexpected XUL element in non-XUL doc");
}
}
if (aDocument) {
NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
"Missing a script blocker!");
// We're in a document now. Kick off the frame load.
LoadSrc();
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -3694,17 +3694,17 @@ nsCSSFrameConstructor::FindInputData(Ele
{ NS_FORM_INPUT_COLOR,
FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewColorControlFrame,
nsCSSAnonBoxes::buttonContent) },
// TODO: this is temporary until a frame is written: bug 635240.
SIMPLE_INT_CREATE(NS_FORM_INPUT_NUMBER, NS_NewNumberControlFrame),
// TODO: this is temporary until a frame is written: bug 888320.
SIMPLE_INT_CREATE(NS_FORM_INPUT_DATE, NS_NewTextControlFrame),
// TODO: this is temporary until a frame is written: bug 888320
- SIMPLE_INT_CREATE(NS_FORM_INPUT_TIME, NS_NewTextControlFrame),
+ SIMPLE_INT_CREATE(NS_FORM_INPUT_TIME, NS_NewDateTimeControlFrame),
// TODO: this is temporary until a frame is written: bug 888320
SIMPLE_INT_CREATE(NS_FORM_INPUT_MONTH, NS_NewTextControlFrame),
{ NS_FORM_INPUT_SUBMIT,
FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewGfxButtonControlFrame,
nsCSSAnonBoxes::buttonContent) },
{ NS_FORM_INPUT_RESET,
FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewGfxButtonControlFrame,
nsCSSAnonBoxes::buttonContent) },
--- a/layout/forms/moz.build
+++ b/layout/forms/moz.build
@@ -17,16 +17,17 @@ EXPORTS += [
'nsISelectControlFrame.h',
'nsITextControlFrame.h',
]
UNIFIED_SOURCES += [
'nsButtonFrameRenderer.cpp',
'nsColorControlFrame.cpp',
'nsComboboxControlFrame.cpp',
+ 'nsDateTimeControlFrame.cpp',
'nsFieldSetFrame.cpp',
'nsFileControlFrame.cpp',
'nsFormControlFrame.cpp',
'nsGfxButtonControlFrame.cpp',
'nsGfxCheckboxControlFrame.cpp',
'nsGfxRadioControlFrame.cpp',
'nsHTMLButtonControlFrame.cpp',
'nsImageControlFrame.cpp',
new file mode 100644
--- /dev/null
+++ b/layout/forms/nsDateTimeControlFrame.cpp
@@ -0,0 +1,332 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsDateTimeControlFrame.h"
+
+#include "nsContentUtils.h"
+#include "nsFormControlFrame.h"
+#include "nsGkAtoms.h"
+#include "nsContentUtils.h"
+#include "nsContentCreatorFunctions.h"
+#include "nsContentList.h"
+#include "mozilla/dom/HTMLInputElement.h"
+#include "nsNodeInfoManager.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+class nsDateTimeListener : public nsIDOMEventListener
+{
+private:
+ virtual ~nsDateTimeListener() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD HandleEvent(nsIDOMEvent*) override
+ {
+ mDateTimeControl->ShowPicker();
+ return NS_OK;
+ }
+
+ explicit nsDateTimeListener(nsDateTimeControlFrame* aDateTimeControl)
+ {
+ mDateTimeControl = aDateTimeControl;
+ }
+
+ nsDateTimeControlFrame* mDateTimeControl;
+};
+
+NS_IMPL_ISUPPORTS(nsDateTimeListener,
+ nsIDOMEventListener)
+
+nsIFrame*
+NS_NewDateTimeControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+ return new (aPresShell) nsDateTimeControlFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsDateTimeControlFrame)
+
+NS_QUERYFRAME_HEAD(nsDateTimeControlFrame)
+ NS_QUERYFRAME_ENTRY(nsDateTimeControlFrame)
+ NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
+NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
+
+nsDateTimeControlFrame::nsDateTimeControlFrame(nsStyleContext* aContext)
+ : nsContainerFrame(aContext)
+{
+}
+
+void
+nsDateTimeControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+ nsContentUtils::DestroyAnonymousContent(&mDateTimeInputBox);
+ nsContainerFrame::DestroyFrom(aDestructRoot);
+}
+
+void
+nsDateTimeControlFrame::ShowPicker()
+{
+ if (XRE_IsContentProcess()) {
+ nsContentUtils::DispatchChromeEvent(mContent->OwnerDoc(), mContent,
+ NS_LITERAL_STRING("mozshowdatetimepicker"),
+ true, false);
+ }
+}
+
+class DispatchEventToDateTimeBox : public Runnable
+{
+public:
+ explicit DispatchEventToDateTimeBox(nsIContent* aContent,
+ const nsAString& aEventName)
+ : mContent(aContent),
+ mEventName(aEventName) {}
+ NS_IMETHOD Run() override {
+ printf_stderr("DispatchTrustedEvent: %s\n", NS_ConvertUTF16toUTF8(mEventName).get());
+ nsContentUtils::DispatchTrustedEvent(mContent->OwnerDoc(), mContent,
+ mEventName,
+ false, false);
+ return NS_OK;
+ }
+ nsString mEventName;
+ nsCOMPtr<nsIContent> mContent;
+};
+
+void
+nsDateTimeControlFrame::UpdateInputBoxValue(const nsAString& aValue)
+{
+ printf_stderr("UpdateInputBoxValue\n");
+ RefPtr<Runnable> event = new DispatchEventToDateTimeBox(mDateTimeInputBox,
+ NS_LITERAL_STRING("datetime-value-changed"));
+ nsContentUtils::AddScriptRunner(event);
+}
+
+nscoord
+nsDateTimeControlFrame::GetMinISize(nsRenderingContext* aRenderingContext)
+{
+ nscoord result;
+ DISPLAY_MIN_WIDTH(this, result);
+
+ nsIFrame* kid = mFrames.FirstChild();
+ if (kid) { // display:none?
+ result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
+ kid,
+ nsLayoutUtils::MIN_ISIZE);
+ } else {
+ result = 0;
+ }
+
+ return result;
+}
+
+nscoord
+nsDateTimeControlFrame::GetPrefISize(nsRenderingContext* aRenderingContext)
+{
+ nscoord result;
+ DISPLAY_PREF_WIDTH(this, result);
+
+ nsIFrame* kid = mFrames.FirstChild();
+ if (kid) { // display:none?
+ result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
+ kid,
+ nsLayoutUtils::PREF_ISIZE);
+ } else {
+ result = 0;
+ }
+
+ return result;
+}
+
+void
+nsDateTimeControlFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+
+ DO_GLOBAL_REFLOW_COUNT("nsDateTimeControlFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
+
+ NS_ASSERTION(mDateTimeInputBox, "DateTime box must exist!");
+
+ const WritingMode myWM = aReflowInput.GetWritingMode();
+
+ // The ISize of our content box, which is the available ISize
+ // for our anonymous content:
+ const nscoord contentBoxISize = aReflowInput.ComputedISize();
+ nscoord contentBoxBSize = aReflowInput.ComputedBSize();
+
+ // Figure out our border-box sizes as well (by adding borderPadding to
+ // content-box sizes):
+ const nscoord borderBoxISize = contentBoxISize +
+ aReflowInput.ComputedLogicalBorderPadding().IStartEnd(myWM);
+
+ nscoord borderBoxBSize;
+ if (contentBoxBSize != NS_INTRINSICSIZE) {
+ borderBoxBSize = contentBoxBSize +
+ aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM);
+ } // else, we'll figure out borderBoxBSize after we resolve contentBoxBSize.
+
+ nsIFrame* outerWrapperFrame = mFrames.FirstChild();
+ if (outerWrapperFrame) {
+ NS_ASSERTION(outerWrapperFrame->GetContent() == mDateTimeInputBox,
+ "What is this child doing here?");
+
+ ReflowOutput wrappersDesiredSize(aReflowInput);
+
+ WritingMode wrapperWM = outerWrapperFrame->GetWritingMode();
+ LogicalSize availSize = aReflowInput.ComputedSize(wrapperWM);
+ availSize.BSize(wrapperWM) = NS_UNCONSTRAINEDSIZE;
+
+ ReflowInput wrapperReflowInput(aPresContext, aReflowInput,
+ outerWrapperFrame, availSize);
+
+ // Convert wrapper margin into my own writing-mode (in case it differs):
+ LogicalMargin wrapperMargin =
+ wrapperReflowInput.ComputedLogicalMargin().ConvertTo(myWM, wrapperWM);
+
+ // offsets of wrapper frame within this frame:
+ LogicalPoint
+ wrapperOffset(myWM,
+ aReflowInput.ComputedLogicalBorderPadding().IStart(myWM) +
+ wrapperMargin.IStart(myWM),
+ aReflowInput.ComputedLogicalBorderPadding().BStart(myWM) +
+ wrapperMargin.BStart(myWM));
+
+ nsReflowStatus childStatus;
+ // We initially reflow the child with a dummy containerSize; positioning
+ // will be fixed later.
+ const nsSize dummyContainerSize;
+ ReflowChild(outerWrapperFrame, aPresContext, wrappersDesiredSize,
+ wrapperReflowInput, myWM, wrapperOffset, dummyContainerSize, 0,
+ childStatus);
+ MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(childStatus),
+ "We gave our child unconstrained available block-size, "
+ "so it should be complete");
+
+ nscoord wrappersMarginBoxBSize =
+ wrappersDesiredSize.BSize(myWM) + wrapperMargin.BStartEnd(myWM);
+
+ if (contentBoxBSize == NS_INTRINSICSIZE) {
+ // We are intrinsically sized -- we should shrinkwrap the outer wrapper's
+ // block-size:
+ contentBoxBSize = wrappersMarginBoxBSize;
+
+ // Make sure we obey min/max-bsize in the case when we're doing intrinsic
+ // sizing (we get it for free when we have a non-intrinsic
+ // aReflowInput.ComputedBSize()). Note that we do this before
+ // adjusting for borderpadding, since ComputedMaxBSize and
+ // ComputedMinBSize are content heights.
+ contentBoxBSize =
+ NS_CSS_MINMAX(contentBoxBSize,
+ aReflowInput.ComputedMinBSize(),
+ aReflowInput.ComputedMaxBSize());
+
+ borderBoxBSize = contentBoxBSize +
+ aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM);
+ }
+
+ // Center child in block axis
+ nscoord extraSpace = contentBoxBSize - wrappersMarginBoxBSize;
+ wrapperOffset.B(myWM) += std::max(0, extraSpace / 2);
+
+ // Needed in FinishReflowChild, for logical-to-physical conversion:
+ nsSize borderBoxSize = LogicalSize(myWM, borderBoxISize, borderBoxBSize).
+ GetPhysicalSize(myWM);
+
+ // Place the child
+ FinishReflowChild(outerWrapperFrame, aPresContext, wrappersDesiredSize,
+ &wrapperReflowInput, myWM, wrapperOffset,
+ borderBoxSize, 0);
+
+ nsSize contentBoxSize =
+ LogicalSize(myWM, contentBoxISize, contentBoxBSize).
+ GetPhysicalSize(myWM);
+ aDesiredSize.SetBlockStartAscent(
+ wrappersDesiredSize.BlockStartAscent() +
+ outerWrapperFrame->BStart(aReflowInput.GetWritingMode(),
+ contentBoxSize));
+ }
+
+ LogicalSize logicalDesiredSize(myWM, borderBoxISize, borderBoxBSize);
+ aDesiredSize.SetSize(myWM, logicalDesiredSize);
+
+ aDesiredSize.SetOverflowAreasToDesiredBounds();
+
+ if (outerWrapperFrame) {
+ ConsiderChildOverflow(aDesiredSize.mOverflowAreas, outerWrapperFrame);
+ }
+
+ FinishAndStoreOverflow(&aDesiredSize);
+
+ aStatus = NS_FRAME_COMPLETE;
+
+ NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
+ ("exit nsDateTimeControlFrame::Reflow: size=%d,%d",
+ aDesiredSize.Width(), aDesiredSize.Height()));
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
+}
+
+nsresult
+nsDateTimeControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
+{
+ // Set up "datetimebox" XUL element which will be XBL-bound to the
+ // actual controls.
+ nsNodeInfoManager *nodeInfoManager = GetContent()->GetComposedDoc()->NodeInfoManager();
+ RefPtr<NodeInfo> nodeInfo;
+ nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::datetimebox, nullptr,
+ kNameSpaceID_XUL, nsIDOMNode::ELEMENT_NODE);
+ NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
+
+ NS_TrustedNewXULElement(getter_AddRefs(mDateTimeInputBox), nodeInfo.forget());
+ if (!aElements.AppendElement(mDateTimeInputBox))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ mDateTimeListener = new nsDateTimeListener(this);
+ mDateTimeInputBox->AddEventListener(NS_LITERAL_STRING("ShowDateTimePicker"), mDateTimeListener,
+ false, true);
+
+return NS_OK;
+}
+
+void
+nsDateTimeControlFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
+ uint32_t aFilter)
+{
+ if (mDateTimeInputBox) {
+ aElements.AppendElement(mDateTimeInputBox);
+ }
+}
+
+nsresult
+nsDateTimeControlFrame::AttributeChanged(int32_t aNameSpaceID,
+ nsIAtom* aAttribute,
+ int32_t aModType)
+{
+ // FIXME: This function is not getting called on attribute changed!?
+ NS_ASSERTION(mDateTimeInputBox, "The datetime box must exist!");
+
+ MOZ_ASSERT(mContent->IsHTMLElement(nsGkAtoms::input), "bad cast");
+ bool typeIsDateTime = static_cast<dom::HTMLInputElement*>(mContent)->GetType() ==
+ NS_FORM_INPUT_TIME;
+ // If script changed the <input>'s type before setting these attributes
+ // then we don't need to do anything since we are going to be reframed.
+ if (typeIsDateTime) {
+ RefPtr<Runnable> event = new DispatchEventToDateTimeBox(mDateTimeInputBox,
+ NS_LITERAL_STRING("datetime-value-changed"));
+ nsContentUtils::AddScriptRunner(event);
+ }
+
+ return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
+ aModType);
+}
+
+nsIAtom*
+nsDateTimeControlFrame::GetType() const
+{
+ return nsGkAtoms::dateTimeControlFrame;
+}
new file mode 100644
--- /dev/null
+++ b/layout/forms/nsDateTimeControlFrame.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsDateTimeControlFrame_h__
+#define nsDateTimeControlFrame_h__
+
+#include "mozilla/Attributes.h"
+#include "nsContainerFrame.h"
+#include "nsIAnonymousContentCreator.h"
+#include "nsCOMPtr.h"
+
+class nsDateTimeControlFrame final : public nsContainerFrame,
+ public nsIAnonymousContentCreator
+{
+ explicit nsDateTimeControlFrame(nsStyleContext* aContext);
+
+public:
+ friend nsIFrame* NS_NewDateTimeControlFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext);
+
+ virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
+
+ NS_DECL_QUERYFRAME_TARGET(nsDateTimeControlFrame)
+ NS_DECL_QUERYFRAME
+ NS_DECL_FRAMEARENA_HELPERS
+
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override {
+ return MakeFrameName(NS_LITERAL_STRING("DateTimeControl"), aResult);
+ }
+#endif
+
+ virtual nsIAtom* GetType() const override;
+
+ virtual bool IsFrameOfType(uint32_t aFlags) const override
+ {
+ return nsContainerFrame::IsFrameOfType(aFlags &
+ ~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock));
+ }
+
+ // Reflow
+ virtual nscoord GetMinISize(nsRenderingContext* aRenderingContext) override;
+
+ virtual nscoord GetPrefISize(nsRenderingContext* aRenderingContext) override;
+
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowState,
+ nsReflowStatus& aStatus) override;
+
+ // nsIAnonymousContentCreator
+ virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override;
+ virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
+ uint32_t aFilter) override;
+
+ virtual nsresult AttributeChanged(int32_t aNameSpaceID,
+ nsIAtom* aAttribute,
+ int32_t aModType) override;
+
+ void ShowPicker();
+
+ void UpdateInputBoxValue(const nsAString& aValue);
+
+private:
+
+ nsCOMPtr<nsIDOMEventListener> mDateTimeListener;
+ nsCOMPtr<nsIContent> mDateTimeInputBox;
+};
+
+#endif // nsDateTimeControlFrame_h__
\ No newline at end of file
--- a/layout/generic/nsFrameIdList.h
+++ b/layout/generic/nsFrameIdList.h
@@ -14,16 +14,17 @@ FRAME_ID(nsBulletFrame)
FRAME_ID(nsButtonBoxFrame)
FRAME_ID(nsCanvasFrame)
FRAME_ID(nsColorControlFrame)
FRAME_ID(nsColumnSetFrame)
FRAME_ID(nsComboboxControlFrame)
FRAME_ID(nsComboboxDisplayFrame)
FRAME_ID(nsContainerFrame)
FRAME_ID(nsContinuingTextFrame)
+FRAME_ID(nsDateTimeControlFrame)
FRAME_ID(nsDeckFrame)
FRAME_ID(nsDocElementBoxFrame)
FRAME_ID(nsFieldSetFrame)
FRAME_ID(nsFileControlFrame)
FRAME_ID(nsFirstLetterFrame)
FRAME_ID(nsFirstLineFrame)
FRAME_ID(nsFlexContainerFrame)
FRAME_ID(nsFormControlFrame)
--- a/layout/generic/nsHTMLParts.h
+++ b/layout/generic/nsHTMLParts.h
@@ -170,16 +170,18 @@ NS_NewComboboxControlFrame(nsIPresShell*
nsIFrame*
NS_NewProgressFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
nsIFrame*
NS_NewMeterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
nsIFrame*
NS_NewRangeFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
nsIFrame*
NS_NewNumberControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewDateTimeControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
class DetailsFrame;
DetailsFrame*
NS_NewDetailsFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
// Table frame factories
class nsTableWrapperFrame;
nsTableWrapperFrame*
NS_NewTableWrapperFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
--- a/layout/style/res/html.css
+++ b/layout/style/res/html.css
@@ -762,16 +762,23 @@ audio:not([controls]) {
transform: translate(0) !important;
}
video > .caption-box {
position: relative;
overflow: hidden;
}
+/* datetime elements */
+
+input[type="time"] > xul|datetimebox {
+ display: flex;
+ -moz-binding: url("chrome://global/content/bindings/datetimebox.xml#time-input");
+}
+
/* details & summary */
/* Need to revert Bug 1259889 Part 2 when removing details preference. */
@supports -moz-bool-pref("dom.details_element.enabled") {
details > summary:first-of-type,
details > summary:-moz-native-anonymous {
display: list-item;
list-style: disclosure-closed inside;
}
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1130,16 +1130,19 @@ pref("dom.min_timeout_value", 4);
pref("dom.min_background_timeout_value", 1000);
// Don't use new input types
pref("dom.experimental_forms", false);
// Enable <input type=number>:
pref("dom.forms.number", true);
+// Enable date/time input types
+pref("dom.forms.datetime", true);
+
// Enable <input type=color> by default. It will be turned off for remaining
// platforms which don't have a color picker implemented yet.
pref("dom.forms.color", true);
// Support for new @autocomplete values
pref("dom.forms.autocomplete.experimental", false);
// Enables requestAutocomplete DOM API on forms.
new file mode 100644
--- /dev/null
+++ b/toolkit/content/datetime-child.js
@@ -0,0 +1,16 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "DateTimeContentHelper",
+ "resource://gre/modules/DateTimeContentHelper.jsm");
+
+addEventListener("mozshowdatetimepicker", event => {
+ if (!event.isTrusted) {
+ return;
+ }
+
+ new DateTimeContentHelper(event.target, this);
+});
--- a/toolkit/content/jar.mn
+++ b/toolkit/content/jar.mn
@@ -28,16 +28,17 @@ toolkit.jar:
content/global/aboutwebrtc/aboutWebrtc.css (aboutwebrtc/aboutWebrtc.css)
content/global/aboutwebrtc/aboutWebrtc.js (aboutwebrtc/aboutWebrtc.js)
content/global/aboutwebrtc/aboutWebrtc.html (aboutwebrtc/aboutWebrtc.html)
content/global/aboutSupport.js
* content/global/aboutSupport.xhtml
content/global/aboutTelemetry.js
content/global/aboutTelemetry.xhtml
content/global/aboutTelemetry.css
+ content/global/datetime-child.js
content/global/directionDetector.html
content/global/plugins.html
content/global/plugins.css
content/global/browser-child.js
content/global/browser-content.js
* content/global/buildconfig.html
content/global/contentAreaUtils.js
#ifndef MOZ_FENNEC
@@ -66,16 +67,18 @@ toolkit.jar:
content/global/treeUtils.js
content/global/viewZoomOverlay.js
content/global/bindings/autocomplete.xml (widgets/autocomplete.xml)
content/global/bindings/browser.xml (widgets/browser.xml)
content/global/bindings/button.xml (widgets/button.xml)
content/global/bindings/checkbox.xml (widgets/checkbox.xml)
content/global/bindings/colorpicker.xml (widgets/colorpicker.xml)
content/global/bindings/datetimepicker.xml (widgets/datetimepicker.xml)
+ content/global/bindings/datetimebox.xml (widgets/datetimebox.xml)
+ content/global/bindings/datetimebox.css (widgets/datetimebox.css)
* content/global/bindings/dialog.xml (widgets/dialog.xml)
content/global/bindings/editor.xml (widgets/editor.xml)
content/global/bindings/expander.xml (widgets/expander.xml)
content/global/bindings/filefield.xml (widgets/filefield.xml)
* content/global/bindings/findbar.xml (widgets/findbar.xml)
content/global/bindings/general.xml (widgets/general.xml)
content/global/bindings/groupbox.xml (widgets/groupbox.xml)
content/global/bindings/listbox.xml (widgets/listbox.xml)
new file mode 100644
--- /dev/null
+++ b/toolkit/content/widgets/datetimebox.css
@@ -0,0 +1,34 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+@namespace url("http://www.w3.org/1999/xhtml");
+@namespace xul url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+.datetime-input-box-wrapper {
+ -moz-appearance: none;
+ display: inline-flex;
+ cursor: text;
+ padding: 0px;
+ background-color: -moz-Field;
+ color: -moz-FieldText;
+}
+
+.datetime-input {
+ text-align: center;
+}
+
+.datetime-separator {
+ margin: 0 !important;
+}
+
+.datetime-reset-button {
+ background-image: url(chrome://global/skin/icons/searchfield-cancel.svg);
+ background-repeat: no-repeat;
+ background-size: 12px, 12px;
+ border: none;
+ height: 12px;
+ width: 12px;
+ margin-left: 3px;
+ margin-top: 1px;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/content/widgets/datetimebox.xml
@@ -0,0 +1,373 @@
+<?xml version="1.0"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<bindings id="datetimeboxBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:xbl="http://www.mozilla.org/xbl">
+
+ <binding id="time-input"
+ extends="chrome://global/content/bindings/datetimebox.xml#datetime-input-base">
+ <resources>
+ <stylesheet src="chrome://global/content/textbox.css"/>
+ <stylesheet src="chrome://global/skin/textbox.css"/>
+ <stylesheet src="chrome://global/content/bindings/datetimebox.css"/>
+ </resources>
+
+ <implementation>
+ <constructor>
+ <![CDATA[
+ // TODO: localization
+ this.amIndicator = "AM";
+ this.pmIndicator = "PM";
+ this.placeHolder = "--";
+ this.separator = ":";
+
+ this.mHourField =
+ document.getAnonymousElementByAttribute(this, "anonid", "input-one");
+ this.mHourField.classList.add("numeric");
+ this.mMinuteField =
+ document.getAnonymousElementByAttribute(this, "anonid", "input-two");
+ this.mMinuteField.classList.add("numeric");
+ this.mAMPMField =
+ document.getAnonymousElementByAttribute(this, "anonid", "input-three");
+
+ this.mHourField.placeholder = this.placeHolder;
+ this.mMinuteField.placeholder = this.placeHolder;
+ this.mAMPMField.placeholder = this.placeHolder;
+
+ this.mHourSeparator =
+ document.getAnonymousElementByAttribute(this, "anonid", "sep-first");
+ this.mHourSeparator.textContent = this.separator;
+ this.mSpaceSeparator =
+ document.getAnonymousElementByAttribute(this, "anonid", "sep-second");
+ // space between time and am/pm field
+ this.mSpaceSeparator.textContent = " ";
+
+ this.mMinuteSeparator = null;
+ this.mSecondField = null;
+ this.mSecondSeparator = null;
+ this.mMillisecondsField = null;
+
+ if (this.mInputElement.value) {
+ this.setFieldsFromInputValue();
+ }
+ ]]>
+ </constructor>
+
+ <method name="insertSecondField">
+ <body>
+ <![CDATA[
+ if (this.mSecondField) {
+ return;
+ }
+
+ var container = this.mMinuteField.parentNode;
+
+ this.mMinuteSeparator =
+ document.createElementNS("http://www.w3.org/1999/xhtml", "span");
+ this.mMinuteSeparator.textContent = this.separator;
+ this.mMinuteSeparator.setAttribute("class", "datetime-separator");
+ container.insertBefore(this.mMinuteSeparator, this.mSpaceSeparator);
+
+ this.mSecondField =
+ document.createElementNS("http://www.w3.org/1999/xhtml", "input");
+ this.mSecondField.classList.add("textbox-input");
+ this.mSecondField.classList.add("datetime-input");
+ this.mSecondField.classList.add("numeric");
+ this.mSecondField.setAttribute("size", "2");
+ this.mSecondField.setAttribute("maxlength", "2");
+ this.mSecondField.setAttribute("anonid", "input-four");
+
+ // Default to readonly to avoid step mismatch
+ this.mSecondField.setAttribute("readonly", "true");
+ this.mSecondField.placeholder = this.placeHolder;
+ container.insertBefore(this.mSecondField, this.mSpaceSeparator);
+ ]]>
+ </body>
+ </method>
+
+ <method name="setFieldsFromInputValue">
+ <body>
+ <![CDATA[
+ var value = this.mInputElement.value;
+ this.log("setFieldsFromInputValue: " + value);
+
+ if (!value) {
+ return;
+ }
+
+ var [hour, minute, second] = value.split(':');
+ if (hour >= 12) {
+ this.mAMPMField.value = this.pmIndicator;
+ } else {
+ this.mAMPMField.value = this.amIndicator;
+ }
+ hour = (hour % 12) || 12;
+ hour = (hour < 10) ? ("0" + hour) : hour;
+
+ this.mHourField.value = hour;
+ this.mMinuteField.value = minute;
+
+ // TODO: handle milliseconds.
+ if (second) {
+ this.insertSecondField();
+ this.mSecondField.value = second;
+ } else {
+ if (this.mSecondField) {
+ this.mSecondField.remove();
+ this.mSecondField = null;
+
+ this.mMinuteSeparator.remove();
+ this.mMinuteSeparator = null;
+ }
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="setInputValueFromFields">
+ <body>
+ <![CDATA[
+ if (this.isEmpty(this.mHourField.value) ||
+ this.isEmpty(this.mMinuteField.value) ||
+ (this.mSecondField && this.isEmpty(this.mSecondField.value))) {
+ return;
+ }
+
+ var hour = Number(this.mHourField.value);
+ var ampm = this.mAMPMField.value;
+ if (ampm == this.pmIndicator && hour < 12) {
+ hour += 12;
+ } else if (ampm == this.amIndicator && hour == 12) {
+ hour = 0;
+ }
+
+ hour = (hour < 10) ? ("0" + hour) : hour;
+
+ var time = hour + ":" + this.mMinuteField.value;
+ if (this.mSecondField) {
+ time += ":" + this.mSecondField.value;
+ }
+
+ this.log("setInputValueFromFields: " + time);
+ this.mInputElement.value = time;
+ ]]>
+ </body>
+
+ </method>
+ <method name="clearInputFields">
+ <body>
+ <![CDATA[
+ this.mHourField.value = "";
+ this.mMinuteField.value = "";
+ this.mAMPMField.value = "";
+
+ // Remove it for now (as Chrome does).
+ if (this.mSecondField) {
+ this.mSecondField.remove();
+ this.mSecondField = null;
+
+ this.mMinuteSeparator.remove();
+ this.mMinuteSeparator = null;
+ }
+
+ // TODO: milliseconds
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="datetime-input-base">
+ <resources>
+ <stylesheet src="chrome://global/content/textbox.css"/>
+ <stylesheet src="chrome://global/skin/textbox.css"/>
+ <stylesheet src="chrome://global/content/bindings/datetimebox.css"/>
+ </resources>
+
+ <content>
+ <html:div class="datetime-input-box-wrapper"
+ xbl:inherits="context,disabled,readonly">
+ <html:span>
+ <html:input anonid="input-one" class="textbox-input datetime-input"
+ size="2" maxlength="2"
+ xbl:inherits="disabled,readonly"/>
+ <html:span anonid="sep-first" class="datetime-separator"></html:span>
+ <html:input anonid="input-two" class="textbox-input datetime-input"
+ size="2" maxlength="2"
+ xbl:inherits="disabled,readonly"/>
+ <html:span anonid="sep-second" class="datetime-separator"></html:span>
+ <html:input anonid="input-three" class="textbox-input datetime-input"
+ size="2" maxlength="2"
+ xbl:inherits="disabled,readonly"/>
+ </html:span>
+
+ <html:button class="datetime-reset-button" anoid="reset-button"
+ onclick="document.getBindingParent(this).clearInputFields();"/>
+ </html:div>
+ </content>
+
+ <implementation>
+ <constructor>
+ <![CDATA[
+ this.debug = true;
+ this.mInputElement = this.parentNode;
+ this.log("datetime-input-base ctor: this.mInputElement value: " +
+ this.mInputElement.value);
+
+ this.mLastFocusedField = null;
+ this.advanceFocus = false;
+ this.mInputElement.addEventListener("focus", aEvent => {
+ if (aEvent.originalTarget == this.mInputElement) {
+ if (this.mLastFocusedField) {
+ this.mLastFocusedField.focus();
+ } else {
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "input-one").focus();
+ }
+ }
+ }, true);
+
+ this.addEventListener("datetime-value-changed", () => {
+ this.setFieldsFromInputValue();
+ });
+ ]]>
+ </constructor>
+
+ <method name="log">
+ <parameter name="aMsg"/>
+ <body>
+ <![CDATA[
+ if (this.debug) {
+ dump("[DateTimeBox] " + aMsg + "\n");
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="onFocus">
+ <parameter name="aEvent"/>
+ <body>
+ <![CDATA[
+ this.log("focus on: " + aEvent.originalTarget);
+
+ var target = aEvent.originalTarget;
+ if (target.type == "text") {
+ this.mLastFocusedField = target;
+ target.select();
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="isEmpty">
+ <parameter name="aValue"/>
+ <body>
+ return (!aValue || 0 === aValue.length);
+ </body>
+ </method>
+
+ <method name="clearInputFields">
+ <body>
+ // Not implemented
+ </body>
+ </method>
+
+ <method name="setFieldsFromInputValue">
+ <body>
+ // Not implemented
+ </body>
+ </method>
+
+ <method name="setInputValueFromFields">
+ <body>
+ // Not implemented
+ </body>
+ </method>
+
+ </implementation>
+
+ <handlers>
+
+ <handler event="keyup">
+ <![CDATA[
+ if (!this.advanceFocus) {
+ return;
+ }
+
+ var focusedInput = this.mLastFocusedField;
+ if (focusedInput.value.length >= focusedInput.maxLength) {
+ var next = focusedInput.nextElementSibling;
+ while(next != null) {
+ if (next.type == "text") {
+ next.focus();
+ this.advanceFocus = false;
+ break;
+ }
+ next = next.nextElementSibling;
+ }
+ }
+ ]]>
+ </handler>
+
+ <handler event="focus" phase="capturing">
+ this.onFocus(event);
+ </handler>
+
+ <handler event="blur" phase="capturing">
+ <![CDATA[
+ this.log("blur");
+ this.setInputValueFromFields();
+ ]]>
+ </handler>
+
+ <handler event="click" phase="capturing">
+ <![CDATA[
+ this.log("click on: " + event.originalTarget);
+
+ if (event.originalTarget == document.getAnonymousElementByAttribute(this, "anonid", "reset-button")) {
+ this.log("reset-button");
+ }
+
+ if (!(event.originalTarget instanceof HTMLButtonElement)) {
+ this.dispatchEvent(new CustomEvent("ShowDateTimePicker"));
+ }
+ ]]>
+ </handler>
+
+ <handler event="keypress" phase="capturing">
+ <![CDATA[
+ var targetField = event.originalTarget;
+ var key = event.key;
+ if (key != "Tab" && key != "Backspace" &&
+ targetField.classList.contains("numeric") && !key.match(/[0-9]/)) {
+ event.preventDefault();
+ return false;
+ }
+
+ this.advanceFocus = (key == "Tab") ? false : true;
+ ]]>
+ </handler>
+
+ <handler event="keypress" keycode="VK_UP">
+ <![CDATA[
+ this.mInputElement.stepUp();
+ this.setFieldsFromInputValue();
+ ]]>
+ </handler>
+
+ <handler event="keypress" keycode="VK_DOWN">
+ <![CDATA[
+ this.mInputElement.stepDown();
+ this.setFieldsFromInputValue();
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+
+</bindings>
--- a/toolkit/content/widgets/remote-browser.xml
+++ b/toolkit/content/widgets/remote-browser.xml
@@ -389,16 +389,22 @@
this.messageManager.loadFrameScript("chrome://global/content/browser-child.js", true);
if (this.hasAttribute("selectmenulist")) {
this.messageManager.addMessageListener("Forms:ShowDropDown", this);
this.messageManager.addMessageListener("Forms:HideDropDown", this);
this.messageManager.loadFrameScript("chrome://global/content/select-child.js", true);
}
+ if (this.hasAttribute("datetimepicker")) {
+ this.messageManager.addMessageListener("Forms:ShowDateTimePicker", this);
+ this.messageManager.addMessageListener("Forms:HideDateTimePicker", this);
+ this.messageManager.loadFrameScript("chrome://global/content/datetime-child.js", true);
+ }
+
if (!this.hasAttribute("disablehistory")) {
Services.obs.addObserver(this, "browser:purge-session-history", true);
}
let jsm = "resource://gre/modules/RemoteController.jsm";
let RemoteController = Components.utils.import(jsm, {}).RemoteController;
this._controller = new RemoteController(this);
this.controllers.appendController(this._controller);
@@ -474,16 +480,28 @@
let menulist = document.getElementById(this.getAttribute("selectmenulist"));
menulist.menupopup.style.direction = data.direction;
this._selectParentHelper.populate(menulist, data.options, data.selectedIndex, this._fullZoom);
this._selectParentHelper.open(this, menulist, data.rect);
break;
}
+ case "Forms:ShowDateTimePicker": {
+ dump("Forms:ShowDateTimePicker\n");
+ if (!this._dateTimeParentHelper) {
+ this._dateTimeParentHelper =
+ Cu.import("resource://gre/modules/DateTimeParentHelper.jsm", {}).DateTimeParentHelper;
+ }
+
+ let picker = document.getElementById(this.getAttribute("datetimepicker"));
+ this._dateTimeParentHelper.open(this, picker, data.rect);
+ break;
+ }
+
case "FullZoomChange": {
this._fullZoom = data.value;
let event = document.createEvent("Events");
event.initEvent("FullZoomChange", true, false);
this.dispatchEvent(event);
break;
}
new file mode 100644
--- /dev/null
+++ b/toolkit/modules/DateTimeContentHelper.jsm
@@ -0,0 +1,49 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
+ "resource://gre/modules/BrowserUtils.jsm");
+
+this.EXPORTED_SYMBOLS = [
+ "DateTimeContentHelper"
+];
+
+this.DateTimeContentHelper = function(aElement, aGlobal) {
+
+ this.element = aElement;
+ this.global = aGlobal;
+ this.init();
+ this.showPicker();
+}
+
+this.DateTimeContentHelper.prototype = {
+ init: function() {
+ // TODO
+ },
+
+ showPicker: function() {
+ let rect = this._getBoundingContentRect();
+
+ this.global.sendAsyncMessage("Forms:ShowDateTimePicker", {
+ rect: rect,
+ direction: getComputedDirection(this.element)
+ });
+ },
+
+ _getBoundingContentRect: function() {
+ return BrowserUtils.getElementBoundingScreenRect(this.element);
+ },
+};
+
+function getComputedDirection(element) {
+ return element.ownerDocument.defaultView.getComputedStyle(element).getPropertyValue("direction");
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/modules/DateTimeParentHelper.jsm
@@ -0,0 +1,21 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = [
+ "DateTimeParentHelper"
+];
+
+var currentBrowser = null;
+
+this.DateTimeParentHelper = {
+ open: function(browser, panel, rect) {
+ panel.hidden = false;
+ currentBrowser = browser;
+
+ panel.openPopupAtScreenRect("after_start", rect.left, rect.top, rect.width,
+ rect.height, false, false);
+ },
+};
--- a/toolkit/modules/moz.build
+++ b/toolkit/modules/moz.build
@@ -30,16 +30,18 @@ EXTRA_JS_MODULES += [
'BinarySearch.jsm',
'BrowserUtils.jsm',
'CanonicalJSON.jsm',
'CertUtils.jsm',
'CharsetMenu.jsm',
'ClientID.jsm',
'Color.jsm',
'Console.jsm',
+ 'DateTimeContentHelper.jsm',
+ 'DateTimeParentHelper.jsm',
'debug.js',
'DeferredTask.jsm',
'Deprecated.jsm',
'FileUtils.jsm',
'Finder.jsm',
'FinderHighlighter.jsm',
'FinderIterator.jsm',
'Geometry.jsm',