nsDateTimeControlFrame.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. /**
  6. * This frame type is used for input type=date, time, month, week, and
  7. * datetime-local.
  8. */
  9. #include "nsDateTimeControlFrame.h"
  10. #include "nsContentUtils.h"
  11. #include "nsFormControlFrame.h"
  12. #include "nsGkAtoms.h"
  13. #include "nsContentUtils.h"
  14. #include "nsContentCreatorFunctions.h"
  15. #include "nsContentList.h"
  16. #include "mozilla/dom/HTMLInputElement.h"
  17. #include "nsNodeInfoManager.h"
  18. #include "nsIDateTimeInputArea.h"
  19. #include "nsIObserverService.h"
  20. #include "nsIDOMHTMLInputElement.h"
  21. #include "nsIDOMMutationEvent.h"
  22. #include "jsapi.h"
  23. #include "nsJSUtils.h"
  24. #include "nsThreadUtils.h"
  25. using namespace mozilla;
  26. using namespace mozilla::dom;
  27. nsIFrame*
  28. NS_NewDateTimeControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
  29. {
  30. return new (aPresShell) nsDateTimeControlFrame(aContext);
  31. }
  32. NS_IMPL_FRAMEARENA_HELPERS(nsDateTimeControlFrame)
  33. NS_QUERYFRAME_HEAD(nsDateTimeControlFrame)
  34. NS_QUERYFRAME_ENTRY(nsDateTimeControlFrame)
  35. NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
  36. NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
  37. nsDateTimeControlFrame::nsDateTimeControlFrame(nsStyleContext* aContext)
  38. : nsContainerFrame(aContext)
  39. {
  40. }
  41. void
  42. nsDateTimeControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
  43. {
  44. nsContentUtils::DestroyAnonymousContent(&mInputAreaContent);
  45. nsContainerFrame::DestroyFrom(aDestructRoot);
  46. }
  47. void
  48. nsDateTimeControlFrame::UpdateInputBoxValue()
  49. {
  50. nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
  51. do_QueryInterface(mInputAreaContent);
  52. if (inputAreaContent) {
  53. inputAreaContent->NotifyInputElementValueChanged();
  54. }
  55. }
  56. void
  57. nsDateTimeControlFrame::SetValueFromPicker(const DateTimeValue& aValue)
  58. {
  59. nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
  60. do_QueryInterface(mInputAreaContent);
  61. if (inputAreaContent) {
  62. AutoJSAPI api;
  63. if (!api.Init(mContent->OwnerDoc()->GetScopeObject())) {
  64. return;
  65. }
  66. JSObject* wrapper = mContent->GetWrapper();
  67. if (!wrapper) {
  68. return;
  69. }
  70. JSObject* scope = xpc::GetXBLScope(api.cx(), wrapper);
  71. AutoJSAPI jsapi;
  72. if (!scope || !jsapi.Init(scope)) {
  73. return;
  74. }
  75. JS::Rooted<JS::Value> jsValue(jsapi.cx());
  76. if (!ToJSValue(jsapi.cx(), aValue, &jsValue)) {
  77. return;
  78. }
  79. inputAreaContent->SetValueFromPicker(jsValue);
  80. }
  81. }
  82. void
  83. nsDateTimeControlFrame::SetPickerState(bool aOpen)
  84. {
  85. nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
  86. do_QueryInterface(mInputAreaContent);
  87. if (inputAreaContent) {
  88. inputAreaContent->SetPickerState(aOpen);
  89. }
  90. }
  91. void
  92. nsDateTimeControlFrame::HandleFocusEvent()
  93. {
  94. nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
  95. do_QueryInterface(mInputAreaContent);
  96. if (inputAreaContent) {
  97. inputAreaContent->FocusInnerTextBox();
  98. }
  99. }
  100. void
  101. nsDateTimeControlFrame::HandleBlurEvent()
  102. {
  103. nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
  104. do_QueryInterface(mInputAreaContent);
  105. if (inputAreaContent) {
  106. inputAreaContent->BlurInnerTextBox();
  107. }
  108. }
  109. nscoord
  110. nsDateTimeControlFrame::GetMinISize(nsRenderingContext* aRenderingContext)
  111. {
  112. nscoord result;
  113. DISPLAY_MIN_WIDTH(this, result);
  114. nsIFrame* kid = mFrames.FirstChild();
  115. if (kid) { // display:none?
  116. result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
  117. kid,
  118. nsLayoutUtils::MIN_ISIZE);
  119. } else {
  120. result = 0;
  121. }
  122. return result;
  123. }
  124. nscoord
  125. nsDateTimeControlFrame::GetPrefISize(nsRenderingContext* aRenderingContext)
  126. {
  127. nscoord result;
  128. DISPLAY_PREF_WIDTH(this, result);
  129. nsIFrame* kid = mFrames.FirstChild();
  130. if (kid) { // display:none?
  131. result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
  132. kid,
  133. nsLayoutUtils::PREF_ISIZE);
  134. } else {
  135. result = 0;
  136. }
  137. return result;
  138. }
  139. void
  140. nsDateTimeControlFrame::Reflow(nsPresContext* aPresContext,
  141. ReflowOutput& aDesiredSize,
  142. const ReflowInput& aReflowInput,
  143. nsReflowStatus& aStatus)
  144. {
  145. MarkInReflow();
  146. DO_GLOBAL_REFLOW_COUNT("nsDateTimeControlFrame");
  147. DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
  148. NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
  149. ("enter nsDateTimeControlFrame::Reflow: availSize=%d,%d",
  150. aReflowInput.AvailableWidth(),
  151. aReflowInput.AvailableHeight()));
  152. NS_ASSERTION(mInputAreaContent, "The input area content must exist!");
  153. const WritingMode myWM = aReflowInput.GetWritingMode();
  154. // The ISize of our content box, which is the available ISize
  155. // for our anonymous content:
  156. const nscoord contentBoxISize = aReflowInput.ComputedISize();
  157. nscoord contentBoxBSize = aReflowInput.ComputedBSize();
  158. // Figure out our border-box sizes as well (by adding borderPadding to
  159. // content-box sizes):
  160. const nscoord borderBoxISize = contentBoxISize +
  161. aReflowInput.ComputedLogicalBorderPadding().IStartEnd(myWM);
  162. nscoord borderBoxBSize;
  163. if (contentBoxBSize != NS_INTRINSICSIZE) {
  164. borderBoxBSize = contentBoxBSize +
  165. aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM);
  166. } // else, we'll figure out borderBoxBSize after we resolve contentBoxBSize.
  167. nsIFrame* inputAreaFrame = mFrames.FirstChild();
  168. if (!inputAreaFrame) { // display:none?
  169. if (contentBoxBSize == NS_INTRINSICSIZE) {
  170. contentBoxBSize = 0;
  171. borderBoxBSize =
  172. aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM);
  173. }
  174. } else {
  175. NS_ASSERTION(inputAreaFrame->GetContent() == mInputAreaContent,
  176. "What is this child doing here?");
  177. ReflowOutput childDesiredSize(aReflowInput);
  178. WritingMode wm = inputAreaFrame->GetWritingMode();
  179. LogicalSize availSize = aReflowInput.ComputedSize(wm);
  180. availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
  181. ReflowInput childReflowOuput(aPresContext, aReflowInput,
  182. inputAreaFrame, availSize);
  183. // Convert input area margin into my own writing-mode (in case it differs):
  184. LogicalMargin childMargin =
  185. childReflowOuput.ComputedLogicalMargin().ConvertTo(myWM, wm);
  186. // offsets of input area frame within this frame:
  187. LogicalPoint
  188. childOffset(myWM,
  189. aReflowInput.ComputedLogicalBorderPadding().IStart(myWM) +
  190. childMargin.IStart(myWM),
  191. aReflowInput.ComputedLogicalBorderPadding().BStart(myWM) +
  192. childMargin.BStart(myWM));
  193. nsReflowStatus childStatus;
  194. // We initially reflow the child with a dummy containerSize; positioning
  195. // will be fixed later.
  196. const nsSize dummyContainerSize;
  197. ReflowChild(inputAreaFrame, aPresContext, childDesiredSize,
  198. childReflowOuput, myWM, childOffset, dummyContainerSize, 0,
  199. childStatus);
  200. MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(childStatus),
  201. "We gave our child unconstrained available block-size, "
  202. "so it should be complete");
  203. nscoord childMarginBoxBSize =
  204. childDesiredSize.BSize(myWM) + childMargin.BStartEnd(myWM);
  205. if (contentBoxBSize == NS_INTRINSICSIZE) {
  206. // We are intrinsically sized -- we should shrinkwrap the input area's
  207. // block-size:
  208. contentBoxBSize = childMarginBoxBSize;
  209. // Make sure we obey min/max-bsize in the case when we're doing intrinsic
  210. // sizing (we get it for free when we have a non-intrinsic
  211. // aReflowInput.ComputedBSize()). Note that we do this before
  212. // adjusting for borderpadding, since ComputedMaxBSize and
  213. // ComputedMinBSize are content heights.
  214. contentBoxBSize =
  215. NS_CSS_MINMAX(contentBoxBSize,
  216. aReflowInput.ComputedMinBSize(),
  217. aReflowInput.ComputedMaxBSize());
  218. borderBoxBSize = contentBoxBSize +
  219. aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM);
  220. }
  221. // Center child in block axis
  222. nscoord extraSpace = contentBoxBSize - childMarginBoxBSize;
  223. childOffset.B(myWM) += std::max(0, extraSpace / 2);
  224. // Needed in FinishReflowChild, for logical-to-physical conversion:
  225. nsSize borderBoxSize = LogicalSize(myWM, borderBoxISize, borderBoxBSize).
  226. GetPhysicalSize(myWM);
  227. // Place the child
  228. FinishReflowChild(inputAreaFrame, aPresContext, childDesiredSize,
  229. &childReflowOuput, myWM, childOffset, borderBoxSize, 0);
  230. nsSize contentBoxSize =
  231. LogicalSize(myWM, contentBoxISize, contentBoxBSize).
  232. GetPhysicalSize(myWM);
  233. aDesiredSize.SetBlockStartAscent(
  234. childDesiredSize.BlockStartAscent() +
  235. inputAreaFrame->BStart(aReflowInput.GetWritingMode(),
  236. contentBoxSize));
  237. }
  238. LogicalSize logicalDesiredSize(myWM, borderBoxISize, borderBoxBSize);
  239. aDesiredSize.SetSize(myWM, logicalDesiredSize);
  240. aDesiredSize.SetOverflowAreasToDesiredBounds();
  241. if (inputAreaFrame) {
  242. ConsiderChildOverflow(aDesiredSize.mOverflowAreas, inputAreaFrame);
  243. }
  244. FinishAndStoreOverflow(&aDesiredSize);
  245. aStatus = NS_FRAME_COMPLETE;
  246. NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
  247. ("exit nsDateTimeControlFrame::Reflow: size=%d,%d",
  248. aDesiredSize.Width(), aDesiredSize.Height()));
  249. NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
  250. }
  251. nsresult
  252. nsDateTimeControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
  253. {
  254. // Set up "datetimebox" XUL element which will be XBL-bound to the
  255. // actual controls.
  256. nsNodeInfoManager* nodeInfoManager =
  257. mContent->GetComposedDoc()->NodeInfoManager();
  258. RefPtr<NodeInfo> nodeInfo =
  259. nodeInfoManager->GetNodeInfo(nsGkAtoms::datetimebox, nullptr,
  260. kNameSpaceID_XUL, nsIDOMNode::ELEMENT_NODE);
  261. NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
  262. NS_TrustedNewXULElement(getter_AddRefs(mInputAreaContent), nodeInfo.forget());
  263. aElements.AppendElement(mInputAreaContent);
  264. // Propogate our tabindex.
  265. nsAutoString tabIndexStr;
  266. if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tabindex, tabIndexStr)) {
  267. mInputAreaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::tabindex,
  268. tabIndexStr, false);
  269. }
  270. // Propagate our readonly state.
  271. nsAutoString readonly;
  272. if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::readonly, readonly)) {
  273. mInputAreaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::readonly, readonly,
  274. false);
  275. }
  276. SyncDisabledState();
  277. return NS_OK;
  278. }
  279. void
  280. nsDateTimeControlFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
  281. uint32_t aFilter)
  282. {
  283. if (mInputAreaContent) {
  284. aElements.AppendElement(mInputAreaContent);
  285. }
  286. }
  287. void
  288. nsDateTimeControlFrame::SyncDisabledState()
  289. {
  290. EventStates eventStates = mContent->AsElement()->State();
  291. if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) {
  292. mInputAreaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled,
  293. EmptyString(), true);
  294. } else {
  295. mInputAreaContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true);
  296. }
  297. }
  298. nsresult
  299. nsDateTimeControlFrame::AttributeChanged(int32_t aNameSpaceID,
  300. nsIAtom* aAttribute,
  301. int32_t aModType)
  302. {
  303. NS_ASSERTION(mInputAreaContent, "The input area content must exist!");
  304. // nsGkAtoms::disabled is handled by SyncDisabledState
  305. if (aNameSpaceID == kNameSpaceID_None) {
  306. if (aAttribute == nsGkAtoms::value ||
  307. aAttribute == nsGkAtoms::readonly ||
  308. aAttribute == nsGkAtoms::tabindex) {
  309. MOZ_ASSERT(mContent->IsHTMLElement(nsGkAtoms::input), "bad cast");
  310. auto contentAsInputElem = static_cast<dom::HTMLInputElement*>(mContent);
  311. // If script changed the <input>'s type before setting these attributes
  312. // then we don't need to do anything since we are going to be reframed.
  313. if (contentAsInputElem->GetType() == NS_FORM_INPUT_TIME ||
  314. contentAsInputElem->GetType() == NS_FORM_INPUT_DATE) {
  315. if (aAttribute == nsGkAtoms::value) {
  316. nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
  317. do_QueryInterface(mInputAreaContent);
  318. if (inputAreaContent) {
  319. nsContentUtils::AddScriptRunner(NewRunnableMethod(inputAreaContent,
  320. &nsIDateTimeInputArea::NotifyInputElementValueChanged));
  321. }
  322. } else {
  323. if (aModType == nsIDOMMutationEvent::REMOVAL) {
  324. mInputAreaContent->UnsetAttr(aNameSpaceID, aAttribute, true);
  325. } else {
  326. MOZ_ASSERT(aModType == nsIDOMMutationEvent::ADDITION ||
  327. aModType == nsIDOMMutationEvent::MODIFICATION);
  328. nsAutoString value;
  329. mContent->GetAttr(aNameSpaceID, aAttribute, value);
  330. mInputAreaContent->SetAttr(aNameSpaceID, aAttribute, value, true);
  331. }
  332. }
  333. }
  334. }
  335. }
  336. return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
  337. aModType);
  338. }
  339. void
  340. nsDateTimeControlFrame::ContentStatesChanged(EventStates aStates)
  341. {
  342. if (aStates.HasState(NS_EVENT_STATE_DISABLED)) {
  343. nsContentUtils::AddScriptRunner(new SyncDisabledStateEvent(this));
  344. }
  345. }
  346. nsIAtom*
  347. nsDateTimeControlFrame::GetType() const
  348. {
  349. return nsGkAtoms::dateTimeControlFrame;
  350. }