123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416 |
- /* -*- 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/. */
- /**
- * This frame type is used for input type=date, time, month, week, and
- * datetime-local.
- */
- #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"
- #include "nsIDateTimeInputArea.h"
- #include "nsIObserverService.h"
- #include "nsIDOMHTMLInputElement.h"
- #include "nsIDOMMutationEvent.h"
- #include "jsapi.h"
- #include "nsJSUtils.h"
- #include "nsThreadUtils.h"
- using namespace mozilla;
- using namespace mozilla::dom;
- 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(&mInputAreaContent);
- nsContainerFrame::DestroyFrom(aDestructRoot);
- }
- void
- nsDateTimeControlFrame::UpdateInputBoxValue()
- {
- nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
- do_QueryInterface(mInputAreaContent);
- if (inputAreaContent) {
- inputAreaContent->NotifyInputElementValueChanged();
- }
- }
- void
- nsDateTimeControlFrame::SetValueFromPicker(const DateTimeValue& aValue)
- {
- nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
- do_QueryInterface(mInputAreaContent);
- if (inputAreaContent) {
- AutoJSAPI api;
- if (!api.Init(mContent->OwnerDoc()->GetScopeObject())) {
- return;
- }
- JSObject* wrapper = mContent->GetWrapper();
- if (!wrapper) {
- return;
- }
- JSObject* scope = xpc::GetXBLScope(api.cx(), wrapper);
- AutoJSAPI jsapi;
- if (!scope || !jsapi.Init(scope)) {
- return;
- }
- JS::Rooted<JS::Value> jsValue(jsapi.cx());
- if (!ToJSValue(jsapi.cx(), aValue, &jsValue)) {
- return;
- }
- inputAreaContent->SetValueFromPicker(jsValue);
- }
- }
- void
- nsDateTimeControlFrame::SetPickerState(bool aOpen)
- {
- nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
- do_QueryInterface(mInputAreaContent);
- if (inputAreaContent) {
- inputAreaContent->SetPickerState(aOpen);
- }
- }
- void
- nsDateTimeControlFrame::HandleFocusEvent()
- {
- nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
- do_QueryInterface(mInputAreaContent);
- if (inputAreaContent) {
- inputAreaContent->FocusInnerTextBox();
- }
- }
- void
- nsDateTimeControlFrame::HandleBlurEvent()
- {
- nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
- do_QueryInterface(mInputAreaContent);
- if (inputAreaContent) {
- inputAreaContent->BlurInnerTextBox();
- }
- }
- 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_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
- ("enter nsDateTimeControlFrame::Reflow: availSize=%d,%d",
- aReflowInput.AvailableWidth(),
- aReflowInput.AvailableHeight()));
- NS_ASSERTION(mInputAreaContent, "The input area content 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* inputAreaFrame = mFrames.FirstChild();
- if (!inputAreaFrame) { // display:none?
- if (contentBoxBSize == NS_INTRINSICSIZE) {
- contentBoxBSize = 0;
- borderBoxBSize =
- aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM);
- }
- } else {
- NS_ASSERTION(inputAreaFrame->GetContent() == mInputAreaContent,
- "What is this child doing here?");
- ReflowOutput childDesiredSize(aReflowInput);
- WritingMode wm = inputAreaFrame->GetWritingMode();
- LogicalSize availSize = aReflowInput.ComputedSize(wm);
- availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
- ReflowInput childReflowOuput(aPresContext, aReflowInput,
- inputAreaFrame, availSize);
- // Convert input area margin into my own writing-mode (in case it differs):
- LogicalMargin childMargin =
- childReflowOuput.ComputedLogicalMargin().ConvertTo(myWM, wm);
- // offsets of input area frame within this frame:
- LogicalPoint
- childOffset(myWM,
- aReflowInput.ComputedLogicalBorderPadding().IStart(myWM) +
- childMargin.IStart(myWM),
- aReflowInput.ComputedLogicalBorderPadding().BStart(myWM) +
- childMargin.BStart(myWM));
- nsReflowStatus childStatus;
- // We initially reflow the child with a dummy containerSize; positioning
- // will be fixed later.
- const nsSize dummyContainerSize;
- ReflowChild(inputAreaFrame, aPresContext, childDesiredSize,
- childReflowOuput, myWM, childOffset, 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 childMarginBoxBSize =
- childDesiredSize.BSize(myWM) + childMargin.BStartEnd(myWM);
- if (contentBoxBSize == NS_INTRINSICSIZE) {
- // We are intrinsically sized -- we should shrinkwrap the input area's
- // block-size:
- contentBoxBSize = childMarginBoxBSize;
- // 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 - childMarginBoxBSize;
- childOffset.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(inputAreaFrame, aPresContext, childDesiredSize,
- &childReflowOuput, myWM, childOffset, borderBoxSize, 0);
- nsSize contentBoxSize =
- LogicalSize(myWM, contentBoxISize, contentBoxBSize).
- GetPhysicalSize(myWM);
- aDesiredSize.SetBlockStartAscent(
- childDesiredSize.BlockStartAscent() +
- inputAreaFrame->BStart(aReflowInput.GetWritingMode(),
- contentBoxSize));
- }
- LogicalSize logicalDesiredSize(myWM, borderBoxISize, borderBoxBSize);
- aDesiredSize.SetSize(myWM, logicalDesiredSize);
- aDesiredSize.SetOverflowAreasToDesiredBounds();
- if (inputAreaFrame) {
- ConsiderChildOverflow(aDesiredSize.mOverflowAreas, inputAreaFrame);
- }
- 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 =
- mContent->GetComposedDoc()->NodeInfoManager();
- RefPtr<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(mInputAreaContent), nodeInfo.forget());
- aElements.AppendElement(mInputAreaContent);
- // Propogate our tabindex.
- nsAutoString tabIndexStr;
- if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tabindex, tabIndexStr)) {
- mInputAreaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::tabindex,
- tabIndexStr, false);
- }
- // Propagate our readonly state.
- nsAutoString readonly;
- if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::readonly, readonly)) {
- mInputAreaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::readonly, readonly,
- false);
- }
- SyncDisabledState();
- return NS_OK;
- }
- void
- nsDateTimeControlFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
- uint32_t aFilter)
- {
- if (mInputAreaContent) {
- aElements.AppendElement(mInputAreaContent);
- }
- }
- void
- nsDateTimeControlFrame::SyncDisabledState()
- {
- EventStates eventStates = mContent->AsElement()->State();
- if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) {
- mInputAreaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled,
- EmptyString(), true);
- } else {
- mInputAreaContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true);
- }
- }
- nsresult
- nsDateTimeControlFrame::AttributeChanged(int32_t aNameSpaceID,
- nsIAtom* aAttribute,
- int32_t aModType)
- {
- NS_ASSERTION(mInputAreaContent, "The input area content must exist!");
- // nsGkAtoms::disabled is handled by SyncDisabledState
- if (aNameSpaceID == kNameSpaceID_None) {
- if (aAttribute == nsGkAtoms::value ||
- aAttribute == nsGkAtoms::readonly ||
- aAttribute == nsGkAtoms::tabindex) {
- MOZ_ASSERT(mContent->IsHTMLElement(nsGkAtoms::input), "bad cast");
- auto contentAsInputElem = static_cast<dom::HTMLInputElement*>(mContent);
- // 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 (contentAsInputElem->GetType() == NS_FORM_INPUT_TIME ||
- contentAsInputElem->GetType() == NS_FORM_INPUT_DATE) {
- if (aAttribute == nsGkAtoms::value) {
- nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
- do_QueryInterface(mInputAreaContent);
- if (inputAreaContent) {
- nsContentUtils::AddScriptRunner(NewRunnableMethod(inputAreaContent,
- &nsIDateTimeInputArea::NotifyInputElementValueChanged));
- }
- } else {
- if (aModType == nsIDOMMutationEvent::REMOVAL) {
- mInputAreaContent->UnsetAttr(aNameSpaceID, aAttribute, true);
- } else {
- MOZ_ASSERT(aModType == nsIDOMMutationEvent::ADDITION ||
- aModType == nsIDOMMutationEvent::MODIFICATION);
- nsAutoString value;
- mContent->GetAttr(aNameSpaceID, aAttribute, value);
- mInputAreaContent->SetAttr(aNameSpaceID, aAttribute, value, true);
- }
- }
- }
- }
- }
- return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
- aModType);
- }
- void
- nsDateTimeControlFrame::ContentStatesChanged(EventStates aStates)
- {
- if (aStates.HasState(NS_EVENT_STATE_DISABLED)) {
- nsContentUtils::AddScriptRunner(new SyncDisabledStateEvent(this));
- }
- }
- nsIAtom*
- nsDateTimeControlFrame::GetType() const
- {
- return nsGkAtoms::dateTimeControlFrame;
- }
|