/src/mozilla-central/dom/html/nsGenericHTMLElement.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "mozilla/ArrayUtils.h" |
8 | | #include "mozilla/DeclarationBlock.h" |
9 | | #include "mozilla/EventDispatcher.h" |
10 | | #include "mozilla/EventListenerManager.h" |
11 | | #include "mozilla/EventStateManager.h" |
12 | | #include "mozilla/EventStates.h" |
13 | | #include "mozilla/MappedDeclarations.h" |
14 | | #include "mozilla/Likely.h" |
15 | | #include "mozilla/MouseEvents.h" |
16 | | #include "mozilla/TextEditor.h" |
17 | | |
18 | | #include "nscore.h" |
19 | | #include "nsGenericHTMLElement.h" |
20 | | #include "nsAttrValueInlines.h" |
21 | | #include "nsCOMPtr.h" |
22 | | #include "nsAtom.h" |
23 | | #include "nsQueryObject.h" |
24 | | #include "nsIContentInlines.h" |
25 | | #include "nsIContentViewer.h" |
26 | | #include "nsIDocument.h" |
27 | | #include "nsIDocumentEncoder.h" |
28 | | #include "nsIDOMWindow.h" |
29 | | #include "nsMappedAttributes.h" |
30 | | #include "nsHTMLStyleSheet.h" |
31 | | #include "nsIHTMLDocument.h" |
32 | | #include "nsPIDOMWindow.h" |
33 | | #include "nsIURL.h" |
34 | | #include "nsEscape.h" |
35 | | #include "nsIFrameInlines.h" |
36 | | #include "nsIScrollableFrame.h" |
37 | | #include "nsView.h" |
38 | | #include "nsViewManager.h" |
39 | | #include "nsIWidget.h" |
40 | | #include "nsRange.h" |
41 | | #include "nsIPresShell.h" |
42 | | #include "nsPresContext.h" |
43 | | #include "nsIDocShell.h" |
44 | | #include "nsNameSpaceManager.h" |
45 | | #include "nsError.h" |
46 | | #include "nsIPrincipal.h" |
47 | | #include "nsContainerFrame.h" |
48 | | #include "nsStyleUtil.h" |
49 | | |
50 | | #include "mozilla/PresState.h" |
51 | | #include "nsILayoutHistoryState.h" |
52 | | |
53 | | #include "nsHTMLParts.h" |
54 | | #include "nsContentUtils.h" |
55 | | #include "mozilla/dom/DirectionalityUtils.h" |
56 | | #include "mozilla/dom/DocumentOrShadowRoot.h" |
57 | | #include "nsString.h" |
58 | | #include "nsUnicharUtils.h" |
59 | | #include "nsGkAtoms.h" |
60 | | #include "nsDOMCSSDeclaration.h" |
61 | | #include "nsITextControlFrame.h" |
62 | | #include "nsIForm.h" |
63 | | #include "nsIFormControl.h" |
64 | | #include "mozilla/dom/HTMLFormElement.h" |
65 | | #include "nsFocusManager.h" |
66 | | #include "nsAttrValueOrString.h" |
67 | | |
68 | | #include "mozilla/InternalMutationEvent.h" |
69 | | #include "nsDOMStringMap.h" |
70 | | |
71 | | #include "nsLayoutUtils.h" |
72 | | #include "mozAutoDocUpdate.h" |
73 | | #include "nsHtml5Module.h" |
74 | | #include "nsITextControlElement.h" |
75 | | #include "mozilla/dom/ElementInlines.h" |
76 | | #include "HTMLFieldSetElement.h" |
77 | | #include "nsTextNode.h" |
78 | | #include "HTMLBRElement.h" |
79 | | #include "HTMLMenuElement.h" |
80 | | #include "nsDOMMutationObserver.h" |
81 | | #include "mozilla/Preferences.h" |
82 | | #include "mozilla/dom/FromParser.h" |
83 | | #include "mozilla/dom/Link.h" |
84 | | #include "mozilla/BloomFilter.h" |
85 | | #include "mozilla/dom/ScriptLoader.h" |
86 | | |
87 | | #include "nsVariant.h" |
88 | | #include "nsDOMTokenList.h" |
89 | | #include "nsThreadUtils.h" |
90 | | #include "nsTextFragment.h" |
91 | | #include "mozilla/dom/BindingUtils.h" |
92 | | #include "mozilla/dom/MouseEventBinding.h" |
93 | | #include "mozilla/dom/TouchEvent.h" |
94 | | #include "mozilla/ErrorResult.h" |
95 | | #include "nsHTMLDocument.h" |
96 | | #include "nsGlobalWindow.h" |
97 | | #include "mozilla/dom/HTMLBodyElement.h" |
98 | | #include "imgIContainer.h" |
99 | | #include "nsComputedDOMStyle.h" |
100 | | #include "ReferrerPolicy.h" |
101 | | #include "mozilla/dom/HTMLLabelElement.h" |
102 | | #include "mozilla/dom/HTMLInputElement.h" |
103 | | |
104 | | using namespace mozilla; |
105 | | using namespace mozilla::dom; |
106 | | |
107 | | nsresult |
108 | | nsGenericHTMLElement::CopyInnerTo(Element* aDst) |
109 | 0 | { |
110 | 0 | MOZ_ASSERT(!aDst->GetUncomposedDoc(), |
111 | 0 | "Should not CopyInnerTo an Element in a document"); |
112 | 0 |
|
113 | 0 | bool reparse = (aDst->OwnerDoc() != OwnerDoc()); |
114 | 0 |
|
115 | 0 | nsresult rv = |
116 | 0 | static_cast<nsGenericHTMLElement*>(aDst)->mAttrs.EnsureCapacityToClone(mAttrs); |
117 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
118 | 0 |
|
119 | 0 | int32_t i, count = GetAttrCount(); |
120 | 0 | for (i = 0; i < count; ++i) { |
121 | 0 | const nsAttrName *name = mAttrs.AttrNameAt(i); |
122 | 0 | const nsAttrValue *value = mAttrs.AttrAt(i); |
123 | 0 |
|
124 | 0 | if (name->Equals(nsGkAtoms::style, kNameSpaceID_None) && |
125 | 0 | value->Type() == nsAttrValue::eCSSDeclaration) { |
126 | 0 | // We still clone CSS attributes, even in the cross-document case. |
127 | 0 | // https://github.com/w3c/webappsec-csp/issues/212 |
128 | 0 |
|
129 | 0 | // We can't just set this as a string, because that will fail |
130 | 0 | // to reparse the string into style data until the node is |
131 | 0 | // inserted into the document. Clone the Rule instead. |
132 | 0 | nsAttrValue valueCopy(*value); |
133 | 0 | rv = aDst->SetParsedAttr(name->NamespaceID(), name->LocalName(), |
134 | 0 | name->GetPrefix(), valueCopy, false); |
135 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
136 | 0 |
|
137 | 0 | DeclarationBlock* cssDeclaration = value->GetCSSDeclarationValue(); |
138 | 0 | cssDeclaration->SetImmutable(); |
139 | 0 | } else if (reparse) { |
140 | 0 | nsAutoString valStr; |
141 | 0 | value->ToString(valStr); |
142 | 0 |
|
143 | 0 | rv = aDst->SetAttr(name->NamespaceID(), name->LocalName(), |
144 | 0 | name->GetPrefix(), valStr, false); |
145 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
146 | 0 | } else { |
147 | 0 | nsAttrValue valueCopy(*value); |
148 | 0 | rv = aDst->SetParsedAttr(name->NamespaceID(), name->LocalName(), |
149 | 0 | name->GetPrefix(), valueCopy, false); |
150 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
151 | 0 | } |
152 | 0 | } |
153 | 0 |
|
154 | 0 | return NS_OK; |
155 | 0 | } |
156 | | |
157 | | static const nsAttrValue::EnumTable kDirTable[] = { |
158 | | { "ltr", eDir_LTR }, |
159 | | { "rtl", eDir_RTL }, |
160 | | { "auto", eDir_Auto }, |
161 | | { nullptr, 0 } |
162 | | }; |
163 | | |
164 | | void |
165 | | nsGenericHTMLElement::AddToNameTable(nsAtom* aName) |
166 | 0 | { |
167 | 0 | MOZ_ASSERT(HasName(), "Node doesn't have name?"); |
168 | 0 | nsIDocument* doc = GetUncomposedDoc(); |
169 | 0 | if (doc && !IsInAnonymousSubtree()) { |
170 | 0 | doc->AddToNameTable(this, aName); |
171 | 0 | } |
172 | 0 | } |
173 | | |
174 | | void |
175 | | nsGenericHTMLElement::RemoveFromNameTable() |
176 | 0 | { |
177 | 0 | if (HasName() && CanHaveName(NodeInfo()->NameAtom())) { |
178 | 0 | if (nsIDocument* doc = GetUncomposedDoc()) { |
179 | 0 | doc->RemoveFromNameTable(this, |
180 | 0 | GetParsedAttr(nsGkAtoms::name)->GetAtomValue()); |
181 | 0 | } |
182 | 0 | } |
183 | 0 | } |
184 | | |
185 | | void |
186 | | nsGenericHTMLElement::GetAccessKeyLabel(nsString& aLabel) |
187 | 0 | { |
188 | 0 | nsAutoString suffix; |
189 | 0 | GetAccessKey(suffix); |
190 | 0 | if (!suffix.IsEmpty()) { |
191 | 0 | EventStateManager::GetAccessKeyLabelPrefix(this, aLabel); |
192 | 0 | aLabel.Append(suffix); |
193 | 0 | } |
194 | 0 | } |
195 | | |
196 | | static bool |
197 | | IsTableCell(LayoutFrameType frameType) |
198 | 0 | { |
199 | 0 | return LayoutFrameType::TableCell == frameType || |
200 | 0 | LayoutFrameType::BCTableCell == frameType; |
201 | 0 | } |
202 | | |
203 | | static bool |
204 | | IsOffsetParent(nsIFrame* aFrame) |
205 | 0 | { |
206 | 0 | LayoutFrameType frameType = aFrame->Type(); |
207 | 0 |
|
208 | 0 | if (IsTableCell(frameType) || frameType == LayoutFrameType::Table) { |
209 | 0 | // Per the IDL for Element, only td, th, and table are acceptable offsetParents |
210 | 0 | // apart from body or positioned elements; we need to check the content type as |
211 | 0 | // well as the frame type so we ignore anonymous tables created by an element |
212 | 0 | // with display: table-cell with no actual table |
213 | 0 | nsIContent* content = aFrame->GetContent(); |
214 | 0 |
|
215 | 0 | return content->IsAnyOfHTMLElements(nsGkAtoms::table, |
216 | 0 | nsGkAtoms::td, |
217 | 0 | nsGkAtoms::th); |
218 | 0 | } |
219 | 0 | return false; |
220 | 0 | } |
221 | | |
222 | | Element* |
223 | | nsGenericHTMLElement::GetOffsetRect(CSSIntRect& aRect) |
224 | 0 | { |
225 | 0 | aRect = CSSIntRect(); |
226 | 0 |
|
227 | 0 | nsIFrame* frame = GetPrimaryFrame(FlushType::Layout); |
228 | 0 | if (!frame) { |
229 | 0 | return nullptr; |
230 | 0 | } |
231 | 0 | |
232 | 0 | nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(frame); |
233 | 0 |
|
234 | 0 | nsIFrame* parent = frame->GetParent(); |
235 | 0 | nsPoint origin(0, 0); |
236 | 0 |
|
237 | 0 | nsIContent* offsetParent = nullptr; |
238 | 0 | Element* docElement = GetComposedDoc()->GetRootElement(); |
239 | 0 | nsIContent* content = frame->GetContent(); |
240 | 0 |
|
241 | 0 | if (content && (content->IsHTMLElement(nsGkAtoms::body) || |
242 | 0 | content == docElement)) { |
243 | 0 | parent = frame; |
244 | 0 | } |
245 | 0 | else { |
246 | 0 | const bool isPositioned = styleFrame->IsAbsPosContainingBlock(); |
247 | 0 | const bool isAbsolutelyPositioned = styleFrame->IsAbsolutelyPositioned(); |
248 | 0 | origin += frame->GetPositionIgnoringScrolling(); |
249 | 0 |
|
250 | 0 | for ( ; parent ; parent = parent->GetParent()) { |
251 | 0 | content = parent->GetContent(); |
252 | 0 |
|
253 | 0 | // Stop at the first ancestor that is positioned. |
254 | 0 | if (parent->IsAbsPosContainingBlock()) { |
255 | 0 | offsetParent = content; |
256 | 0 | break; |
257 | 0 | } |
258 | 0 | |
259 | 0 | // Add the parent's origin to our own to get to the |
260 | 0 | // right coordinate system. |
261 | 0 | const bool isOffsetParent = !isPositioned && IsOffsetParent(parent); |
262 | 0 | if (!isOffsetParent) { |
263 | 0 | origin += parent->GetPositionIgnoringScrolling(); |
264 | 0 | } |
265 | 0 |
|
266 | 0 | if (content) { |
267 | 0 | // If we've hit the document element, break here. |
268 | 0 | if (content == docElement) { |
269 | 0 | break; |
270 | 0 | } |
271 | 0 | |
272 | 0 | // Break if the ancestor frame type makes it suitable as offset parent |
273 | 0 | // and this element is *not* positioned or if we found the body element. |
274 | 0 | if (isOffsetParent || content->IsHTMLElement(nsGkAtoms::body)) { |
275 | 0 | offsetParent = content; |
276 | 0 | break; |
277 | 0 | } |
278 | 0 | } |
279 | 0 | } |
280 | 0 |
|
281 | 0 | if (isAbsolutelyPositioned && !offsetParent) { |
282 | 0 | // If this element is absolutely positioned, but we don't have |
283 | 0 | // an offset parent it means this element is an absolutely |
284 | 0 | // positioned child that's not nested inside another positioned |
285 | 0 | // element, in this case the element's frame's parent is the |
286 | 0 | // frame for the HTML element so we fail to find the body in the |
287 | 0 | // parent chain. We want the offset parent in this case to be |
288 | 0 | // the body, so we just get the body element from the document. |
289 | 0 | // |
290 | 0 | // We use GetBodyElement() here, not GetBody(), because we don't want to |
291 | 0 | // end up with framesets here. |
292 | 0 | offsetParent = GetComposedDoc()->GetBodyElement(); |
293 | 0 | } |
294 | 0 | } |
295 | 0 |
|
296 | 0 | // Subtract the parent border unless it uses border-box sizing. |
297 | 0 | if (parent && |
298 | 0 | parent->StylePosition()->mBoxSizing != StyleBoxSizing::Border) { |
299 | 0 | const nsStyleBorder* border = parent->StyleBorder(); |
300 | 0 | origin.x -= border->GetComputedBorderWidth(eSideLeft); |
301 | 0 | origin.y -= border->GetComputedBorderWidth(eSideTop); |
302 | 0 | } |
303 | 0 |
|
304 | 0 | // XXX We should really consider subtracting out padding for |
305 | 0 | // content-box sizing, but we should see what IE does.... |
306 | 0 |
|
307 | 0 | // Get the union of all rectangles in this and continuation frames. |
308 | 0 | // It doesn't really matter what we use as aRelativeTo here, since |
309 | 0 | // we only care about the size. We just have to use something non-null. |
310 | 0 | nsRect rcFrame = nsLayoutUtils::GetAllInFlowRectsUnion(frame, frame); |
311 | 0 | rcFrame.MoveTo(origin); |
312 | 0 | aRect = CSSIntRect::FromAppUnitsRounded(rcFrame); |
313 | 0 |
|
314 | 0 | return offsetParent ? offsetParent->AsElement() : nullptr; |
315 | 0 | } |
316 | | |
317 | | bool |
318 | | nsGenericHTMLElement::Spellcheck() |
319 | 0 | { |
320 | 0 | // Has the state has been explicitly set? |
321 | 0 | nsIContent* node; |
322 | 0 | for (node = this; node; node = node->GetParent()) { |
323 | 0 | if (node->IsHTMLElement()) { |
324 | 0 | static Element::AttrValuesArray strings[] = |
325 | 0 | {&nsGkAtoms::_true, &nsGkAtoms::_false, nullptr}; |
326 | 0 | switch (node->AsElement()->FindAttrValueIn(kNameSpaceID_None, |
327 | 0 | nsGkAtoms::spellcheck, strings, |
328 | 0 | eCaseMatters)) { |
329 | 0 | case 0: // spellcheck = "true" |
330 | 0 | return true; |
331 | 0 | case 1: // spellcheck = "false" |
332 | 0 | return false; |
333 | 0 | } |
334 | 0 | } |
335 | 0 | } |
336 | 0 |
|
337 | 0 | // contenteditable/designMode are spellchecked by default |
338 | 0 | if (IsEditable()) { |
339 | 0 | return true; |
340 | 0 | } |
341 | 0 | |
342 | 0 | // Is this a chrome element? |
343 | 0 | if (nsContentUtils::IsChromeDoc(OwnerDoc())) { |
344 | 0 | return false; // Not spellchecked by default |
345 | 0 | } |
346 | 0 | |
347 | 0 | // Anything else that's not a form control is not spellchecked by default |
348 | 0 | nsCOMPtr<nsIFormControl> formControl = do_QueryObject(this); |
349 | 0 | if (!formControl) { |
350 | 0 | return false; // Not spellchecked by default |
351 | 0 | } |
352 | 0 | |
353 | 0 | // Is this a multiline plaintext input? |
354 | 0 | int32_t controlType = formControl->ControlType(); |
355 | 0 | if (controlType == NS_FORM_TEXTAREA) { |
356 | 0 | return true; // Spellchecked by default |
357 | 0 | } |
358 | 0 | |
359 | 0 | // Is this anything other than an input text? |
360 | 0 | // Other inputs are not spellchecked. |
361 | 0 | if (controlType != NS_FORM_INPUT_TEXT) { |
362 | 0 | return false; // Not spellchecked by default |
363 | 0 | } |
364 | 0 | |
365 | 0 | // Does the user want input text spellchecked by default? |
366 | 0 | // NOTE: Do not reflect a pref value of 0 back to the DOM getter. |
367 | 0 | // The web page should not know if the user has disabled spellchecking. |
368 | 0 | // We'll catch this in the editor itself. |
369 | 0 | int32_t spellcheckLevel = Preferences::GetInt("layout.spellcheckDefault", 1); |
370 | 0 | return spellcheckLevel == 2; // "Spellcheck multi- and single-line" |
371 | 0 | } |
372 | | |
373 | | bool |
374 | | nsGenericHTMLElement::InNavQuirksMode(nsIDocument* aDoc) |
375 | 0 | { |
376 | 0 | return aDoc && aDoc->GetCompatibilityMode() == eCompatibility_NavQuirks; |
377 | 0 | } |
378 | | |
379 | | void |
380 | | nsGenericHTMLElement::UpdateEditableState(bool aNotify) |
381 | 0 | { |
382 | 0 | // XXX Should we do this only when in a document? |
383 | 0 | ContentEditableTristate value = GetContentEditableValue(); |
384 | 0 | if (value != eInherit) { |
385 | 0 | DoSetEditableFlag(!!value, aNotify); |
386 | 0 | return; |
387 | 0 | } |
388 | 0 | |
389 | 0 | nsStyledElement::UpdateEditableState(aNotify); |
390 | 0 | } |
391 | | |
392 | | EventStates |
393 | | nsGenericHTMLElement::IntrinsicState() const |
394 | 0 | { |
395 | 0 | EventStates state = nsGenericHTMLElementBase::IntrinsicState(); |
396 | 0 |
|
397 | 0 | if (GetDirectionality() == eDir_RTL) { |
398 | 0 | state |= NS_EVENT_STATE_RTL; |
399 | 0 | state &= ~NS_EVENT_STATE_LTR; |
400 | 0 | } else { // at least for HTML, directionality is exclusively LTR or RTL |
401 | 0 | NS_ASSERTION(GetDirectionality() == eDir_LTR, |
402 | 0 | "HTML element's directionality must be either RTL or LTR"); |
403 | 0 | state |= NS_EVENT_STATE_LTR; |
404 | 0 | state &= ~NS_EVENT_STATE_RTL; |
405 | 0 | } |
406 | 0 |
|
407 | 0 | return state; |
408 | 0 | } |
409 | | |
410 | | uint32_t |
411 | | nsGenericHTMLElement::EditableInclusiveDescendantCount() |
412 | 0 | { |
413 | 0 | bool isEditable = IsInComposedDoc() && HasFlag(NODE_IS_EDITABLE) && |
414 | 0 | GetContentEditableValue() == eTrue; |
415 | 0 | return EditableDescendantCount() + isEditable; |
416 | 0 | } |
417 | | |
418 | | nsresult |
419 | | nsGenericHTMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, |
420 | | nsIContent* aBindingParent) |
421 | 0 | { |
422 | 0 | nsresult rv = nsGenericHTMLElementBase::BindToTree(aDocument, aParent, |
423 | 0 | aBindingParent); |
424 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
425 | 0 |
|
426 | 0 | if (aDocument) { |
427 | 0 | RegAccessKey(); |
428 | 0 | if (HasName() && CanHaveName(NodeInfo()->NameAtom())) { |
429 | 0 | aDocument-> |
430 | 0 | AddToNameTable(this, GetParsedAttr(nsGkAtoms::name)->GetAtomValue()); |
431 | 0 | } |
432 | 0 | } |
433 | 0 |
|
434 | 0 | if (HasFlag(NODE_IS_EDITABLE) && GetContentEditableValue() == eTrue && |
435 | 0 | IsInComposedDoc()) { |
436 | 0 | nsCOMPtr<nsIHTMLDocument> htmlDocument = |
437 | 0 | do_QueryInterface(GetComposedDoc()); |
438 | 0 | if (htmlDocument) { |
439 | 0 | htmlDocument->ChangeContentEditableCount(this, +1); |
440 | 0 | } |
441 | 0 | } |
442 | 0 |
|
443 | 0 | // We need to consider a labels element is moved to another subtree |
444 | 0 | // with different root, it needs to update labels list and its root |
445 | 0 | // as well. |
446 | 0 | nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots(); |
447 | 0 | if (slots && slots->mLabelsList) { |
448 | 0 | slots->mLabelsList->MaybeResetRoot(SubtreeRoot()); |
449 | 0 | } |
450 | 0 |
|
451 | 0 | return rv; |
452 | 0 | } |
453 | | |
454 | | void |
455 | | nsGenericHTMLElement::UnbindFromTree(bool aDeep, bool aNullParent) |
456 | 0 | { |
457 | 0 | if (IsInUncomposedDoc()) { |
458 | 0 | UnregAccessKey(); |
459 | 0 | } |
460 | 0 |
|
461 | 0 | RemoveFromNameTable(); |
462 | 0 |
|
463 | 0 | if (GetContentEditableValue() == eTrue) { |
464 | 0 | nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(GetComposedDoc()); |
465 | 0 | if (htmlDocument) { |
466 | 0 | htmlDocument->ChangeContentEditableCount(this, -1); |
467 | 0 | } |
468 | 0 | } |
469 | 0 |
|
470 | 0 | nsStyledElement::UnbindFromTree(aDeep, aNullParent); |
471 | 0 |
|
472 | 0 | // Invalidate .labels list. It will be repopulated when used the next time. |
473 | 0 | nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots(); |
474 | 0 | if (slots && slots->mLabelsList) { |
475 | 0 | slots->mLabelsList->MaybeResetRoot(SubtreeRoot()); |
476 | 0 | } |
477 | 0 | } |
478 | | |
479 | | HTMLFormElement* |
480 | | nsGenericHTMLElement::FindAncestorForm(HTMLFormElement* aCurrentForm) |
481 | 0 | { |
482 | 0 | NS_ASSERTION(!HasAttr(kNameSpaceID_None, nsGkAtoms::form) || |
483 | 0 | IsHTMLElement(nsGkAtoms::img), |
484 | 0 | "FindAncestorForm should not be called if @form is set!"); |
485 | 0 |
|
486 | 0 | // Make sure we don't end up finding a form that's anonymous from |
487 | 0 | // our point of view. See also nsGenericHTMLFormElement::UpdateFieldSet. |
488 | 0 | nsIContent* bindingParent = GetBindingParent(); |
489 | 0 |
|
490 | 0 | nsIContent* content = this; |
491 | 0 | while (content != bindingParent && content) { |
492 | 0 | // If the current ancestor is a form, return it as our form |
493 | 0 | if (content->IsHTMLElement(nsGkAtoms::form)) { |
494 | | #ifdef DEBUG |
495 | | if (!nsContentUtils::IsInSameAnonymousTree(this, content)) { |
496 | | // It's possible that we started unbinding at |content| or |
497 | | // some ancestor of it, and |content| and |this| used to all be |
498 | | // anonymous. Check for this the hard way. |
499 | | for (nsIContent* child = this; child != content; |
500 | | child = child->GetParent()) { |
501 | | NS_ASSERTION(child->GetParent()->ComputeIndexOf(child) != -1, |
502 | | "Walked too far?"); |
503 | | } |
504 | | } |
505 | | #endif |
506 | | return static_cast<HTMLFormElement*>(content); |
507 | 0 | } |
508 | 0 |
|
509 | 0 | nsIContent *prevContent = content; |
510 | 0 | content = prevContent->GetParent(); |
511 | 0 |
|
512 | 0 | if (!content && aCurrentForm) { |
513 | 0 | // We got to the root of the subtree we're in, and we're being removed |
514 | 0 | // from the DOM (the only time we get into this method with a non-null |
515 | 0 | // aCurrentForm). Check whether aCurrentForm is in the same subtree. If |
516 | 0 | // it is, we want to return aCurrentForm, since this case means that |
517 | 0 | // we're one of those inputs-in-a-table that have a hacked mForm pointer |
518 | 0 | // and a subtree containing both us and the form got removed from the |
519 | 0 | // DOM. |
520 | 0 | if (nsContentUtils::ContentIsDescendantOf(aCurrentForm, prevContent)) { |
521 | 0 | return aCurrentForm; |
522 | 0 | } |
523 | 0 | } |
524 | 0 | } |
525 | 0 |
|
526 | 0 | return nullptr; |
527 | 0 | } |
528 | | |
529 | | bool |
530 | | nsGenericHTMLElement::CheckHandleEventForAnchorsPreconditions( |
531 | | EventChainVisitor& aVisitor) |
532 | 0 | { |
533 | 0 | MOZ_ASSERT(nsCOMPtr<Link>(do_QueryObject(this)), |
534 | 0 | "should be called only when |this| implements |Link|"); |
535 | 0 |
|
536 | 0 | if (!aVisitor.mPresContext) { |
537 | 0 | // We need a pres context to do link stuff. Some events (e.g. mutation |
538 | 0 | // events) don't have one. |
539 | 0 | // XXX: ideally, shouldn't we be able to do what we need without one? |
540 | 0 | return false; |
541 | 0 | } |
542 | 0 | |
543 | 0 | //Need to check if we hit an imagemap area and if so see if we're handling |
544 | 0 | //the event on that map or on a link farther up the tree. If we're on a |
545 | 0 | //link farther up, do nothing. |
546 | 0 | nsCOMPtr<nsIContent> target = aVisitor.mPresContext->EventStateManager()-> |
547 | 0 | GetEventTargetContent(aVisitor.mEvent); |
548 | 0 |
|
549 | 0 | return !target || !target->IsHTMLElement(nsGkAtoms::area) || |
550 | 0 | IsHTMLElement(nsGkAtoms::area); |
551 | 0 | } |
552 | | |
553 | | void |
554 | | nsGenericHTMLElement::GetEventTargetParentForAnchors(EventChainPreVisitor& aVisitor) |
555 | 0 | { |
556 | 0 | nsGenericHTMLElementBase::GetEventTargetParent(aVisitor); |
557 | 0 |
|
558 | 0 | if (!CheckHandleEventForAnchorsPreconditions(aVisitor)) { |
559 | 0 | return; |
560 | 0 | } |
561 | 0 | |
562 | 0 | GetEventTargetParentForLinks(aVisitor); |
563 | 0 | } |
564 | | |
565 | | nsresult |
566 | | nsGenericHTMLElement::PostHandleEventForAnchors(EventChainPostVisitor& aVisitor) |
567 | 0 | { |
568 | 0 | if (!CheckHandleEventForAnchorsPreconditions(aVisitor)) { |
569 | 0 | return NS_OK; |
570 | 0 | } |
571 | 0 | |
572 | 0 | return PostHandleEventForLinks(aVisitor); |
573 | 0 | } |
574 | | |
575 | | bool |
576 | | nsGenericHTMLElement::IsHTMLLink(nsIURI** aURI) const |
577 | 0 | { |
578 | 0 | MOZ_ASSERT(aURI, "Must provide aURI out param"); |
579 | 0 |
|
580 | 0 | *aURI = GetHrefURIForAnchors().take(); |
581 | 0 | // We promise out param is non-null if we return true, so base rv on it |
582 | 0 | return *aURI != nullptr; |
583 | 0 | } |
584 | | |
585 | | already_AddRefed<nsIURI> |
586 | | nsGenericHTMLElement::GetHrefURIForAnchors() const |
587 | 0 | { |
588 | 0 | // This is used by the three Link implementations and |
589 | 0 | // nsHTMLStyleElement. |
590 | 0 |
|
591 | 0 | // Get href= attribute (relative URI). |
592 | 0 |
|
593 | 0 | // We use the nsAttrValue's copy of the URI string to avoid copying. |
594 | 0 | nsCOMPtr<nsIURI> uri; |
595 | 0 | GetURIAttr(nsGkAtoms::href, nullptr, getter_AddRefs(uri)); |
596 | 0 |
|
597 | 0 | return uri.forget(); |
598 | 0 | } |
599 | | |
600 | | nsresult |
601 | | nsGenericHTMLElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName, |
602 | | const nsAttrValueOrString* aValue, |
603 | | bool aNotify) |
604 | 0 | { |
605 | 0 | if (aNamespaceID == kNameSpaceID_None) { |
606 | 0 | if (aName == nsGkAtoms::accesskey) { |
607 | 0 | // Have to unregister before clearing flag. See UnregAccessKey |
608 | 0 | UnregAccessKey(); |
609 | 0 | if (!aValue) { |
610 | 0 | UnsetFlags(NODE_HAS_ACCESSKEY); |
611 | 0 | } |
612 | 0 | } else if (aName == nsGkAtoms::name) { |
613 | 0 | // Have to do this before clearing flag. See RemoveFromNameTable |
614 | 0 | RemoveFromNameTable(); |
615 | 0 | if (!aValue || aValue->IsEmpty()) { |
616 | 0 | ClearHasName(); |
617 | 0 | } |
618 | 0 | } else if (aName == nsGkAtoms::contenteditable) { |
619 | 0 | if (aValue) { |
620 | 0 | // Set this before the attribute is set so that any subclass code that |
621 | 0 | // runs before the attribute is set won't think we're missing a |
622 | 0 | // contenteditable attr when we actually have one. |
623 | 0 | SetMayHaveContentEditableAttr(); |
624 | 0 | } |
625 | 0 | } |
626 | 0 | if (!aValue && IsEventAttributeName(aName)) { |
627 | 0 | if (EventListenerManager* manager = GetExistingListenerManager()) { |
628 | 0 | manager->RemoveEventHandler(aName); |
629 | 0 | } |
630 | 0 | } |
631 | 0 | } |
632 | 0 |
|
633 | 0 | return nsGenericHTMLElementBase::BeforeSetAttr(aNamespaceID, aName, aValue, |
634 | 0 | aNotify); |
635 | 0 | } |
636 | | |
637 | | nsresult |
638 | | nsGenericHTMLElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName, |
639 | | const nsAttrValue* aValue, |
640 | | const nsAttrValue* aOldValue, |
641 | | nsIPrincipal* aMaybeScriptedPrincipal, |
642 | | bool aNotify) |
643 | 0 | { |
644 | 0 | if (aNamespaceID == kNameSpaceID_None) { |
645 | 0 | if (IsEventAttributeName(aName) && aValue) { |
646 | 0 | MOZ_ASSERT(aValue->Type() == nsAttrValue::eString, |
647 | 0 | "Expected string value for script body"); |
648 | 0 | nsresult rv = SetEventHandler(aName, aValue->GetStringValue()); |
649 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
650 | 0 | } |
651 | 0 | else if (aNotify && aName == nsGkAtoms::spellcheck) { |
652 | 0 | SyncEditorsOnSubtree(this); |
653 | 0 | } |
654 | 0 | else if (aName == nsGkAtoms::dir) { |
655 | 0 | Directionality dir = eDir_LTR; |
656 | 0 | // A boolean tracking whether we need to recompute our directionality. |
657 | 0 | // This needs to happen after we update our internal "dir" attribute |
658 | 0 | // state but before we call SetDirectionalityOnDescendants. |
659 | 0 | bool recomputeDirectionality = false; |
660 | 0 | // We don't want to have to keep getting the "dir" attribute in |
661 | 0 | // IntrinsicState, so we manually recompute our dir-related event states |
662 | 0 | // here and send the relevant update notifications. |
663 | 0 | EventStates dirStates; |
664 | 0 | if (aValue && aValue->Type() == nsAttrValue::eEnum) { |
665 | 0 | SetHasValidDir(); |
666 | 0 | dirStates |= NS_EVENT_STATE_HAS_DIR_ATTR; |
667 | 0 | Directionality dirValue = (Directionality)aValue->GetEnumValue(); |
668 | 0 | if (dirValue == eDir_Auto) { |
669 | 0 | dirStates |= NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO; |
670 | 0 | } else { |
671 | 0 | dir = dirValue; |
672 | 0 | SetDirectionality(dir, aNotify); |
673 | 0 | if (dirValue == eDir_LTR) { |
674 | 0 | dirStates |= NS_EVENT_STATE_DIR_ATTR_LTR; |
675 | 0 | } else { |
676 | 0 | MOZ_ASSERT(dirValue == eDir_RTL); |
677 | 0 | dirStates |= NS_EVENT_STATE_DIR_ATTR_RTL; |
678 | 0 | } |
679 | 0 | } |
680 | 0 | } else { |
681 | 0 | if (aValue) { |
682 | 0 | // We have a value, just not a valid one. |
683 | 0 | dirStates |= NS_EVENT_STATE_HAS_DIR_ATTR; |
684 | 0 | } |
685 | 0 | ClearHasValidDir(); |
686 | 0 | if (NodeInfo()->Equals(nsGkAtoms::bdi)) { |
687 | 0 | dirStates |= NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO; |
688 | 0 | } else { |
689 | 0 | recomputeDirectionality = true; |
690 | 0 | } |
691 | 0 | } |
692 | 0 | // Now figure out what's changed about our dir states. |
693 | 0 | EventStates oldDirStates = State() & DIR_ATTR_STATES; |
694 | 0 | EventStates changedStates = dirStates ^ oldDirStates; |
695 | 0 | ToggleStates(changedStates, aNotify); |
696 | 0 | if (recomputeDirectionality) { |
697 | 0 | dir = RecomputeDirectionality(this, aNotify); |
698 | 0 | } |
699 | 0 | SetDirectionalityOnDescendants(this, dir, aNotify); |
700 | 0 | } else if (aName == nsGkAtoms::contenteditable) { |
701 | 0 | int32_t editableCountDelta = 0; |
702 | 0 | if (aOldValue && |
703 | 0 | (aOldValue->Equals(NS_LITERAL_STRING("true"), eIgnoreCase) || |
704 | 0 | aOldValue->Equals(EmptyString(), eIgnoreCase))) { |
705 | 0 | editableCountDelta = -1; |
706 | 0 | } |
707 | 0 | if (aValue && (aValue->Equals(NS_LITERAL_STRING("true"), eIgnoreCase) || |
708 | 0 | aValue->Equals(EmptyString(), eIgnoreCase))) { |
709 | 0 | ++editableCountDelta; |
710 | 0 | } |
711 | 0 | ChangeEditableState(editableCountDelta); |
712 | 0 | } else if (aName == nsGkAtoms::accesskey) { |
713 | 0 | if (aValue && !aValue->Equals(EmptyString(), eIgnoreCase)) { |
714 | 0 | SetFlags(NODE_HAS_ACCESSKEY); |
715 | 0 | RegAccessKey(); |
716 | 0 | } |
717 | 0 | } else if (aName == nsGkAtoms::name) { |
718 | 0 | if (aValue && !aValue->Equals(EmptyString(), eIgnoreCase)) { |
719 | 0 | // This may not be quite right because we can have subclass code run |
720 | 0 | // before here. But in practice subclasses don't care about this flag, |
721 | 0 | // and in particular selector matching does not care. Otherwise we'd |
722 | 0 | // want to handle it like we handle id attributes (in PreIdMaybeChange |
723 | 0 | // and PostIdMaybeChange). |
724 | 0 | SetHasName(); |
725 | 0 | if (CanHaveName(NodeInfo()->NameAtom())) { |
726 | 0 | AddToNameTable(aValue->GetAtomValue()); |
727 | 0 | } |
728 | 0 | } |
729 | 0 | } |
730 | 0 | } |
731 | 0 |
|
732 | 0 | return nsGenericHTMLElementBase::AfterSetAttr(aNamespaceID, aName, |
733 | 0 | aValue, aOldValue, |
734 | 0 | aMaybeScriptedPrincipal, |
735 | 0 | aNotify); |
736 | 0 | } |
737 | | |
738 | | EventListenerManager* |
739 | | nsGenericHTMLElement::GetEventListenerManagerForAttr(nsAtom* aAttrName, |
740 | | bool* aDefer) |
741 | 0 | { |
742 | 0 | // Attributes on the body and frameset tags get set on the global object |
743 | 0 | if ((mNodeInfo->Equals(nsGkAtoms::body) || |
744 | 0 | mNodeInfo->Equals(nsGkAtoms::frameset)) && |
745 | 0 | // We only forward some event attributes from body/frameset to window |
746 | 0 | (0 |
747 | 0 | #define EVENT(name_, id_, type_, struct_) /* nothing */ |
748 | 0 | #define FORWARDED_EVENT(name_, id_, type_, struct_) \ |
749 | 0 | || nsGkAtoms::on##name_ == aAttrName |
750 | 0 | #define WINDOW_EVENT FORWARDED_EVENT |
751 | 0 | #include "mozilla/EventNameList.h" // IWYU pragma: keep |
752 | 0 | #undef WINDOW_EVENT |
753 | 0 | #undef FORWARDED_EVENT |
754 | 0 | #undef EVENT |
755 | 0 | ) |
756 | 0 | ) { |
757 | 0 | nsPIDOMWindowInner *win; |
758 | 0 |
|
759 | 0 | // If we have a document, and it has a window, add the event |
760 | 0 | // listener on the window (the inner window). If not, proceed as |
761 | 0 | // normal. |
762 | 0 | // XXXbz sXBL/XBL2 issue: should we instead use GetComposedDoc() here, |
763 | 0 | // override BindToTree for those classes and munge event listeners there? |
764 | 0 | nsIDocument *document = OwnerDoc(); |
765 | 0 |
|
766 | 0 | *aDefer = false; |
767 | 0 | if ((win = document->GetInnerWindow())) { |
768 | 0 | nsCOMPtr<EventTarget> piTarget(do_QueryInterface(win)); |
769 | 0 |
|
770 | 0 | return piTarget->GetOrCreateListenerManager(); |
771 | 0 | } |
772 | 0 | |
773 | 0 | return nullptr; |
774 | 0 | } |
775 | 0 | |
776 | 0 | return nsGenericHTMLElementBase::GetEventListenerManagerForAttr(aAttrName, |
777 | 0 | aDefer); |
778 | 0 | } |
779 | | |
780 | | #define EVENT(name_, id_, type_, struct_) /* nothing; handled by nsINode */ |
781 | | #define FORWARDED_EVENT(name_, id_, type_, struct_) \ |
782 | | EventHandlerNonNull* \ |
783 | 0 | nsGenericHTMLElement::GetOn##name_() \ |
784 | 0 | { \ |
785 | 0 | if (IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset)) { \ |
786 | 0 | /* XXXbz note to self: add tests for this! */ \ |
787 | 0 | if (nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow()) { \ |
788 | 0 | nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \ |
789 | 0 | return globalWin->GetOn##name_(); \ |
790 | 0 | } \ |
791 | 0 | return nullptr; \ |
792 | 0 | } \ |
793 | 0 | \ |
794 | 0 | return nsINode::GetOn##name_(); \ |
795 | 0 | } \ Unexecuted instantiation: nsGenericHTMLElement::GetOnblur() Unexecuted instantiation: nsGenericHTMLElement::GetOnfocus() Unexecuted instantiation: nsGenericHTMLElement::GetOnfocusin() Unexecuted instantiation: nsGenericHTMLElement::GetOnfocusout() Unexecuted instantiation: nsGenericHTMLElement::GetOnload() Unexecuted instantiation: nsGenericHTMLElement::GetOnresize() Unexecuted instantiation: nsGenericHTMLElement::GetOnscroll() |
796 | | void \ |
797 | 0 | nsGenericHTMLElement::SetOn##name_(EventHandlerNonNull* handler) \ |
798 | 0 | { \ |
799 | 0 | if (IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset)) { \ |
800 | 0 | nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow(); \ |
801 | 0 | if (!win) { \ |
802 | 0 | return; \ |
803 | 0 | } \ |
804 | 0 | \ |
805 | 0 | nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \ |
806 | 0 | return globalWin->SetOn##name_(handler); \ |
807 | 0 | } \ |
808 | 0 | \ |
809 | 0 | return nsINode::SetOn##name_(handler); \ |
810 | 0 | } Unexecuted instantiation: nsGenericHTMLElement::SetOnblur(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsGenericHTMLElement::SetOnfocus(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsGenericHTMLElement::SetOnfocusin(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsGenericHTMLElement::SetOnfocusout(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsGenericHTMLElement::SetOnload(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsGenericHTMLElement::SetOnresize(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsGenericHTMLElement::SetOnscroll(mozilla::dom::EventHandlerNonNull*) |
811 | | #define ERROR_EVENT(name_, id_, type_, struct_) \ |
812 | | already_AddRefed<EventHandlerNonNull> \ |
813 | 0 | nsGenericHTMLElement::GetOn##name_() \ |
814 | 0 | { \ |
815 | 0 | if (IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset)) { \ |
816 | 0 | /* XXXbz note to self: add tests for this! */ \ |
817 | 0 | if (nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow()) { \ |
818 | 0 | nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \ |
819 | 0 | OnErrorEventHandlerNonNull* errorHandler = globalWin->GetOn##name_(); \ |
820 | 0 | if (errorHandler) { \ |
821 | 0 | RefPtr<EventHandlerNonNull> handler = \ |
822 | 0 | new EventHandlerNonNull(errorHandler); \ |
823 | 0 | return handler.forget(); \ |
824 | 0 | } \ |
825 | 0 | } \ |
826 | 0 | return nullptr; \ |
827 | 0 | } \ |
828 | 0 | \ |
829 | 0 | RefPtr<EventHandlerNonNull> handler = nsINode::GetOn##name_(); \ |
830 | 0 | return handler.forget(); \ |
831 | 0 | } \ |
832 | | void \ |
833 | 0 | nsGenericHTMLElement::SetOn##name_(EventHandlerNonNull* handler) \ |
834 | 0 | { \ |
835 | 0 | if (IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset)) { \ |
836 | 0 | nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow(); \ |
837 | 0 | if (!win) { \ |
838 | 0 | return; \ |
839 | 0 | } \ |
840 | 0 | \ |
841 | 0 | nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \ |
842 | 0 | RefPtr<OnErrorEventHandlerNonNull> errorHandler; \ |
843 | 0 | if (handler) { \ |
844 | 0 | errorHandler = new OnErrorEventHandlerNonNull(handler); \ |
845 | 0 | } \ |
846 | 0 | return globalWin->SetOn##name_(errorHandler); \ |
847 | 0 | } \ |
848 | 0 | \ |
849 | 0 | return nsINode::SetOn##name_(handler); \ |
850 | 0 | } |
851 | | #include "mozilla/EventNameList.h" // IWYU pragma: keep |
852 | | #undef ERROR_EVENT |
853 | | #undef FORWARDED_EVENT |
854 | | #undef EVENT |
855 | | |
856 | | void |
857 | | nsGenericHTMLElement::GetBaseTarget(nsAString& aBaseTarget) const |
858 | 0 | { |
859 | 0 | OwnerDoc()->GetBaseTarget(aBaseTarget); |
860 | 0 | } |
861 | | |
862 | | //---------------------------------------------------------------------- |
863 | | |
864 | | bool |
865 | | nsGenericHTMLElement::ParseAttribute(int32_t aNamespaceID, |
866 | | nsAtom* aAttribute, |
867 | | const nsAString& aValue, |
868 | | nsIPrincipal* aMaybeScriptedPrincipal, |
869 | | nsAttrValue& aResult) |
870 | 0 | { |
871 | 0 | if (aNamespaceID == kNameSpaceID_None) { |
872 | 0 | if (aAttribute == nsGkAtoms::dir) { |
873 | 0 | return aResult.ParseEnumValue(aValue, kDirTable, false); |
874 | 0 | } |
875 | 0 | |
876 | 0 | if (aAttribute == nsGkAtoms::tabindex) { |
877 | 0 | return aResult.ParseIntValue(aValue); |
878 | 0 | } |
879 | 0 | |
880 | 0 | if (aAttribute == nsGkAtoms::referrerpolicy) { |
881 | 0 | return ParseReferrerAttribute(aValue, aResult); |
882 | 0 | } |
883 | 0 | |
884 | 0 | if (aAttribute == nsGkAtoms::name) { |
885 | 0 | // Store name as an atom. name="" means that the element has no name, |
886 | 0 | // not that it has an empty string as the name. |
887 | 0 | if (aValue.IsEmpty()) { |
888 | 0 | return false; |
889 | 0 | } |
890 | 0 | aResult.ParseAtom(aValue); |
891 | 0 | return true; |
892 | 0 | } |
893 | 0 | |
894 | 0 | if (aAttribute == nsGkAtoms::contenteditable) { |
895 | 0 | aResult.ParseAtom(aValue); |
896 | 0 | return true; |
897 | 0 | } |
898 | 0 | |
899 | 0 | if (aAttribute == nsGkAtoms::rel) { |
900 | 0 | aResult.ParseAtomArray(aValue); |
901 | 0 | return true; |
902 | 0 | } |
903 | 0 | } |
904 | 0 | |
905 | 0 | return nsGenericHTMLElementBase::ParseAttribute(aNamespaceID, aAttribute, |
906 | 0 | aValue, aMaybeScriptedPrincipal, |
907 | 0 | aResult); |
908 | 0 | } |
909 | | |
910 | | bool |
911 | | nsGenericHTMLElement::ParseBackgroundAttribute(int32_t aNamespaceID, |
912 | | nsAtom* aAttribute, |
913 | | const nsAString& aValue, |
914 | | nsAttrValue& aResult) |
915 | 0 | { |
916 | 0 | if (aNamespaceID == kNameSpaceID_None && |
917 | 0 | aAttribute == nsGkAtoms::background && |
918 | 0 | !aValue.IsEmpty()) { |
919 | 0 | // Resolve url to an absolute url |
920 | 0 | nsIDocument* doc = OwnerDoc(); |
921 | 0 | nsCOMPtr<nsIURI> baseURI = GetBaseURI(); |
922 | 0 | nsCOMPtr<nsIURI> uri; |
923 | 0 | nsresult rv = nsContentUtils::NewURIWithDocumentCharset( |
924 | 0 | getter_AddRefs(uri), aValue, doc, baseURI); |
925 | 0 | if (NS_FAILED(rv)) { |
926 | 0 | return false; |
927 | 0 | } |
928 | 0 | aResult.SetTo(uri, &aValue); |
929 | 0 | return true; |
930 | 0 | } |
931 | 0 | |
932 | 0 | return false; |
933 | 0 | } |
934 | | |
935 | | bool |
936 | | nsGenericHTMLElement::IsAttributeMapped(const nsAtom* aAttribute) const |
937 | 0 | { |
938 | 0 | static const MappedAttributeEntry* const map[] = { |
939 | 0 | sCommonAttributeMap |
940 | 0 | }; |
941 | 0 |
|
942 | 0 | return FindAttributeDependence(aAttribute, map); |
943 | 0 | } |
944 | | |
945 | | nsMapRuleToAttributesFunc |
946 | | nsGenericHTMLElement::GetAttributeMappingFunction() const |
947 | 0 | { |
948 | 0 | return &MapCommonAttributesInto; |
949 | 0 | } |
950 | | |
951 | | nsIFormControlFrame* |
952 | | nsGenericHTMLElement::GetFormControlFrame(bool aFlushFrames) |
953 | 0 | { |
954 | 0 | if (aFlushFrames && IsInComposedDoc()) { |
955 | 0 | // Cause a flush of the frames, so we get up-to-date frame information |
956 | 0 | GetComposedDoc()->FlushPendingNotifications(FlushType::Frames); |
957 | 0 | } |
958 | 0 | nsIFrame* frame = GetPrimaryFrame(); |
959 | 0 | if (frame) { |
960 | 0 | nsIFormControlFrame* form_frame = do_QueryFrame(frame); |
961 | 0 | if (form_frame) { |
962 | 0 | return form_frame; |
963 | 0 | } |
964 | 0 | |
965 | 0 | // If we have generated content, the primary frame will be a |
966 | 0 | // wrapper frame.. out real frame will be in its child list. |
967 | 0 | for (frame = frame->PrincipalChildList().FirstChild(); |
968 | 0 | frame; |
969 | 0 | frame = frame->GetNextSibling()) { |
970 | 0 | form_frame = do_QueryFrame(frame); |
971 | 0 | if (form_frame) { |
972 | 0 | return form_frame; |
973 | 0 | } |
974 | 0 | } |
975 | 0 | } |
976 | 0 |
|
977 | 0 | return nullptr; |
978 | 0 | } |
979 | | |
980 | | nsPresContext* |
981 | | nsGenericHTMLElement::GetPresContext(PresContextFor aFor) |
982 | 0 | { |
983 | 0 | // Get the document |
984 | 0 | nsIDocument* doc = (aFor == eForComposedDoc) ? |
985 | 0 | GetComposedDoc() : GetUncomposedDoc(); |
986 | 0 | if (doc) { |
987 | 0 | return doc->GetPresContext(); |
988 | 0 | } |
989 | 0 | |
990 | 0 | return nullptr; |
991 | 0 | } |
992 | | |
993 | | static const nsAttrValue::EnumTable kDivAlignTable[] = { |
994 | | { "left", NS_STYLE_TEXT_ALIGN_MOZ_LEFT }, |
995 | | { "right", NS_STYLE_TEXT_ALIGN_MOZ_RIGHT }, |
996 | | { "center", NS_STYLE_TEXT_ALIGN_MOZ_CENTER }, |
997 | | { "middle", NS_STYLE_TEXT_ALIGN_MOZ_CENTER }, |
998 | | { "justify", NS_STYLE_TEXT_ALIGN_JUSTIFY }, |
999 | | { nullptr, 0 } |
1000 | | }; |
1001 | | |
1002 | | static const nsAttrValue::EnumTable kFrameborderTable[] = { |
1003 | | { "yes", NS_STYLE_FRAME_YES }, |
1004 | | { "no", NS_STYLE_FRAME_NO }, |
1005 | | { "1", NS_STYLE_FRAME_1 }, |
1006 | | { "0", NS_STYLE_FRAME_0 }, |
1007 | | { nullptr, 0 } |
1008 | | }; |
1009 | | |
1010 | | static const nsAttrValue::EnumTable kScrollingTable[] = { |
1011 | | { "yes", NS_STYLE_FRAME_YES }, |
1012 | | { "no", NS_STYLE_FRAME_NO }, |
1013 | | { "on", NS_STYLE_FRAME_ON }, |
1014 | | { "off", NS_STYLE_FRAME_OFF }, |
1015 | | { "scroll", NS_STYLE_FRAME_SCROLL }, |
1016 | | { "noscroll", NS_STYLE_FRAME_NOSCROLL }, |
1017 | | { "auto", NS_STYLE_FRAME_AUTO }, |
1018 | | { nullptr, 0 } |
1019 | | }; |
1020 | | |
1021 | | static const nsAttrValue::EnumTable kTableVAlignTable[] = { |
1022 | | { "top", NS_STYLE_VERTICAL_ALIGN_TOP }, |
1023 | | { "middle", NS_STYLE_VERTICAL_ALIGN_MIDDLE }, |
1024 | | { "bottom", NS_STYLE_VERTICAL_ALIGN_BOTTOM }, |
1025 | | { "baseline",NS_STYLE_VERTICAL_ALIGN_BASELINE }, |
1026 | | { nullptr, 0 } |
1027 | | }; |
1028 | | |
1029 | | bool |
1030 | | nsGenericHTMLElement::ParseAlignValue(const nsAString& aString, |
1031 | | nsAttrValue& aResult) |
1032 | 0 | { |
1033 | 0 | static const nsAttrValue::EnumTable kAlignTable[] = { |
1034 | 0 | { "left", NS_STYLE_TEXT_ALIGN_LEFT }, |
1035 | 0 | { "right", NS_STYLE_TEXT_ALIGN_RIGHT }, |
1036 | 0 |
|
1037 | 0 | { "top", NS_STYLE_VERTICAL_ALIGN_TOP }, |
1038 | 0 | { "middle", NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE }, |
1039 | 0 | { "bottom", NS_STYLE_VERTICAL_ALIGN_BASELINE }, |
1040 | 0 |
|
1041 | 0 | { "center", NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE }, |
1042 | 0 | { "baseline", NS_STYLE_VERTICAL_ALIGN_BASELINE }, |
1043 | 0 |
|
1044 | 0 | { "texttop", NS_STYLE_VERTICAL_ALIGN_TEXT_TOP }, |
1045 | 0 | { "absmiddle", NS_STYLE_VERTICAL_ALIGN_MIDDLE }, |
1046 | 0 | { "abscenter", NS_STYLE_VERTICAL_ALIGN_MIDDLE }, |
1047 | 0 | { "absbottom", NS_STYLE_VERTICAL_ALIGN_BOTTOM }, |
1048 | 0 | { nullptr, 0 } |
1049 | 0 | }; |
1050 | 0 |
|
1051 | 0 | return aResult.ParseEnumValue(aString, kAlignTable, false); |
1052 | 0 | } |
1053 | | |
1054 | | //---------------------------------------- |
1055 | | |
1056 | | static const nsAttrValue::EnumTable kTableHAlignTable[] = { |
1057 | | { "left", NS_STYLE_TEXT_ALIGN_LEFT }, |
1058 | | { "right", NS_STYLE_TEXT_ALIGN_RIGHT }, |
1059 | | { "center", NS_STYLE_TEXT_ALIGN_CENTER }, |
1060 | | { "char", NS_STYLE_TEXT_ALIGN_CHAR }, |
1061 | | { "justify",NS_STYLE_TEXT_ALIGN_JUSTIFY }, |
1062 | | { nullptr, 0 } |
1063 | | }; |
1064 | | |
1065 | | bool |
1066 | | nsGenericHTMLElement::ParseTableHAlignValue(const nsAString& aString, |
1067 | | nsAttrValue& aResult) |
1068 | 0 | { |
1069 | 0 | return aResult.ParseEnumValue(aString, kTableHAlignTable, false); |
1070 | 0 | } |
1071 | | |
1072 | | //---------------------------------------- |
1073 | | |
1074 | | // This table is used for td, th, tr, col, thead, tbody and tfoot. |
1075 | | static const nsAttrValue::EnumTable kTableCellHAlignTable[] = { |
1076 | | { "left", NS_STYLE_TEXT_ALIGN_MOZ_LEFT }, |
1077 | | { "right", NS_STYLE_TEXT_ALIGN_MOZ_RIGHT }, |
1078 | | { "center", NS_STYLE_TEXT_ALIGN_MOZ_CENTER }, |
1079 | | { "char", NS_STYLE_TEXT_ALIGN_CHAR }, |
1080 | | { "justify",NS_STYLE_TEXT_ALIGN_JUSTIFY }, |
1081 | | { "middle", NS_STYLE_TEXT_ALIGN_MOZ_CENTER }, |
1082 | | { "absmiddle", NS_STYLE_TEXT_ALIGN_CENTER }, |
1083 | | { nullptr, 0 } |
1084 | | }; |
1085 | | |
1086 | | bool |
1087 | | nsGenericHTMLElement::ParseTableCellHAlignValue(const nsAString& aString, |
1088 | | nsAttrValue& aResult) |
1089 | 0 | { |
1090 | 0 | return aResult.ParseEnumValue(aString, kTableCellHAlignTable, false); |
1091 | 0 | } |
1092 | | |
1093 | | //---------------------------------------- |
1094 | | |
1095 | | bool |
1096 | | nsGenericHTMLElement::ParseTableVAlignValue(const nsAString& aString, |
1097 | | nsAttrValue& aResult) |
1098 | 0 | { |
1099 | 0 | return aResult.ParseEnumValue(aString, kTableVAlignTable, false); |
1100 | 0 | } |
1101 | | |
1102 | | bool |
1103 | | nsGenericHTMLElement::ParseDivAlignValue(const nsAString& aString, |
1104 | | nsAttrValue& aResult) |
1105 | 0 | { |
1106 | 0 | return aResult.ParseEnumValue(aString, kDivAlignTable, false); |
1107 | 0 | } |
1108 | | |
1109 | | bool |
1110 | | nsGenericHTMLElement::ParseImageAttribute(nsAtom* aAttribute, |
1111 | | const nsAString& aString, |
1112 | | nsAttrValue& aResult) |
1113 | 0 | { |
1114 | 0 | if ((aAttribute == nsGkAtoms::width) || |
1115 | 0 | (aAttribute == nsGkAtoms::height)) { |
1116 | 0 | return aResult.ParseSpecialIntValue(aString); |
1117 | 0 | } |
1118 | 0 | if ((aAttribute == nsGkAtoms::hspace) || |
1119 | 0 | (aAttribute == nsGkAtoms::vspace) || |
1120 | 0 | (aAttribute == nsGkAtoms::border)) { |
1121 | 0 | return aResult.ParseIntWithBounds(aString, 0); |
1122 | 0 | } |
1123 | 0 | return false; |
1124 | 0 | } |
1125 | | |
1126 | | bool |
1127 | | nsGenericHTMLElement::ParseReferrerAttribute(const nsAString& aString, |
1128 | | nsAttrValue& aResult) |
1129 | 0 | { |
1130 | 0 | static const nsAttrValue::EnumTable kReferrerTable[] = { |
1131 | 0 | { net::kRPS_No_Referrer, static_cast<int16_t>(net::RP_No_Referrer) }, |
1132 | 0 | { net::kRPS_Origin, static_cast<int16_t>(net::RP_Origin) }, |
1133 | 0 | { net::kRPS_Origin_When_Cross_Origin, static_cast<int16_t>(net::RP_Origin_When_Crossorigin) }, |
1134 | 0 | { net::kRPS_No_Referrer_When_Downgrade, static_cast<int16_t>(net::RP_No_Referrer_When_Downgrade) }, |
1135 | 0 | { net::kRPS_Unsafe_URL, static_cast<int16_t>(net::RP_Unsafe_URL) }, |
1136 | 0 | { net::kRPS_Strict_Origin, static_cast<int16_t>(net::RP_Strict_Origin) }, |
1137 | 0 | { net::kRPS_Same_Origin, static_cast<int16_t>(net::RP_Same_Origin) }, |
1138 | 0 | { net::kRPS_Strict_Origin_When_Cross_Origin, static_cast<int16_t>(net::RP_Strict_Origin_When_Cross_Origin) }, |
1139 | 0 | { nullptr, 0 } |
1140 | 0 | }; |
1141 | 0 | return aResult.ParseEnumValue(aString, kReferrerTable, false); |
1142 | 0 | } |
1143 | | |
1144 | | bool |
1145 | | nsGenericHTMLElement::ParseFrameborderValue(const nsAString& aString, |
1146 | | nsAttrValue& aResult) |
1147 | 0 | { |
1148 | 0 | return aResult.ParseEnumValue(aString, kFrameborderTable, false); |
1149 | 0 | } |
1150 | | |
1151 | | bool |
1152 | | nsGenericHTMLElement::ParseScrollingValue(const nsAString& aString, |
1153 | | nsAttrValue& aResult) |
1154 | 0 | { |
1155 | 0 | return aResult.ParseEnumValue(aString, kScrollingTable, false); |
1156 | 0 | } |
1157 | | |
1158 | | static inline void |
1159 | | MapLangAttributeInto(const nsMappedAttributes* aAttributes, MappedDeclarations& aDecls) |
1160 | 0 | { |
1161 | 0 | const nsAttrValue* langValue = aAttributes->GetAttr(nsGkAtoms::lang); |
1162 | 0 | if (!langValue) { |
1163 | 0 | return; |
1164 | 0 | } |
1165 | 0 | MOZ_ASSERT(langValue->Type() == nsAttrValue::eAtom); |
1166 | 0 | aDecls.SetIdentAtomValueIfUnset(eCSSProperty__x_lang, |
1167 | 0 | langValue->GetAtomValue()); |
1168 | 0 | if (!aDecls.PropertyIsSet(eCSSProperty_text_emphasis_position)) { |
1169 | 0 | const nsAtom* lang = langValue->GetAtomValue(); |
1170 | 0 | if (nsStyleUtil::MatchesLanguagePrefix(lang, u"zh")) { |
1171 | 0 | aDecls.SetKeywordValue(eCSSProperty_text_emphasis_position, |
1172 | 0 | NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT_ZH); |
1173 | 0 | } else if (nsStyleUtil::MatchesLanguagePrefix(lang, u"ja") || |
1174 | 0 | nsStyleUtil::MatchesLanguagePrefix(lang, u"mn")) { |
1175 | 0 | // This branch is currently no part of the spec. |
1176 | 0 | // See bug 1040668 comment 69 and comment 75. |
1177 | 0 | aDecls.SetKeywordValue(eCSSProperty_text_emphasis_position, |
1178 | 0 | NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT); |
1179 | 0 | } |
1180 | 0 | } |
1181 | 0 | } |
1182 | | |
1183 | | /** |
1184 | | * Handle attributes common to all html elements |
1185 | | */ |
1186 | | void |
1187 | | nsGenericHTMLElement::MapCommonAttributesIntoExceptHidden(const nsMappedAttributes* aAttributes, |
1188 | | MappedDeclarations& aDecls) |
1189 | 0 | { |
1190 | 0 | if (!aDecls.PropertyIsSet(eCSSProperty__moz_user_modify)) { |
1191 | 0 | const nsAttrValue* value = |
1192 | 0 | aAttributes->GetAttr(nsGkAtoms::contenteditable); |
1193 | 0 | if (value) { |
1194 | 0 | if (value->Equals(nsGkAtoms::_empty, eCaseMatters) || |
1195 | 0 | value->Equals(nsGkAtoms::_true, eIgnoreCase)) { |
1196 | 0 | aDecls.SetKeywordValue(eCSSProperty__moz_user_modify, |
1197 | 0 | StyleUserModify::ReadWrite); |
1198 | 0 | } |
1199 | 0 | else if (value->Equals(nsGkAtoms::_false, eIgnoreCase)) { |
1200 | 0 | aDecls.SetKeywordValue(eCSSProperty__moz_user_modify, |
1201 | 0 | StyleUserModify::ReadOnly); |
1202 | 0 | } |
1203 | 0 | } |
1204 | 0 | } |
1205 | 0 |
|
1206 | 0 | MapLangAttributeInto(aAttributes, aDecls); |
1207 | 0 | } |
1208 | | |
1209 | | void |
1210 | | nsGenericHTMLElement::MapCommonAttributesInto(const nsMappedAttributes* aAttributes, |
1211 | | MappedDeclarations& aDecls) |
1212 | 0 | { |
1213 | 0 | MapCommonAttributesIntoExceptHidden(aAttributes, aDecls); |
1214 | 0 |
|
1215 | 0 | if (!aDecls.PropertyIsSet(eCSSProperty_display)) { |
1216 | 0 | if (aAttributes->IndexOfAttr(nsGkAtoms::hidden) >= 0) { |
1217 | 0 | aDecls.SetKeywordValue(eCSSProperty_display, StyleDisplay::None); |
1218 | 0 | } |
1219 | 0 | } |
1220 | 0 | } |
1221 | | |
1222 | | /* static */ const nsGenericHTMLElement::MappedAttributeEntry |
1223 | | nsGenericHTMLElement::sCommonAttributeMap[] = { |
1224 | | { &nsGkAtoms::contenteditable }, |
1225 | | { &nsGkAtoms::lang }, |
1226 | | { &nsGkAtoms::hidden }, |
1227 | | { nullptr } |
1228 | | }; |
1229 | | |
1230 | | /* static */ const Element::MappedAttributeEntry |
1231 | | nsGenericHTMLElement::sImageMarginSizeAttributeMap[] = { |
1232 | | { &nsGkAtoms::width }, |
1233 | | { &nsGkAtoms::height }, |
1234 | | { &nsGkAtoms::hspace }, |
1235 | | { &nsGkAtoms::vspace }, |
1236 | | { nullptr } |
1237 | | }; |
1238 | | |
1239 | | /* static */ const Element::MappedAttributeEntry |
1240 | | nsGenericHTMLElement::sImageAlignAttributeMap[] = { |
1241 | | { &nsGkAtoms::align }, |
1242 | | { nullptr } |
1243 | | }; |
1244 | | |
1245 | | /* static */ const Element::MappedAttributeEntry |
1246 | | nsGenericHTMLElement::sDivAlignAttributeMap[] = { |
1247 | | { &nsGkAtoms::align }, |
1248 | | { nullptr } |
1249 | | }; |
1250 | | |
1251 | | /* static */ const Element::MappedAttributeEntry |
1252 | | nsGenericHTMLElement::sImageBorderAttributeMap[] = { |
1253 | | { &nsGkAtoms::border }, |
1254 | | { nullptr } |
1255 | | }; |
1256 | | |
1257 | | /* static */ const Element::MappedAttributeEntry |
1258 | | nsGenericHTMLElement::sBackgroundAttributeMap[] = { |
1259 | | { &nsGkAtoms::background }, |
1260 | | { &nsGkAtoms::bgcolor }, |
1261 | | { nullptr } |
1262 | | }; |
1263 | | |
1264 | | /* static */ const Element::MappedAttributeEntry |
1265 | | nsGenericHTMLElement::sBackgroundColorAttributeMap[] = { |
1266 | | { &nsGkAtoms::bgcolor }, |
1267 | | { nullptr } |
1268 | | }; |
1269 | | |
1270 | | void |
1271 | | nsGenericHTMLElement::MapImageAlignAttributeInto(const nsMappedAttributes* aAttributes, |
1272 | | MappedDeclarations& aDecls) |
1273 | 0 | { |
1274 | 0 | const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::align); |
1275 | 0 | if (value && value->Type() == nsAttrValue::eEnum) { |
1276 | 0 | int32_t align = value->GetEnumValue(); |
1277 | 0 | if (!aDecls.PropertyIsSet(eCSSProperty_float)) { |
1278 | 0 | if (align == NS_STYLE_TEXT_ALIGN_LEFT) { |
1279 | 0 | aDecls.SetKeywordValue(eCSSProperty_float, StyleFloat::Left); |
1280 | 0 | } else if (align == NS_STYLE_TEXT_ALIGN_RIGHT) { |
1281 | 0 | aDecls.SetKeywordValue(eCSSProperty_float, StyleFloat::Right); |
1282 | 0 | } |
1283 | 0 | } |
1284 | 0 | if (!aDecls.PropertyIsSet(eCSSProperty_vertical_align)) { |
1285 | 0 | switch (align) { |
1286 | 0 | case NS_STYLE_TEXT_ALIGN_LEFT: |
1287 | 0 | case NS_STYLE_TEXT_ALIGN_RIGHT: |
1288 | 0 | break; |
1289 | 0 | default: |
1290 | 0 | aDecls.SetKeywordValue(eCSSProperty_vertical_align, align); |
1291 | 0 | break; |
1292 | 0 | } |
1293 | 0 | } |
1294 | 0 | } |
1295 | 0 | } |
1296 | | |
1297 | | void |
1298 | | nsGenericHTMLElement::MapDivAlignAttributeInto(const nsMappedAttributes* aAttributes, |
1299 | | MappedDeclarations& aDecls) |
1300 | 0 | { |
1301 | 0 | if (!aDecls.PropertyIsSet(eCSSProperty_text_align)) { |
1302 | 0 | // align: enum |
1303 | 0 | const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::align); |
1304 | 0 | if (value && value->Type() == nsAttrValue::eEnum) |
1305 | 0 | aDecls.SetKeywordValue(eCSSProperty_text_align, value->GetEnumValue()); |
1306 | 0 | } |
1307 | 0 | } |
1308 | | |
1309 | | void |
1310 | | nsGenericHTMLElement::MapVAlignAttributeInto(const nsMappedAttributes* aAttributes, |
1311 | | MappedDeclarations& aDecls) |
1312 | 0 | { |
1313 | 0 | if (!aDecls.PropertyIsSet(eCSSProperty_vertical_align)) { |
1314 | 0 | // align: enum |
1315 | 0 | const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::valign); |
1316 | 0 | if (value && value->Type() == nsAttrValue::eEnum) |
1317 | 0 | aDecls.SetKeywordValue(eCSSProperty_vertical_align, value->GetEnumValue()); |
1318 | 0 | } |
1319 | 0 | } |
1320 | | |
1321 | | void |
1322 | | nsGenericHTMLElement::MapImageMarginAttributeInto(const nsMappedAttributes* aAttributes, |
1323 | | MappedDeclarations& aDecls) |
1324 | 0 | { |
1325 | 0 | const nsAttrValue* value; |
1326 | 0 |
|
1327 | 0 | // hspace: value |
1328 | 0 | value = aAttributes->GetAttr(nsGkAtoms::hspace); |
1329 | 0 | if (value) { |
1330 | 0 | if (value->Type() == nsAttrValue::eInteger) { |
1331 | 0 | aDecls.SetPixelValueIfUnset(eCSSProperty_margin_left, |
1332 | 0 | (float)value->GetIntegerValue()); |
1333 | 0 | aDecls.SetPixelValueIfUnset(eCSSProperty_margin_right, |
1334 | 0 | (float)value->GetIntegerValue()); |
1335 | 0 | } else if (value->Type() == nsAttrValue::ePercent) { |
1336 | 0 | aDecls.SetPercentValueIfUnset(eCSSProperty_margin_left, |
1337 | 0 | value->GetPercentValue()); |
1338 | 0 | aDecls.SetPercentValueIfUnset(eCSSProperty_margin_right, |
1339 | 0 | value->GetPercentValue()); |
1340 | 0 | } |
1341 | 0 | } |
1342 | 0 |
|
1343 | 0 | // vspace: value |
1344 | 0 | value = aAttributes->GetAttr(nsGkAtoms::vspace); |
1345 | 0 | if (value) { |
1346 | 0 | if (value->Type() == nsAttrValue::eInteger) { |
1347 | 0 | aDecls.SetPixelValueIfUnset(eCSSProperty_margin_top, |
1348 | 0 | (float)value->GetIntegerValue()); |
1349 | 0 | aDecls.SetPixelValueIfUnset(eCSSProperty_margin_bottom, |
1350 | 0 | (float)value->GetIntegerValue()); |
1351 | 0 | } else if (value->Type() == nsAttrValue::ePercent) { |
1352 | 0 | aDecls.SetPercentValueIfUnset(eCSSProperty_margin_top, |
1353 | 0 | value->GetPercentValue()); |
1354 | 0 | aDecls.SetPercentValueIfUnset(eCSSProperty_margin_bottom, |
1355 | 0 | value->GetPercentValue()); |
1356 | 0 | } |
1357 | 0 | } |
1358 | 0 | } |
1359 | | |
1360 | | void |
1361 | | nsGenericHTMLElement::MapWidthAttributeInto(const nsMappedAttributes* aAttributes, |
1362 | | MappedDeclarations& aDecls) |
1363 | 0 | { |
1364 | 0 | // width: value |
1365 | 0 | if (!aDecls.PropertyIsSet(eCSSProperty_width)) { |
1366 | 0 | const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::width); |
1367 | 0 | if (value && value->Type() == nsAttrValue::eInteger) { |
1368 | 0 | aDecls.SetPixelValue(eCSSProperty_width, |
1369 | 0 | (float)value->GetIntegerValue()); |
1370 | 0 | } else if (value && value->Type() == nsAttrValue::ePercent) { |
1371 | 0 | aDecls.SetPercentValue(eCSSProperty_width, |
1372 | 0 | value->GetPercentValue()); |
1373 | 0 | } |
1374 | 0 | } |
1375 | 0 | } |
1376 | | |
1377 | | void |
1378 | | nsGenericHTMLElement::MapHeightAttributeInto(const nsMappedAttributes* aAttributes, |
1379 | | MappedDeclarations& aDecls) |
1380 | 0 | { |
1381 | 0 | // height: value |
1382 | 0 | if (!aDecls.PropertyIsSet(eCSSProperty_height)) { |
1383 | 0 | const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::height); |
1384 | 0 | if (value && value->Type() == nsAttrValue::eInteger) { |
1385 | 0 | aDecls.SetPixelValue(eCSSProperty_height, |
1386 | 0 | (float)value->GetIntegerValue()); |
1387 | 0 | } else if (value && value->Type() == nsAttrValue::ePercent) { |
1388 | 0 | aDecls.SetPercentValue(eCSSProperty_height, |
1389 | 0 | value->GetPercentValue()); |
1390 | 0 | } |
1391 | 0 | } |
1392 | 0 | } |
1393 | | |
1394 | | void |
1395 | | nsGenericHTMLElement::MapImageSizeAttributesInto(const nsMappedAttributes* aAttributes, |
1396 | | MappedDeclarations& aDecls) |
1397 | 0 | { |
1398 | 0 | nsGenericHTMLElement::MapWidthAttributeInto(aAttributes, aDecls); |
1399 | 0 | nsGenericHTMLElement::MapHeightAttributeInto(aAttributes, aDecls); |
1400 | 0 | } |
1401 | | |
1402 | | void |
1403 | | nsGenericHTMLElement::MapImageBorderAttributeInto(const nsMappedAttributes* aAttributes, |
1404 | | MappedDeclarations& aDecls) |
1405 | 0 | { |
1406 | 0 | // border: pixels |
1407 | 0 | const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::border); |
1408 | 0 | if (!value) |
1409 | 0 | return; |
1410 | 0 | |
1411 | 0 | nscoord val = 0; |
1412 | 0 | if (value->Type() == nsAttrValue::eInteger) |
1413 | 0 | val = value->GetIntegerValue(); |
1414 | 0 |
|
1415 | 0 | aDecls.SetPixelValueIfUnset(eCSSProperty_border_top_width, (float)val); |
1416 | 0 | aDecls.SetPixelValueIfUnset(eCSSProperty_border_right_width, (float)val); |
1417 | 0 | aDecls.SetPixelValueIfUnset(eCSSProperty_border_bottom_width, (float)val); |
1418 | 0 | aDecls.SetPixelValueIfUnset(eCSSProperty_border_left_width, (float)val); |
1419 | 0 |
|
1420 | 0 | aDecls.SetKeywordValueIfUnset(eCSSProperty_border_top_style, |
1421 | 0 | NS_STYLE_BORDER_STYLE_SOLID); |
1422 | 0 | aDecls.SetKeywordValueIfUnset(eCSSProperty_border_right_style, |
1423 | 0 | NS_STYLE_BORDER_STYLE_SOLID); |
1424 | 0 | aDecls.SetKeywordValueIfUnset(eCSSProperty_border_bottom_style, |
1425 | 0 | NS_STYLE_BORDER_STYLE_SOLID); |
1426 | 0 | aDecls.SetKeywordValueIfUnset(eCSSProperty_border_left_style, |
1427 | 0 | NS_STYLE_BORDER_STYLE_SOLID); |
1428 | 0 |
|
1429 | 0 | aDecls.SetCurrentColorIfUnset(eCSSProperty_border_top_color); |
1430 | 0 | aDecls.SetCurrentColorIfUnset(eCSSProperty_border_right_color); |
1431 | 0 | aDecls.SetCurrentColorIfUnset(eCSSProperty_border_bottom_color); |
1432 | 0 | aDecls.SetCurrentColorIfUnset(eCSSProperty_border_left_color); |
1433 | 0 | } |
1434 | | |
1435 | | void |
1436 | | nsGenericHTMLElement::MapBackgroundInto(const nsMappedAttributes* aAttributes, |
1437 | | MappedDeclarations& aDecls) |
1438 | 0 | { |
1439 | 0 |
|
1440 | 0 | if (!aDecls.PropertyIsSet(eCSSProperty_background_image)) { |
1441 | 0 | // background |
1442 | 0 | nsAttrValue* value = |
1443 | 0 | const_cast<nsAttrValue*>(aAttributes->GetAttr(nsGkAtoms::background)); |
1444 | 0 | if (value) { |
1445 | 0 | aDecls.SetBackgroundImage(*value); |
1446 | 0 | } |
1447 | 0 | } |
1448 | 0 | } |
1449 | | |
1450 | | void |
1451 | | nsGenericHTMLElement::MapBGColorInto(const nsMappedAttributes* aAttributes, |
1452 | | MappedDeclarations& aDecls) |
1453 | 0 | { |
1454 | 0 | if (!aDecls.PropertyIsSet(eCSSProperty_background_color)) { |
1455 | 0 | const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::bgcolor); |
1456 | 0 | nscolor color; |
1457 | 0 | if (value && value->GetColorValue(color)) { |
1458 | 0 | aDecls.SetColorValue(eCSSProperty_background_color, color); |
1459 | 0 | } |
1460 | 0 | } |
1461 | 0 | } |
1462 | | |
1463 | | void |
1464 | | nsGenericHTMLElement::MapBackgroundAttributesInto(const nsMappedAttributes* aAttributes, |
1465 | | MappedDeclarations& aDecls) |
1466 | 0 | { |
1467 | 0 | MapBackgroundInto(aAttributes, aDecls); |
1468 | 0 | MapBGColorInto(aAttributes, aDecls); |
1469 | 0 | } |
1470 | | |
1471 | | //---------------------------------------------------------------------- |
1472 | | |
1473 | | int32_t |
1474 | | nsGenericHTMLElement::GetIntAttr(nsAtom* aAttr, int32_t aDefault) const |
1475 | 0 | { |
1476 | 0 | const nsAttrValue* attrVal = mAttrs.GetAttr(aAttr); |
1477 | 0 | if (attrVal && attrVal->Type() == nsAttrValue::eInteger) { |
1478 | 0 | return attrVal->GetIntegerValue(); |
1479 | 0 | } |
1480 | 0 | return aDefault; |
1481 | 0 | } |
1482 | | |
1483 | | nsresult |
1484 | | nsGenericHTMLElement::SetIntAttr(nsAtom* aAttr, int32_t aValue) |
1485 | 0 | { |
1486 | 0 | nsAutoString value; |
1487 | 0 | value.AppendInt(aValue); |
1488 | 0 |
|
1489 | 0 | return SetAttr(kNameSpaceID_None, aAttr, value, true); |
1490 | 0 | } |
1491 | | |
1492 | | uint32_t |
1493 | | nsGenericHTMLElement::GetUnsignedIntAttr(nsAtom* aAttr, |
1494 | | uint32_t aDefault) const |
1495 | 0 | { |
1496 | 0 | const nsAttrValue* attrVal = mAttrs.GetAttr(aAttr); |
1497 | 0 | if (!attrVal || attrVal->Type() != nsAttrValue::eInteger) { |
1498 | 0 | return aDefault; |
1499 | 0 | } |
1500 | 0 | |
1501 | 0 | return attrVal->GetIntegerValue(); |
1502 | 0 | } |
1503 | | |
1504 | | void |
1505 | | nsGenericHTMLElement::GetURIAttr(nsAtom* aAttr, nsAtom* aBaseAttr, |
1506 | | nsAString& aResult) const |
1507 | 0 | { |
1508 | 0 | nsCOMPtr<nsIURI> uri; |
1509 | 0 | bool hadAttr = GetURIAttr(aAttr, aBaseAttr, getter_AddRefs(uri)); |
1510 | 0 | if (!hadAttr) { |
1511 | 0 | aResult.Truncate(); |
1512 | 0 | return; |
1513 | 0 | } |
1514 | 0 | |
1515 | 0 | if (!uri) { |
1516 | 0 | // Just return the attr value |
1517 | 0 | GetAttr(kNameSpaceID_None, aAttr, aResult); |
1518 | 0 | return; |
1519 | 0 | } |
1520 | 0 | |
1521 | 0 | nsAutoCString spec; |
1522 | 0 | uri->GetSpec(spec); |
1523 | 0 | CopyUTF8toUTF16(spec, aResult); |
1524 | 0 | } |
1525 | | |
1526 | | bool |
1527 | | nsGenericHTMLElement::GetURIAttr(nsAtom* aAttr, nsAtom* aBaseAttr, nsIURI** aURI) const |
1528 | 0 | { |
1529 | 0 | *aURI = nullptr; |
1530 | 0 |
|
1531 | 0 | const nsAttrValue* attr = mAttrs.GetAttr(aAttr); |
1532 | 0 | if (!attr) { |
1533 | 0 | return false; |
1534 | 0 | } |
1535 | 0 | |
1536 | 0 | nsCOMPtr<nsIURI> baseURI = GetBaseURI(); |
1537 | 0 |
|
1538 | 0 | if (aBaseAttr) { |
1539 | 0 | nsAutoString baseAttrValue; |
1540 | 0 | if (GetAttr(kNameSpaceID_None, aBaseAttr, baseAttrValue)) { |
1541 | 0 | nsCOMPtr<nsIURI> baseAttrURI; |
1542 | 0 | nsresult rv = |
1543 | 0 | nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(baseAttrURI), |
1544 | 0 | baseAttrValue, OwnerDoc(), |
1545 | 0 | baseURI); |
1546 | 0 | if (NS_FAILED(rv)) { |
1547 | 0 | return true; |
1548 | 0 | } |
1549 | 0 | baseURI.swap(baseAttrURI); |
1550 | 0 | } |
1551 | 0 | } |
1552 | 0 |
|
1553 | 0 | // Don't care about return value. If it fails, we still want to |
1554 | 0 | // return true, and *aURI will be null. |
1555 | 0 | nsContentUtils::NewURIWithDocumentCharset(aURI, |
1556 | 0 | attr->GetStringValue(), |
1557 | 0 | OwnerDoc(), baseURI); |
1558 | 0 | return true; |
1559 | 0 | } |
1560 | | |
1561 | | HTMLMenuElement* |
1562 | | nsGenericHTMLElement::GetContextMenu() const |
1563 | 0 | { |
1564 | 0 | nsAutoString value; |
1565 | 0 | GetHTMLAttr(nsGkAtoms::contextmenu, value); |
1566 | 0 | if (!value.IsEmpty()) { |
1567 | 0 | //XXXsmaug How should this work in Shadow DOM? |
1568 | 0 | nsIDocument* doc = GetUncomposedDoc(); |
1569 | 0 | if (doc) { |
1570 | 0 | return HTMLMenuElement::FromNodeOrNull(doc->GetElementById(value)); |
1571 | 0 | } |
1572 | 0 | } |
1573 | 0 | return nullptr; |
1574 | 0 | } |
1575 | | |
1576 | | bool |
1577 | | nsGenericHTMLElement::IsLabelable() const |
1578 | 0 | { |
1579 | 0 | return IsAnyOfHTMLElements(nsGkAtoms::progress, nsGkAtoms::meter); |
1580 | 0 | } |
1581 | | |
1582 | | /* static */ bool |
1583 | | nsGenericHTMLElement::MatchLabelsElement(Element* aElement, int32_t aNamespaceID, |
1584 | | nsAtom* aAtom, void* aData) |
1585 | 0 | { |
1586 | 0 | HTMLLabelElement* element = HTMLLabelElement::FromNode(aElement); |
1587 | 0 | return element && element->GetControl() == aData; |
1588 | 0 | } |
1589 | | |
1590 | | already_AddRefed<nsINodeList> |
1591 | | nsGenericHTMLElement::Labels() |
1592 | 0 | { |
1593 | 0 | MOZ_ASSERT(IsLabelable(), |
1594 | 0 | "Labels() only allow labelable elements to use it."); |
1595 | 0 | nsExtendedDOMSlots* slots = ExtendedDOMSlots(); |
1596 | 0 |
|
1597 | 0 | if (!slots->mLabelsList) { |
1598 | 0 | slots->mLabelsList = new nsLabelsNodeList(SubtreeRoot(), MatchLabelsElement, |
1599 | 0 | nullptr, this); |
1600 | 0 | } |
1601 | 0 |
|
1602 | 0 | RefPtr<nsLabelsNodeList> labels = slots->mLabelsList; |
1603 | 0 | return labels.forget(); |
1604 | 0 | } |
1605 | | |
1606 | | bool |
1607 | | nsGenericHTMLElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const |
1608 | 0 | { |
1609 | 0 | return IsAnyOfHTMLElements(nsGkAtoms::details, nsGkAtoms::embed, |
1610 | 0 | nsGkAtoms::keygen) || |
1611 | 0 | (!aIgnoreTabindex && HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)); |
1612 | 0 | } |
1613 | | |
1614 | | // static |
1615 | | bool |
1616 | | nsGenericHTMLElement::TouchEventsEnabled(JSContext* aCx, JSObject* aGlobal) |
1617 | 0 | { |
1618 | 0 | return TouchEvent::PrefEnabled(aCx, aGlobal); |
1619 | 0 | } |
1620 | | |
1621 | | //---------------------------------------------------------------------- |
1622 | | |
1623 | | nsGenericHTMLFormElement::nsGenericHTMLFormElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, |
1624 | | uint8_t aType) |
1625 | | : nsGenericHTMLElement(std::move(aNodeInfo)) |
1626 | | , nsIFormControl(aType) |
1627 | | , mForm(nullptr) |
1628 | | , mFieldSet(nullptr) |
1629 | 0 | { |
1630 | 0 | // We should add the NS_EVENT_STATE_ENABLED bit here as needed, but |
1631 | 0 | // that depends on our type, which is not initialized yet. So we |
1632 | 0 | // have to do this in subclasses. |
1633 | 0 | } |
1634 | | |
1635 | | nsGenericHTMLFormElement::~nsGenericHTMLFormElement() |
1636 | 0 | { |
1637 | 0 | if (mFieldSet) { |
1638 | 0 | mFieldSet->RemoveElement(this); |
1639 | 0 | } |
1640 | 0 |
|
1641 | 0 | // Check that this element doesn't know anything about its form at this point. |
1642 | 0 | NS_ASSERTION(!mForm, "mForm should be null at this point!"); |
1643 | 0 | } |
1644 | | |
1645 | | NS_IMPL_ISUPPORTS_INHERITED(nsGenericHTMLFormElement, |
1646 | | nsGenericHTMLElement, |
1647 | | nsIFormControl) |
1648 | | |
1649 | | nsINode* |
1650 | | nsGenericHTMLFormElement::GetScopeChainParent() const |
1651 | 0 | { |
1652 | 0 | return mForm ? mForm : nsGenericHTMLElement::GetScopeChainParent(); |
1653 | 0 | } |
1654 | | |
1655 | | bool |
1656 | | nsGenericHTMLFormElement::IsNodeOfType(uint32_t aFlags) const |
1657 | 0 | { |
1658 | 0 | return !(aFlags & ~eHTML_FORM_CONTROL); |
1659 | 0 | } |
1660 | | |
1661 | | void |
1662 | | nsGenericHTMLFormElement::SaveSubtreeState() |
1663 | 0 | { |
1664 | 0 | SaveState(); |
1665 | 0 |
|
1666 | 0 | nsGenericHTMLElement::SaveSubtreeState(); |
1667 | 0 | } |
1668 | | |
1669 | | void |
1670 | | nsGenericHTMLFormElement::SetForm(HTMLFormElement* aForm) |
1671 | 0 | { |
1672 | 0 | MOZ_ASSERT(aForm, "Don't pass null here"); |
1673 | 0 | NS_ASSERTION(!mForm, |
1674 | 0 | "We don't support switching from one non-null form to another."); |
1675 | 0 |
|
1676 | 0 | SetForm(aForm, false); |
1677 | 0 | } |
1678 | | |
1679 | | void nsGenericHTMLFormElement::SetForm(HTMLFormElement* aForm, bool aBindToTree) |
1680 | 0 | { |
1681 | 0 | if (aForm) { |
1682 | 0 | BeforeSetForm(aBindToTree); |
1683 | 0 | } |
1684 | 0 |
|
1685 | 0 | // keep a *weak* ref to the form here |
1686 | 0 | mForm = aForm; |
1687 | 0 | } |
1688 | | |
1689 | | void |
1690 | | nsGenericHTMLFormElement::ClearForm(bool aRemoveFromForm, bool aUnbindOrDelete) |
1691 | 0 | { |
1692 | 0 | NS_ASSERTION((mForm != nullptr) == HasFlag(ADDED_TO_FORM), |
1693 | 0 | "Form control should have had flag set correctly"); |
1694 | 0 |
|
1695 | 0 | if (!mForm) { |
1696 | 0 | return; |
1697 | 0 | } |
1698 | 0 | |
1699 | 0 | if (aRemoveFromForm) { |
1700 | 0 | nsAutoString nameVal, idVal; |
1701 | 0 | GetAttr(kNameSpaceID_None, nsGkAtoms::name, nameVal); |
1702 | 0 | GetAttr(kNameSpaceID_None, nsGkAtoms::id, idVal); |
1703 | 0 |
|
1704 | 0 | mForm->RemoveElement(this, true); |
1705 | 0 |
|
1706 | 0 | if (!nameVal.IsEmpty()) { |
1707 | 0 | mForm->RemoveElementFromTable(this, nameVal); |
1708 | 0 | } |
1709 | 0 |
|
1710 | 0 | if (!idVal.IsEmpty()) { |
1711 | 0 | mForm->RemoveElementFromTable(this, idVal); |
1712 | 0 | } |
1713 | 0 | } |
1714 | 0 |
|
1715 | 0 | UnsetFlags(ADDED_TO_FORM); |
1716 | 0 | mForm = nullptr; |
1717 | 0 |
|
1718 | 0 | AfterClearForm(aUnbindOrDelete); |
1719 | 0 | } |
1720 | | |
1721 | | Element* |
1722 | | nsGenericHTMLFormElement::GetFormElement() |
1723 | 0 | { |
1724 | 0 | return mForm; |
1725 | 0 | } |
1726 | | |
1727 | | HTMLFieldSetElement* |
1728 | | nsGenericHTMLFormElement::GetFieldSet() |
1729 | 0 | { |
1730 | 0 | return mFieldSet; |
1731 | 0 | } |
1732 | | |
1733 | | nsIContent::IMEState |
1734 | | nsGenericHTMLFormElement::GetDesiredIMEState() |
1735 | 0 | { |
1736 | 0 | TextEditor* textEditor = GetTextEditorInternal(); |
1737 | 0 | if (!textEditor) { |
1738 | 0 | return nsGenericHTMLElement::GetDesiredIMEState(); |
1739 | 0 | } |
1740 | 0 | IMEState state; |
1741 | 0 | nsresult rv = textEditor->GetPreferredIMEState(&state); |
1742 | 0 | if (NS_FAILED(rv)) { |
1743 | 0 | return nsGenericHTMLElement::GetDesiredIMEState(); |
1744 | 0 | } |
1745 | 0 | return state; |
1746 | 0 | } |
1747 | | |
1748 | | nsresult |
1749 | | nsGenericHTMLFormElement::BindToTree(nsIDocument* aDocument, |
1750 | | nsIContent* aParent, |
1751 | | nsIContent* aBindingParent) |
1752 | 0 | { |
1753 | 0 | nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent, |
1754 | 0 | aBindingParent); |
1755 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1756 | 0 |
|
1757 | 0 | // An autofocus event has to be launched if the autofocus attribute is |
1758 | 0 | // specified and the element accept the autofocus attribute. In addition, |
1759 | 0 | // the document should not be already loaded and the "browser.autofocus" |
1760 | 0 | // preference should be 'true'. |
1761 | 0 | if (IsAutofocusable() && HasAttr(kNameSpaceID_None, nsGkAtoms::autofocus) && |
1762 | 0 | nsContentUtils::AutoFocusEnabled() && aDocument) { |
1763 | 0 | aDocument->SetAutoFocusElement(this); |
1764 | 0 | } |
1765 | 0 |
|
1766 | 0 | // If @form is set, the element *has* to be in a composed document, otherwise |
1767 | 0 | // it wouldn't be possible to find an element with the corresponding id. |
1768 | 0 | // If @form isn't set, the element *has* to have a parent, otherwise it |
1769 | 0 | // wouldn't be possible to find a form ancestor. |
1770 | 0 | // We should not call UpdateFormOwner if none of these conditions are |
1771 | 0 | // fulfilled. |
1772 | 0 | if (HasAttr(kNameSpaceID_None, nsGkAtoms::form) ? IsInComposedDoc() |
1773 | 0 | : !!aParent) { |
1774 | 0 | UpdateFormOwner(true, nullptr); |
1775 | 0 | } |
1776 | 0 |
|
1777 | 0 | // Set parent fieldset which should be used for the disabled state. |
1778 | 0 | UpdateFieldSet(false); |
1779 | 0 |
|
1780 | 0 | return NS_OK; |
1781 | 0 | } |
1782 | | |
1783 | | void |
1784 | | nsGenericHTMLFormElement::UnbindFromTree(bool aDeep, bool aNullParent) |
1785 | 0 | { |
1786 | 0 | // Save state before doing anything |
1787 | 0 | SaveState(); |
1788 | 0 |
|
1789 | 0 | if (mForm) { |
1790 | 0 | // Might need to unset mForm |
1791 | 0 | if (aNullParent) { |
1792 | 0 | // No more parent means no more form |
1793 | 0 | ClearForm(true, true); |
1794 | 0 | } else { |
1795 | 0 | // Recheck whether we should still have an mForm. |
1796 | 0 | if (HasAttr(kNameSpaceID_None, nsGkAtoms::form) || |
1797 | 0 | !FindAncestorForm(mForm)) { |
1798 | 0 | ClearForm(true, true); |
1799 | 0 | } else { |
1800 | 0 | UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT); |
1801 | 0 | } |
1802 | 0 | } |
1803 | 0 |
|
1804 | 0 | if (!mForm) { |
1805 | 0 | // Our novalidate state might have changed |
1806 | 0 | UpdateState(false); |
1807 | 0 | } |
1808 | 0 | } |
1809 | 0 |
|
1810 | 0 | // We have to remove the form id observer if there was one. |
1811 | 0 | // We will re-add one later if needed (during bind to tree). |
1812 | 0 | if (nsContentUtils::HasNonEmptyAttr(this, kNameSpaceID_None, |
1813 | 0 | nsGkAtoms::form)) { |
1814 | 0 | RemoveFormIdObserver(); |
1815 | 0 | } |
1816 | 0 |
|
1817 | 0 | nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); |
1818 | 0 |
|
1819 | 0 | // The element might not have a fieldset anymore. |
1820 | 0 | UpdateFieldSet(false); |
1821 | 0 | } |
1822 | | |
1823 | | nsresult |
1824 | | nsGenericHTMLFormElement::BeforeSetAttr(int32_t aNameSpaceID, nsAtom* aName, |
1825 | | const nsAttrValueOrString* aValue, |
1826 | | bool aNotify) |
1827 | 0 | { |
1828 | 0 | if (aNameSpaceID == kNameSpaceID_None) { |
1829 | 0 | nsAutoString tmp; |
1830 | 0 |
|
1831 | 0 | // remove the control from the hashtable as needed |
1832 | 0 |
|
1833 | 0 | if (mForm && (aName == nsGkAtoms::name || aName == nsGkAtoms::id)) { |
1834 | 0 | GetAttr(kNameSpaceID_None, aName, tmp); |
1835 | 0 |
|
1836 | 0 | if (!tmp.IsEmpty()) { |
1837 | 0 | mForm->RemoveElementFromTable(this, tmp); |
1838 | 0 | } |
1839 | 0 | } |
1840 | 0 |
|
1841 | 0 | if (mForm && aName == nsGkAtoms::type) { |
1842 | 0 | GetAttr(kNameSpaceID_None, nsGkAtoms::name, tmp); |
1843 | 0 |
|
1844 | 0 | if (!tmp.IsEmpty()) { |
1845 | 0 | mForm->RemoveElementFromTable(this, tmp); |
1846 | 0 | } |
1847 | 0 |
|
1848 | 0 | GetAttr(kNameSpaceID_None, nsGkAtoms::id, tmp); |
1849 | 0 |
|
1850 | 0 | if (!tmp.IsEmpty()) { |
1851 | 0 | mForm->RemoveElementFromTable(this, tmp); |
1852 | 0 | } |
1853 | 0 |
|
1854 | 0 | mForm->RemoveElement(this, false); |
1855 | 0 | } |
1856 | 0 |
|
1857 | 0 | if (aName == nsGkAtoms::form) { |
1858 | 0 | // If @form isn't set or set to the empty string, there were no observer |
1859 | 0 | // so we don't have to remove it. |
1860 | 0 | if (nsContentUtils::HasNonEmptyAttr(this, kNameSpaceID_None, |
1861 | 0 | nsGkAtoms::form)) { |
1862 | 0 | // The current form id observer is no longer needed. |
1863 | 0 | // A new one may be added in AfterSetAttr. |
1864 | 0 | RemoveFormIdObserver(); |
1865 | 0 | } |
1866 | 0 | } |
1867 | 0 | } |
1868 | 0 |
|
1869 | 0 | return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID, aName, |
1870 | 0 | aValue, aNotify); |
1871 | 0 | } |
1872 | | |
1873 | | nsresult |
1874 | | nsGenericHTMLFormElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName, |
1875 | | const nsAttrValue* aValue, |
1876 | | const nsAttrValue* aOldValue, |
1877 | | nsIPrincipal* aMaybeScriptedPrincipal, |
1878 | | bool aNotify) |
1879 | 0 | { |
1880 | 0 | if (aNameSpaceID == kNameSpaceID_None) { |
1881 | 0 | // add the control to the hashtable as needed |
1882 | 0 |
|
1883 | 0 | if (mForm && (aName == nsGkAtoms::name || aName == nsGkAtoms::id) && |
1884 | 0 | aValue && !aValue->IsEmptyString()) { |
1885 | 0 | MOZ_ASSERT(aValue->Type() == nsAttrValue::eAtom, |
1886 | 0 | "Expected atom value for name/id"); |
1887 | 0 | mForm->AddElementToTable(this, |
1888 | 0 | nsDependentAtomString(aValue->GetAtomValue())); |
1889 | 0 | } |
1890 | 0 |
|
1891 | 0 | if (mForm && aName == nsGkAtoms::type) { |
1892 | 0 | nsAutoString tmp; |
1893 | 0 |
|
1894 | 0 | GetAttr(kNameSpaceID_None, nsGkAtoms::name, tmp); |
1895 | 0 |
|
1896 | 0 | if (!tmp.IsEmpty()) { |
1897 | 0 | mForm->AddElementToTable(this, tmp); |
1898 | 0 | } |
1899 | 0 |
|
1900 | 0 | GetAttr(kNameSpaceID_None, nsGkAtoms::id, tmp); |
1901 | 0 |
|
1902 | 0 | if (!tmp.IsEmpty()) { |
1903 | 0 | mForm->AddElementToTable(this, tmp); |
1904 | 0 | } |
1905 | 0 |
|
1906 | 0 | mForm->AddElement(this, false, aNotify); |
1907 | 0 | } |
1908 | 0 |
|
1909 | 0 | if (aName == nsGkAtoms::form) { |
1910 | 0 | // We need a new form id observer. |
1911 | 0 | DocumentOrShadowRoot* docOrShadow = |
1912 | 0 | GetUncomposedDocOrConnectedShadowRoot(); |
1913 | 0 | if (docOrShadow) { |
1914 | 0 | Element* formIdElement = nullptr; |
1915 | 0 | if (aValue && !aValue->IsEmptyString()) { |
1916 | 0 | formIdElement = AddFormIdObserver(); |
1917 | 0 | } |
1918 | 0 |
|
1919 | 0 | // Because we have a new @form value (or no more @form), we have to |
1920 | 0 | // update our form owner. |
1921 | 0 | UpdateFormOwner(false, formIdElement); |
1922 | 0 | } |
1923 | 0 | } |
1924 | 0 | } |
1925 | 0 |
|
1926 | 0 | return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, |
1927 | 0 | aValue, aOldValue, |
1928 | 0 | aMaybeScriptedPrincipal, |
1929 | 0 | aNotify); |
1930 | 0 | } |
1931 | | |
1932 | | void |
1933 | | nsGenericHTMLFormElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) |
1934 | 0 | { |
1935 | 0 | if (aVisitor.mEvent->IsTrusted() && (aVisitor.mEvent->mMessage == eFocus || |
1936 | 0 | aVisitor.mEvent->mMessage == eBlur)) { |
1937 | 0 | // We have to handle focus/blur event to change focus states in |
1938 | 0 | // PreHandleEvent to prevent it breaks event target chain creation. |
1939 | 0 | aVisitor.mWantsPreHandleEvent = true; |
1940 | 0 | } |
1941 | 0 | nsGenericHTMLElement::GetEventTargetParent(aVisitor); |
1942 | 0 | } |
1943 | | |
1944 | | nsresult |
1945 | | nsGenericHTMLFormElement::PreHandleEvent(EventChainVisitor& aVisitor) |
1946 | 0 | { |
1947 | 0 | if (aVisitor.mEvent->IsTrusted()) { |
1948 | 0 | switch (aVisitor.mEvent->mMessage) { |
1949 | 0 | case eFocus: { |
1950 | 0 | // Check to see if focus has bubbled up from a form control's |
1951 | 0 | // child textfield or button. If that's the case, don't focus |
1952 | 0 | // this parent file control -- leave focus on the child. |
1953 | 0 | nsIFormControlFrame* formControlFrame = GetFormControlFrame(true); |
1954 | 0 | if (formControlFrame && |
1955 | 0 | aVisitor.mEvent->mOriginalTarget == static_cast<nsINode*>(this)) |
1956 | 0 | formControlFrame->SetFocus(true, true); |
1957 | 0 | break; |
1958 | 0 | } |
1959 | 0 | case eBlur: { |
1960 | 0 | nsIFormControlFrame* formControlFrame = GetFormControlFrame(true); |
1961 | 0 | if (formControlFrame) |
1962 | 0 | formControlFrame->SetFocus(false, false); |
1963 | 0 | break; |
1964 | 0 | } |
1965 | 0 | default: |
1966 | 0 | break; |
1967 | 0 | } |
1968 | 0 | } |
1969 | 0 | return nsGenericHTMLElement::PreHandleEvent(aVisitor); |
1970 | 0 | } |
1971 | | |
1972 | | void |
1973 | | nsGenericHTMLFormElement::ForgetFieldSet(nsIContent* aFieldset) |
1974 | 0 | { |
1975 | 0 | if (mFieldSet == aFieldset) { |
1976 | 0 | mFieldSet = nullptr; |
1977 | 0 | } |
1978 | 0 | } |
1979 | | |
1980 | | bool |
1981 | | nsGenericHTMLFormElement::CanBeDisabled() const |
1982 | 0 | { |
1983 | 0 | int32_t type = ControlType(); |
1984 | 0 | // It's easier to test the types that _cannot_ be disabled |
1985 | 0 | return |
1986 | 0 | type != NS_FORM_OBJECT && |
1987 | 0 | type != NS_FORM_OUTPUT; |
1988 | 0 | } |
1989 | | |
1990 | | bool |
1991 | | nsGenericHTMLFormElement::DoesReadOnlyApply() const |
1992 | 0 | { |
1993 | 0 | int32_t type = ControlType(); |
1994 | 0 | if (!(type & NS_FORM_INPUT_ELEMENT) && type != NS_FORM_TEXTAREA) { |
1995 | 0 | return false; |
1996 | 0 | } |
1997 | 0 | |
1998 | 0 | switch (type) |
1999 | 0 | { |
2000 | 0 | case NS_FORM_INPUT_HIDDEN: |
2001 | 0 | case NS_FORM_INPUT_BUTTON: |
2002 | 0 | case NS_FORM_INPUT_IMAGE: |
2003 | 0 | case NS_FORM_INPUT_RESET: |
2004 | 0 | case NS_FORM_INPUT_SUBMIT: |
2005 | 0 | case NS_FORM_INPUT_RADIO: |
2006 | 0 | case NS_FORM_INPUT_FILE: |
2007 | 0 | case NS_FORM_INPUT_CHECKBOX: |
2008 | 0 | case NS_FORM_INPUT_RANGE: |
2009 | 0 | case NS_FORM_INPUT_COLOR: |
2010 | 0 | return false; |
2011 | | #ifdef DEBUG |
2012 | | case NS_FORM_TEXTAREA: |
2013 | | case NS_FORM_INPUT_TEXT: |
2014 | | case NS_FORM_INPUT_PASSWORD: |
2015 | | case NS_FORM_INPUT_SEARCH: |
2016 | | case NS_FORM_INPUT_TEL: |
2017 | | case NS_FORM_INPUT_EMAIL: |
2018 | | case NS_FORM_INPUT_URL: |
2019 | | case NS_FORM_INPUT_NUMBER: |
2020 | | case NS_FORM_INPUT_DATE: |
2021 | | case NS_FORM_INPUT_TIME: |
2022 | | case NS_FORM_INPUT_MONTH: |
2023 | | case NS_FORM_INPUT_WEEK: |
2024 | | case NS_FORM_INPUT_DATETIME_LOCAL: |
2025 | | return true; |
2026 | | default: |
2027 | | MOZ_ASSERT_UNREACHABLE("Unexpected input type in DoesReadOnlyApply()"); |
2028 | | return true; |
2029 | | #else // DEBUG |
2030 | 0 | default: |
2031 | 0 | return true; |
2032 | 0 | #endif // DEBUG |
2033 | 0 | } |
2034 | 0 | } |
2035 | | |
2036 | | bool |
2037 | | nsGenericHTMLFormElement::IsHTMLFocusable(bool aWithMouse, |
2038 | | bool* aIsFocusable, |
2039 | | int32_t* aTabIndex) |
2040 | 0 | { |
2041 | 0 | if (nsGenericHTMLElement::IsHTMLFocusable(aWithMouse, aIsFocusable, aTabIndex)) { |
2042 | 0 | return true; |
2043 | 0 | } |
2044 | 0 | |
2045 | | #ifdef XP_MACOSX |
2046 | | *aIsFocusable = |
2047 | | (!aWithMouse || nsFocusManager::sMouseFocusesFormControl) && *aIsFocusable; |
2048 | | #endif |
2049 | 0 | return false; |
2050 | 0 | } |
2051 | | |
2052 | | EventStates |
2053 | | nsGenericHTMLFormElement::IntrinsicState() const |
2054 | 0 | { |
2055 | 0 | // If you add attribute-dependent states here, you need to add them them to |
2056 | 0 | // AfterSetAttr too. And add them to AfterSetAttr for all subclasses that |
2057 | 0 | // implement IntrinsicState() and are affected by that attribute. |
2058 | 0 | EventStates state = nsGenericHTMLElement::IntrinsicState(); |
2059 | 0 |
|
2060 | 0 | if (mForm && mForm->IsDefaultSubmitElement(this)) { |
2061 | 0 | NS_ASSERTION(IsSubmitControl(), |
2062 | 0 | "Default submit element that isn't a submit control."); |
2063 | 0 | // We are the default submit element (:default) |
2064 | 0 | state |= NS_EVENT_STATE_DEFAULT; |
2065 | 0 | } |
2066 | 0 |
|
2067 | 0 | // Make the text controls read-write |
2068 | 0 | if (!state.HasState(NS_EVENT_STATE_MOZ_READWRITE) && |
2069 | 0 | DoesReadOnlyApply()) { |
2070 | 0 | if (!GetBoolAttr(nsGkAtoms::readonly)) { |
2071 | 0 | state |= NS_EVENT_STATE_MOZ_READWRITE; |
2072 | 0 | state &= ~NS_EVENT_STATE_MOZ_READONLY; |
2073 | 0 | } |
2074 | 0 | } |
2075 | 0 |
|
2076 | 0 | return state; |
2077 | 0 | } |
2078 | | |
2079 | | nsGenericHTMLFormElement::FocusTristate |
2080 | | nsGenericHTMLFormElement::FocusState() |
2081 | 0 | { |
2082 | 0 | // We can't be focused if we aren't in a (composed) document |
2083 | 0 | nsIDocument* doc = GetComposedDoc(); |
2084 | 0 | if (!doc) |
2085 | 0 | return eUnfocusable; |
2086 | 0 | |
2087 | 0 | // first see if we are disabled or not. If disabled then do nothing. |
2088 | 0 | if (IsDisabled()) { |
2089 | 0 | return eUnfocusable; |
2090 | 0 | } |
2091 | 0 | |
2092 | 0 | // If the window is not active, do not allow the focus to bring the |
2093 | 0 | // window to the front. We update the focus controller, but do |
2094 | 0 | // nothing else. |
2095 | 0 | if (nsPIDOMWindowOuter* win = doc->GetWindow()) { |
2096 | 0 | nsCOMPtr<nsPIDOMWindowOuter> rootWindow = win->GetPrivateRoot(); |
2097 | 0 |
|
2098 | 0 | nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID); |
2099 | 0 | if (fm && rootWindow) { |
2100 | 0 | nsCOMPtr<mozIDOMWindowProxy> activeWindow; |
2101 | 0 | fm->GetActiveWindow(getter_AddRefs(activeWindow)); |
2102 | 0 | if (activeWindow == rootWindow) { |
2103 | 0 | return eActiveWindow; |
2104 | 0 | } |
2105 | 0 | } |
2106 | 0 | } |
2107 | 0 | |
2108 | 0 | return eInactiveWindow; |
2109 | 0 | } |
2110 | | |
2111 | | Element* |
2112 | | nsGenericHTMLFormElement::AddFormIdObserver() |
2113 | 0 | { |
2114 | 0 | nsAutoString formId; |
2115 | 0 | DocumentOrShadowRoot* docOrShadow = GetUncomposedDocOrConnectedShadowRoot(); |
2116 | 0 | GetAttr(kNameSpaceID_None, nsGkAtoms::form, formId); |
2117 | 0 | NS_ASSERTION(!formId.IsEmpty(), |
2118 | 0 | "@form value should not be the empty string!"); |
2119 | 0 | RefPtr<nsAtom> atom = NS_Atomize(formId); |
2120 | 0 |
|
2121 | 0 | return docOrShadow->AddIDTargetObserver(atom, FormIdUpdated, this, false); |
2122 | 0 | } |
2123 | | |
2124 | | void |
2125 | | nsGenericHTMLFormElement::RemoveFormIdObserver() |
2126 | 0 | { |
2127 | 0 | DocumentOrShadowRoot* docOrShadow = GetUncomposedDocOrConnectedShadowRoot(); |
2128 | 0 | if (!docOrShadow) { |
2129 | 0 | return; |
2130 | 0 | } |
2131 | 0 | |
2132 | 0 | nsAutoString formId; |
2133 | 0 | GetAttr(kNameSpaceID_None, nsGkAtoms::form, formId); |
2134 | 0 | NS_ASSERTION(!formId.IsEmpty(), |
2135 | 0 | "@form value should not be the empty string!"); |
2136 | 0 | RefPtr<nsAtom> atom = NS_Atomize(formId); |
2137 | 0 |
|
2138 | 0 | docOrShadow->RemoveIDTargetObserver(atom, FormIdUpdated, this, false); |
2139 | 0 | } |
2140 | | |
2141 | | |
2142 | | /* static */ |
2143 | | bool |
2144 | | nsGenericHTMLFormElement::FormIdUpdated(Element* aOldElement, |
2145 | | Element* aNewElement, |
2146 | | void* aData) |
2147 | 0 | { |
2148 | 0 | nsGenericHTMLFormElement* element = |
2149 | 0 | static_cast<nsGenericHTMLFormElement*>(aData); |
2150 | 0 |
|
2151 | 0 | NS_ASSERTION(element->IsHTMLElement(), "aData should be an HTML element"); |
2152 | 0 |
|
2153 | 0 | element->UpdateFormOwner(false, aNewElement); |
2154 | 0 |
|
2155 | 0 | return true; |
2156 | 0 | } |
2157 | | |
2158 | | bool |
2159 | | nsGenericHTMLFormElement::IsElementDisabledForEvents(EventMessage aMessage, |
2160 | | nsIFrame* aFrame) |
2161 | 0 | { |
2162 | 0 | switch (aMessage) { |
2163 | 0 | case eMouseMove: |
2164 | 0 | case eMouseOver: |
2165 | 0 | case eMouseOut: |
2166 | 0 | case eMouseEnter: |
2167 | 0 | case eMouseLeave: |
2168 | 0 | case ePointerMove: |
2169 | 0 | case ePointerOver: |
2170 | 0 | case ePointerOut: |
2171 | 0 | case ePointerEnter: |
2172 | 0 | case ePointerLeave: |
2173 | 0 | case eWheel: |
2174 | 0 | case eLegacyMouseLineOrPageScroll: |
2175 | 0 | case eLegacyMousePixelScroll: |
2176 | 0 | return false; |
2177 | 0 | default: |
2178 | 0 | break; |
2179 | 0 | } |
2180 | 0 | |
2181 | 0 | // FIXME(emilio): This poking at the style of the frame is slightly bogus |
2182 | 0 | // unless we flush before every event, which we don't really want to do. |
2183 | 0 | if (aFrame && aFrame->StyleUI()->mUserInput == StyleUserInput::None) { |
2184 | 0 | return true; |
2185 | 0 | } |
2186 | 0 | |
2187 | 0 | return IsDisabled(); |
2188 | 0 | } |
2189 | | |
2190 | | void |
2191 | | nsGenericHTMLFormElement::UpdateFormOwner(bool aBindToTree, |
2192 | | Element* aFormIdElement) |
2193 | 0 | { |
2194 | 0 | MOZ_ASSERT(!aBindToTree || !aFormIdElement, |
2195 | 0 | "aFormIdElement shouldn't be set if aBindToTree is true!"); |
2196 | 0 |
|
2197 | 0 | bool needStateUpdate = false; |
2198 | 0 | if (!aBindToTree) { |
2199 | 0 | needStateUpdate = mForm && mForm->IsDefaultSubmitElement(this); |
2200 | 0 | ClearForm(true, false); |
2201 | 0 | } |
2202 | 0 |
|
2203 | 0 | HTMLFormElement *oldForm = mForm; |
2204 | 0 |
|
2205 | 0 | if (!mForm) { |
2206 | 0 | // If @form is set, we have to use that to find the form. |
2207 | 0 | nsAutoString formId; |
2208 | 0 | if (GetAttr(kNameSpaceID_None, nsGkAtoms::form, formId)) { |
2209 | 0 | if (!formId.IsEmpty()) { |
2210 | 0 | Element* element = nullptr; |
2211 | 0 |
|
2212 | 0 | if (aBindToTree) { |
2213 | 0 | element = AddFormIdObserver(); |
2214 | 0 | } else { |
2215 | 0 | element = aFormIdElement; |
2216 | 0 | } |
2217 | 0 |
|
2218 | 0 | NS_ASSERTION(!IsInComposedDoc() || |
2219 | 0 | element == GetUncomposedDocOrConnectedShadowRoot()-> |
2220 | 0 | GetElementById(formId), |
2221 | 0 | "element should be equals to the current element " |
2222 | 0 | "associated with the id in @form!"); |
2223 | 0 |
|
2224 | 0 | if (element && |
2225 | 0 | element->IsHTMLElement(nsGkAtoms::form) && |
2226 | 0 | nsContentUtils::IsInSameAnonymousTree(this, element)) { |
2227 | 0 | SetForm(static_cast<HTMLFormElement*>(element), aBindToTree); |
2228 | 0 | } |
2229 | 0 | } |
2230 | 0 | } else { |
2231 | 0 | // We now have a parent, so we may have picked up an ancestor form. Search |
2232 | 0 | // for it. Note that if mForm is already set we don't want to do this, |
2233 | 0 | // because that means someone (probably the content sink) has already set |
2234 | 0 | // it to the right value. Also note that even if being bound here didn't |
2235 | 0 | // change our parent, we still need to search, since our parent chain |
2236 | 0 | // probably changed _somewhere_. |
2237 | 0 | SetForm(FindAncestorForm(), aBindToTree); |
2238 | 0 | } |
2239 | 0 | } |
2240 | 0 |
|
2241 | 0 | if (mForm && !HasFlag(ADDED_TO_FORM)) { |
2242 | 0 | // Now we need to add ourselves to the form |
2243 | 0 | nsAutoString nameVal, idVal; |
2244 | 0 | GetAttr(kNameSpaceID_None, nsGkAtoms::name, nameVal); |
2245 | 0 | GetAttr(kNameSpaceID_None, nsGkAtoms::id, idVal); |
2246 | 0 |
|
2247 | 0 | SetFlags(ADDED_TO_FORM); |
2248 | 0 |
|
2249 | 0 | // Notify only if we just found this mForm. |
2250 | 0 | mForm->AddElement(this, true, oldForm == nullptr); |
2251 | 0 |
|
2252 | 0 | if (!nameVal.IsEmpty()) { |
2253 | 0 | mForm->AddElementToTable(this, nameVal); |
2254 | 0 | } |
2255 | 0 |
|
2256 | 0 | if (!idVal.IsEmpty()) { |
2257 | 0 | mForm->AddElementToTable(this, idVal); |
2258 | 0 | } |
2259 | 0 | } |
2260 | 0 |
|
2261 | 0 | if (mForm != oldForm || needStateUpdate) { |
2262 | 0 | UpdateState(true); |
2263 | 0 | } |
2264 | 0 | } |
2265 | | |
2266 | | void |
2267 | | nsGenericHTMLFormElement::UpdateFieldSet(bool aNotify) |
2268 | 0 | { |
2269 | 0 | nsIContent* parent = nullptr; |
2270 | 0 | nsIContent* prev = nullptr; |
2271 | 0 |
|
2272 | 0 | // Don't walk out of anonymous subtrees. Note the similar code in |
2273 | 0 | // nsGenericHTMLElement::FindAncestorForm. |
2274 | 0 | nsIContent* bindingParent = GetBindingParent(); |
2275 | 0 |
|
2276 | 0 | for (parent = GetParent(); parent && parent != bindingParent; |
2277 | 0 | prev = parent, parent = parent->GetParent()) { |
2278 | 0 | HTMLFieldSetElement* fieldset = |
2279 | 0 | HTMLFieldSetElement::FromNode(parent); |
2280 | 0 | if (fieldset && |
2281 | 0 | (!prev || fieldset->GetFirstLegend() != prev)) { |
2282 | 0 | if (mFieldSet == fieldset) { |
2283 | 0 | // We already have the right fieldset; |
2284 | 0 | return; |
2285 | 0 | } |
2286 | 0 | |
2287 | 0 | if (mFieldSet) { |
2288 | 0 | mFieldSet->RemoveElement(this); |
2289 | 0 | } |
2290 | 0 | mFieldSet = fieldset; |
2291 | 0 | fieldset->AddElement(this); |
2292 | 0 |
|
2293 | 0 | // The disabled state may have changed |
2294 | 0 | FieldSetDisabledChanged(aNotify); |
2295 | 0 | return; |
2296 | 0 | } |
2297 | 0 | } |
2298 | 0 |
|
2299 | 0 | // No fieldset found. |
2300 | 0 | if (mFieldSet) { |
2301 | 0 | mFieldSet->RemoveElement(this); |
2302 | 0 | mFieldSet = nullptr; |
2303 | 0 | // The disabled state may have changed |
2304 | 0 | FieldSetDisabledChanged(aNotify); |
2305 | 0 | } |
2306 | 0 | } |
2307 | | |
2308 | | void |
2309 | | nsGenericHTMLFormElement::UpdateDisabledState(bool aNotify) |
2310 | 0 | { |
2311 | 0 | if (!CanBeDisabled()) { |
2312 | 0 | return; |
2313 | 0 | } |
2314 | 0 | |
2315 | 0 | bool isDisabled = HasAttr(kNameSpaceID_None, nsGkAtoms::disabled); |
2316 | 0 | if (!isDisabled && mFieldSet) { |
2317 | 0 | isDisabled = mFieldSet->IsDisabled(); |
2318 | 0 | } |
2319 | 0 |
|
2320 | 0 | EventStates disabledStates; |
2321 | 0 | if (isDisabled) { |
2322 | 0 | disabledStates |= NS_EVENT_STATE_DISABLED; |
2323 | 0 | } else { |
2324 | 0 | disabledStates |= NS_EVENT_STATE_ENABLED; |
2325 | 0 | } |
2326 | 0 |
|
2327 | 0 | EventStates oldDisabledStates = State() & DISABLED_STATES; |
2328 | 0 | EventStates changedStates = disabledStates ^ oldDisabledStates; |
2329 | 0 |
|
2330 | 0 | if (!changedStates.IsEmpty()) { |
2331 | 0 | ToggleStates(changedStates, aNotify); |
2332 | 0 | } |
2333 | 0 | } |
2334 | | |
2335 | | void |
2336 | | nsGenericHTMLFormElement::UpdateRequiredState(bool aIsRequired, bool aNotify) |
2337 | 0 | { |
2338 | | #ifdef DEBUG |
2339 | | int32_t type = ControlType(); |
2340 | | #endif |
2341 | 0 | MOZ_ASSERT((type & NS_FORM_INPUT_ELEMENT) || |
2342 | 0 | type == NS_FORM_SELECT || |
2343 | 0 | type == NS_FORM_TEXTAREA, |
2344 | 0 | "This should be called only on types that @required applies"); |
2345 | 0 |
|
2346 | | #ifdef DEBUG |
2347 | | HTMLInputElement* input = HTMLInputElement::FromNode(this); |
2348 | | if (input) { |
2349 | | MOZ_ASSERT(input->DoesRequiredApply(), |
2350 | | "This should be called only on input types that @required applies"); |
2351 | | } |
2352 | | #endif |
2353 | |
|
2354 | 0 | EventStates requiredStates; |
2355 | 0 | if (aIsRequired) { |
2356 | 0 | requiredStates |= NS_EVENT_STATE_REQUIRED; |
2357 | 0 | } else { |
2358 | 0 | requiredStates |= NS_EVENT_STATE_OPTIONAL; |
2359 | 0 | } |
2360 | 0 |
|
2361 | 0 | EventStates oldRequiredStates = State() & REQUIRED_STATES; |
2362 | 0 | EventStates changedStates = requiredStates ^ oldRequiredStates; |
2363 | 0 |
|
2364 | 0 | if (!changedStates.IsEmpty()) { |
2365 | 0 | ToggleStates(changedStates, aNotify); |
2366 | 0 | } |
2367 | 0 | } |
2368 | | |
2369 | | void |
2370 | | nsGenericHTMLFormElement::FieldSetDisabledChanged(bool aNotify) |
2371 | 0 | { |
2372 | 0 | UpdateDisabledState(aNotify); |
2373 | 0 | } |
2374 | | |
2375 | | bool |
2376 | | nsGenericHTMLFormElement::IsLabelable() const |
2377 | 0 | { |
2378 | 0 | // TODO: keygen should be in that list, see bug 101019. |
2379 | 0 | uint32_t type = ControlType(); |
2380 | 0 | return (type & NS_FORM_INPUT_ELEMENT && type != NS_FORM_INPUT_HIDDEN) || |
2381 | 0 | type & NS_FORM_BUTTON_ELEMENT || |
2382 | 0 | // type == NS_FORM_KEYGEN || |
2383 | 0 | type == NS_FORM_OUTPUT || |
2384 | 0 | type == NS_FORM_SELECT || |
2385 | 0 | type == NS_FORM_TEXTAREA; |
2386 | 0 | } |
2387 | | |
2388 | | void |
2389 | | nsGenericHTMLFormElement::GetFormAction(nsString& aValue) |
2390 | 0 | { |
2391 | 0 | uint32_t type = ControlType(); |
2392 | 0 | if (!(type & NS_FORM_INPUT_ELEMENT) && !(type & NS_FORM_BUTTON_ELEMENT)) { |
2393 | 0 | return; |
2394 | 0 | } |
2395 | 0 | |
2396 | 0 | if (!GetAttr(kNameSpaceID_None, nsGkAtoms::formaction, aValue) || |
2397 | 0 | aValue.IsEmpty()) { |
2398 | 0 | nsIDocument* document = OwnerDoc(); |
2399 | 0 | nsIURI* docURI = document->GetDocumentURI(); |
2400 | 0 | if (docURI) { |
2401 | 0 | nsAutoCString spec; |
2402 | 0 | nsresult rv = docURI->GetSpec(spec); |
2403 | 0 | if (NS_FAILED(rv)) { |
2404 | 0 | return; |
2405 | 0 | } |
2406 | 0 | |
2407 | 0 | CopyUTF8toUTF16(spec, aValue); |
2408 | 0 | } |
2409 | 0 | } else { |
2410 | 0 | GetURIAttr(nsGkAtoms::formaction, nullptr, aValue); |
2411 | 0 | } |
2412 | 0 | } |
2413 | | |
2414 | | //---------------------------------------------------------------------- |
2415 | | |
2416 | | void |
2417 | | nsGenericHTMLElement::Click(CallerType aCallerType) |
2418 | 0 | { |
2419 | 0 | if (HandlingClick()) |
2420 | 0 | return; |
2421 | 0 | |
2422 | 0 | // Strong in case the event kills it |
2423 | 0 | nsCOMPtr<nsIDocument> doc = GetComposedDoc(); |
2424 | 0 |
|
2425 | 0 | RefPtr<nsPresContext> context; |
2426 | 0 | if (doc) { |
2427 | 0 | context = doc->GetPresContext(); |
2428 | 0 | } |
2429 | 0 |
|
2430 | 0 | SetHandlingClick(); |
2431 | 0 |
|
2432 | 0 | // Mark this event trusted if Click() is called from system code. |
2433 | 0 | WidgetMouseEvent event(aCallerType == CallerType::System, |
2434 | 0 | eMouseClick, nullptr, WidgetMouseEvent::eReal); |
2435 | 0 | event.mFlags.mIsPositionless = true; |
2436 | 0 | event.inputSource = MouseEvent_Binding::MOZ_SOURCE_UNKNOWN; |
2437 | 0 |
|
2438 | 0 | EventDispatcher::Dispatch(static_cast<nsIContent*>(this), context, &event); |
2439 | 0 |
|
2440 | 0 | ClearHandlingClick(); |
2441 | 0 | } |
2442 | | |
2443 | | bool |
2444 | | nsGenericHTMLElement::IsHTMLFocusable(bool aWithMouse, |
2445 | | bool *aIsFocusable, |
2446 | | int32_t *aTabIndex) |
2447 | 0 | { |
2448 | 0 | nsIDocument* doc = GetComposedDoc(); |
2449 | 0 | if (!doc || doc->HasFlag(NODE_IS_EDITABLE)) { |
2450 | 0 | // In designMode documents we only allow focusing the document. |
2451 | 0 | if (aTabIndex) { |
2452 | 0 | *aTabIndex = -1; |
2453 | 0 | } |
2454 | 0 |
|
2455 | 0 | *aIsFocusable = false; |
2456 | 0 |
|
2457 | 0 | return true; |
2458 | 0 | } |
2459 | 0 |
|
2460 | 0 | int32_t tabIndex = TabIndex(); |
2461 | 0 | bool disabled = false; |
2462 | 0 | bool disallowOverridingFocusability = true; |
2463 | 0 |
|
2464 | 0 | if (IsEditableRoot()) { |
2465 | 0 | // Editable roots should always be focusable. |
2466 | 0 | disallowOverridingFocusability = true; |
2467 | 0 |
|
2468 | 0 | // Ignore the disabled attribute in editable contentEditable/designMode |
2469 | 0 | // roots. |
2470 | 0 | if (!HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) { |
2471 | 0 | // The default value for tabindex should be 0 for editable |
2472 | 0 | // contentEditable roots. |
2473 | 0 | tabIndex = 0; |
2474 | 0 | } |
2475 | 0 | } |
2476 | 0 | else { |
2477 | 0 | disallowOverridingFocusability = false; |
2478 | 0 |
|
2479 | 0 | // Just check for disabled attribute on form controls |
2480 | 0 | disabled = IsDisabled(); |
2481 | 0 | if (disabled) { |
2482 | 0 | tabIndex = -1; |
2483 | 0 | } |
2484 | 0 | } |
2485 | 0 |
|
2486 | 0 | if (aTabIndex) { |
2487 | 0 | *aTabIndex = tabIndex; |
2488 | 0 | } |
2489 | 0 |
|
2490 | 0 | // If a tabindex is specified at all, or the default tabindex is 0, we're focusable |
2491 | 0 | *aIsFocusable = |
2492 | 0 | (tabIndex >= 0 || (!disabled && HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex))); |
2493 | 0 |
|
2494 | 0 | return disallowOverridingFocusability; |
2495 | 0 | } |
2496 | | |
2497 | | void |
2498 | | nsGenericHTMLElement::RegUnRegAccessKey(bool aDoReg) |
2499 | 0 | { |
2500 | 0 | // first check to see if we have an access key |
2501 | 0 | nsAutoString accessKey; |
2502 | 0 | GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, accessKey); |
2503 | 0 | if (accessKey.IsEmpty()) { |
2504 | 0 | return; |
2505 | 0 | } |
2506 | 0 | |
2507 | 0 | // We have an access key, so get the ESM from the pres context. |
2508 | 0 | nsPresContext* presContext = GetPresContext(eForUncomposedDoc); |
2509 | 0 |
|
2510 | 0 | if (presContext) { |
2511 | 0 | EventStateManager* esm = presContext->EventStateManager(); |
2512 | 0 |
|
2513 | 0 | // Register or unregister as appropriate. |
2514 | 0 | if (aDoReg) { |
2515 | 0 | esm->RegisterAccessKey(this, (uint32_t)accessKey.First()); |
2516 | 0 | } else { |
2517 | 0 | esm->UnregisterAccessKey(this, (uint32_t)accessKey.First()); |
2518 | 0 | } |
2519 | 0 | } |
2520 | 0 | } |
2521 | | |
2522 | | bool |
2523 | | nsGenericHTMLElement::PerformAccesskey(bool aKeyCausesActivation, |
2524 | | bool aIsTrustedEvent) |
2525 | 0 | { |
2526 | 0 | nsPresContext* presContext = GetPresContext(eForUncomposedDoc); |
2527 | 0 | if (!presContext) { |
2528 | 0 | return false; |
2529 | 0 | } |
2530 | 0 | |
2531 | 0 | // It's hard to say what HTML4 wants us to do in all cases. |
2532 | 0 | bool focused = true; |
2533 | 0 | nsFocusManager* fm = nsFocusManager::GetFocusManager(); |
2534 | 0 | if (fm) { |
2535 | 0 | fm->SetFocus(this, nsIFocusManager::FLAG_BYKEY); |
2536 | 0 |
|
2537 | 0 | // Return true if the element became the current focus within its window. |
2538 | 0 | nsPIDOMWindowOuter* window = OwnerDoc()->GetWindow(); |
2539 | 0 | focused = (window && window->GetFocusedElement()); |
2540 | 0 | } |
2541 | 0 |
|
2542 | 0 | if (aKeyCausesActivation) { |
2543 | 0 | // Click on it if the users prefs indicate to do so. |
2544 | 0 | nsAutoPopupStatePusher popupStatePusher(aIsTrustedEvent ? |
2545 | 0 | openAllowed : openAbused); |
2546 | 0 | DispatchSimulatedClick(this, aIsTrustedEvent, presContext); |
2547 | 0 | } |
2548 | 0 |
|
2549 | 0 | return focused; |
2550 | 0 | } |
2551 | | |
2552 | | nsresult |
2553 | | nsGenericHTMLElement::DispatchSimulatedClick(nsGenericHTMLElement* aElement, |
2554 | | bool aIsTrusted, |
2555 | | nsPresContext* aPresContext) |
2556 | 0 | { |
2557 | 0 | WidgetMouseEvent event(aIsTrusted, eMouseClick, nullptr, |
2558 | 0 | WidgetMouseEvent::eReal); |
2559 | 0 | event.inputSource = MouseEvent_Binding::MOZ_SOURCE_KEYBOARD; |
2560 | 0 | event.mFlags.mIsPositionless = true; |
2561 | 0 | return EventDispatcher::Dispatch(ToSupports(aElement), aPresContext, &event); |
2562 | 0 | } |
2563 | | |
2564 | | already_AddRefed<TextEditor> |
2565 | | nsGenericHTMLElement::GetAssociatedEditor() |
2566 | 0 | { |
2567 | 0 | // If contenteditable is ever implemented, it might need to do something different here? |
2568 | 0 |
|
2569 | 0 | RefPtr<TextEditor> textEditor = GetTextEditorInternal(); |
2570 | 0 | return textEditor.forget(); |
2571 | 0 | } |
2572 | | |
2573 | | // static |
2574 | | void |
2575 | | nsGenericHTMLElement::SyncEditorsOnSubtree(nsIContent* content) |
2576 | 0 | { |
2577 | 0 | /* Sync this node */ |
2578 | 0 | nsGenericHTMLElement* element = FromNode(content); |
2579 | 0 | if (element) { |
2580 | 0 | RefPtr<TextEditor> textEditor = element->GetAssociatedEditor(); |
2581 | 0 | if (textEditor) { |
2582 | 0 | textEditor->SyncRealTimeSpell(); |
2583 | 0 | } |
2584 | 0 | } |
2585 | 0 |
|
2586 | 0 | /* Sync all children */ |
2587 | 0 | for (nsIContent* child = content->GetFirstChild(); |
2588 | 0 | child; |
2589 | 0 | child = child->GetNextSibling()) { |
2590 | 0 | SyncEditorsOnSubtree(child); |
2591 | 0 | } |
2592 | 0 | } |
2593 | | |
2594 | | void |
2595 | | nsGenericHTMLElement::RecompileScriptEventListeners() |
2596 | 0 | { |
2597 | 0 | int32_t i, count = mAttrs.AttrCount(); |
2598 | 0 | for (i = 0; i < count; ++i) { |
2599 | 0 | const nsAttrName *name = mAttrs.AttrNameAt(i); |
2600 | 0 |
|
2601 | 0 | // Eventlistenener-attributes are always in the null namespace |
2602 | 0 | if (!name->IsAtom()) { |
2603 | 0 | continue; |
2604 | 0 | } |
2605 | 0 | |
2606 | 0 | nsAtom *attr = name->Atom(); |
2607 | 0 | if (!IsEventAttributeName(attr)) { |
2608 | 0 | continue; |
2609 | 0 | } |
2610 | 0 | |
2611 | 0 | nsAutoString value; |
2612 | 0 | GetAttr(kNameSpaceID_None, attr, value); |
2613 | 0 | SetEventHandler(attr, value, true); |
2614 | 0 | } |
2615 | 0 | } |
2616 | | |
2617 | | bool |
2618 | | nsGenericHTMLElement::IsEditableRoot() const |
2619 | 0 | { |
2620 | 0 | nsIDocument *document = GetComposedDoc(); |
2621 | 0 | if (!document) { |
2622 | 0 | return false; |
2623 | 0 | } |
2624 | 0 | |
2625 | 0 | if (document->HasFlag(NODE_IS_EDITABLE)) { |
2626 | 0 | return false; |
2627 | 0 | } |
2628 | 0 | |
2629 | 0 | if (GetContentEditableValue() != eTrue) { |
2630 | 0 | return false; |
2631 | 0 | } |
2632 | 0 | |
2633 | 0 | nsIContent *parent = GetParent(); |
2634 | 0 |
|
2635 | 0 | return !parent || !parent->HasFlag(NODE_IS_EDITABLE); |
2636 | 0 | } |
2637 | | |
2638 | | static void |
2639 | | MakeContentDescendantsEditable(nsIContent *aContent, nsIDocument *aDocument) |
2640 | 0 | { |
2641 | 0 | // If aContent is not an element, we just need to update its |
2642 | 0 | // internal editable state and don't need to notify anyone about |
2643 | 0 | // that. For elements, we need to send a ContentStateChanged |
2644 | 0 | // notification. |
2645 | 0 | if (!aContent->IsElement()) { |
2646 | 0 | aContent->UpdateEditableState(false); |
2647 | 0 | return; |
2648 | 0 | } |
2649 | 0 | |
2650 | 0 | Element *element = aContent->AsElement(); |
2651 | 0 |
|
2652 | 0 | element->UpdateEditableState(true); |
2653 | 0 |
|
2654 | 0 | for (nsIContent *child = aContent->GetFirstChild(); |
2655 | 0 | child; |
2656 | 0 | child = child->GetNextSibling()) { |
2657 | 0 | if (!child->IsElement() || |
2658 | 0 | !child->AsElement()->HasAttr(kNameSpaceID_None, |
2659 | 0 | nsGkAtoms::contenteditable)) { |
2660 | 0 | MakeContentDescendantsEditable(child, aDocument); |
2661 | 0 | } |
2662 | 0 | } |
2663 | 0 | } |
2664 | | |
2665 | | void |
2666 | | nsGenericHTMLElement::ChangeEditableState(int32_t aChange) |
2667 | 0 | { |
2668 | 0 | nsIDocument* document = GetComposedDoc(); |
2669 | 0 | if (!document) { |
2670 | 0 | return; |
2671 | 0 | } |
2672 | 0 | |
2673 | 0 | if (aChange != 0) { |
2674 | 0 | nsCOMPtr<nsIHTMLDocument> htmlDocument = |
2675 | 0 | do_QueryInterface(document); |
2676 | 0 | if (htmlDocument) { |
2677 | 0 | htmlDocument->ChangeContentEditableCount(this, aChange); |
2678 | 0 | } |
2679 | 0 |
|
2680 | 0 | nsIContent* parent = GetParent(); |
2681 | 0 | // Don't update across Shadow DOM boundary. |
2682 | 0 | while (parent && parent->IsElement()) { |
2683 | 0 | parent->ChangeEditableDescendantCount(aChange); |
2684 | 0 | parent = parent->GetParent(); |
2685 | 0 | } |
2686 | 0 | } |
2687 | 0 |
|
2688 | 0 | if (document->HasFlag(NODE_IS_EDITABLE)) { |
2689 | 0 | document = nullptr; |
2690 | 0 | } |
2691 | 0 |
|
2692 | 0 | // MakeContentDescendantsEditable is going to call ContentStateChanged for |
2693 | 0 | // this element and all descendants if editable state has changed. |
2694 | 0 | // We might as well wrap it all in one script blocker. |
2695 | 0 | nsAutoScriptBlocker scriptBlocker; |
2696 | 0 | MakeContentDescendantsEditable(this, document); |
2697 | 0 | } |
2698 | | |
2699 | | |
2700 | | //---------------------------------------------------------------------- |
2701 | | |
2702 | | nsGenericHTMLFormElementWithState::nsGenericHTMLFormElementWithState( |
2703 | | already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, uint8_t aType |
2704 | | ) |
2705 | | : nsGenericHTMLFormElement(std::move(aNodeInfo), aType) |
2706 | 0 | { |
2707 | 0 | mStateKey.SetIsVoid(true); |
2708 | 0 | } |
2709 | | |
2710 | | nsresult |
2711 | | nsGenericHTMLFormElementWithState::GenerateStateKey() |
2712 | 0 | { |
2713 | 0 | // Keep the key if already computed |
2714 | 0 | if (!mStateKey.IsVoid()) { |
2715 | 0 | return NS_OK; |
2716 | 0 | } |
2717 | 0 | |
2718 | 0 | nsIDocument* doc = GetUncomposedDoc(); |
2719 | 0 | if (!doc) { |
2720 | 0 | return NS_OK; |
2721 | 0 | } |
2722 | 0 | |
2723 | 0 | // Generate the state key |
2724 | 0 | nsresult rv = nsContentUtils::GenerateStateKey(this, doc, mStateKey); |
2725 | 0 |
|
2726 | 0 | if (NS_FAILED(rv)) { |
2727 | 0 | mStateKey.SetIsVoid(true); |
2728 | 0 | return rv; |
2729 | 0 | } |
2730 | 0 | |
2731 | 0 | // If the state key is blank, this is anonymous content or for whatever |
2732 | 0 | // reason we are not supposed to save/restore state: keep it as such. |
2733 | 0 | if (!mStateKey.IsEmpty()) { |
2734 | 0 | // Add something unique to content so layout doesn't muck us up. |
2735 | 0 | mStateKey += "-C"; |
2736 | 0 | } |
2737 | 0 | return NS_OK; |
2738 | 0 | } |
2739 | | |
2740 | | PresState* |
2741 | | nsGenericHTMLFormElementWithState::GetPrimaryPresState() |
2742 | 0 | { |
2743 | 0 | if (mStateKey.IsEmpty()) { |
2744 | 0 | return nullptr; |
2745 | 0 | } |
2746 | 0 | |
2747 | 0 | nsCOMPtr<nsILayoutHistoryState> history = GetLayoutHistory(false); |
2748 | 0 |
|
2749 | 0 | if (!history) { |
2750 | 0 | return nullptr; |
2751 | 0 | } |
2752 | 0 | |
2753 | 0 | // Get the pres state for this key, if it doesn't exist, create one. |
2754 | 0 | PresState* result = history->GetState(mStateKey); |
2755 | 0 | if (!result) { |
2756 | 0 | UniquePtr<PresState> newState = NewPresState(); |
2757 | 0 | result = newState.get(); |
2758 | 0 | history->AddState(mStateKey, std::move(newState)); |
2759 | 0 | } |
2760 | 0 |
|
2761 | 0 | return result; |
2762 | 0 | } |
2763 | | |
2764 | | already_AddRefed<nsILayoutHistoryState> |
2765 | | nsGenericHTMLFormElementWithState::GetLayoutHistory(bool aRead) |
2766 | 0 | { |
2767 | 0 | nsCOMPtr<nsIDocument> doc = GetUncomposedDoc(); |
2768 | 0 | if (!doc) { |
2769 | 0 | return nullptr; |
2770 | 0 | } |
2771 | 0 | |
2772 | 0 | // |
2773 | 0 | // Get the history |
2774 | 0 | // |
2775 | 0 | nsCOMPtr<nsILayoutHistoryState> history = doc->GetLayoutHistoryState(); |
2776 | 0 | if (!history) { |
2777 | 0 | return nullptr; |
2778 | 0 | } |
2779 | 0 | |
2780 | 0 | if (aRead && !history->HasStates()) { |
2781 | 0 | return nullptr; |
2782 | 0 | } |
2783 | 0 | |
2784 | 0 | return history.forget(); |
2785 | 0 | } |
2786 | | |
2787 | | bool |
2788 | | nsGenericHTMLFormElementWithState::RestoreFormControlState() |
2789 | 0 | { |
2790 | 0 | if (mStateKey.IsEmpty()) { |
2791 | 0 | return false; |
2792 | 0 | } |
2793 | 0 | |
2794 | 0 | nsCOMPtr<nsILayoutHistoryState> history = |
2795 | 0 | GetLayoutHistory(true); |
2796 | 0 | if (!history) { |
2797 | 0 | return false; |
2798 | 0 | } |
2799 | 0 | |
2800 | 0 | // Get the pres state for this key |
2801 | 0 | PresState* state = history->GetState(mStateKey); |
2802 | 0 | if (state) { |
2803 | 0 | bool result = RestoreState(state); |
2804 | 0 | history->RemoveState(mStateKey); |
2805 | 0 | return result; |
2806 | 0 | } |
2807 | 0 | |
2808 | 0 | return false; |
2809 | 0 | } |
2810 | | |
2811 | | void |
2812 | | nsGenericHTMLFormElementWithState::NodeInfoChanged(nsIDocument* aOldDoc) |
2813 | 0 | { |
2814 | 0 | nsGenericHTMLElement::NodeInfoChanged(aOldDoc); |
2815 | 0 | mStateKey.SetIsVoid(true); |
2816 | 0 | } |
2817 | | |
2818 | | nsSize |
2819 | | nsGenericHTMLElement::GetWidthHeightForImage(RefPtr<imgRequestProxy>& aImageRequest) |
2820 | 0 | { |
2821 | 0 | nsSize size(0,0); |
2822 | 0 |
|
2823 | 0 | nsIFrame* frame = GetPrimaryFrame(FlushType::Layout); |
2824 | 0 |
|
2825 | 0 | if (frame) { |
2826 | 0 | size = frame->GetContentRect().Size(); |
2827 | 0 |
|
2828 | 0 | size.width = nsPresContext::AppUnitsToIntCSSPixels(size.width); |
2829 | 0 | size.height = nsPresContext::AppUnitsToIntCSSPixels(size.height); |
2830 | 0 | } else { |
2831 | 0 | const nsAttrValue* value; |
2832 | 0 | nsCOMPtr<imgIContainer> image; |
2833 | 0 | if (aImageRequest) { |
2834 | 0 | aImageRequest->GetImage(getter_AddRefs(image)); |
2835 | 0 | } |
2836 | 0 |
|
2837 | 0 | if ((value = GetParsedAttr(nsGkAtoms::width)) && |
2838 | 0 | value->Type() == nsAttrValue::eInteger) { |
2839 | 0 | size.width = value->GetIntegerValue(); |
2840 | 0 | } else if (image) { |
2841 | 0 | image->GetWidth(&size.width); |
2842 | 0 | } |
2843 | 0 |
|
2844 | 0 | if ((value = GetParsedAttr(nsGkAtoms::height)) && |
2845 | 0 | value->Type() == nsAttrValue::eInteger) { |
2846 | 0 | size.height = value->GetIntegerValue(); |
2847 | 0 | } else if (image) { |
2848 | 0 | image->GetHeight(&size.height); |
2849 | 0 | } |
2850 | 0 | } |
2851 | 0 |
|
2852 | 0 | NS_ASSERTION(size.width >= 0, "negative width"); |
2853 | 0 | NS_ASSERTION(size.height >= 0, "negative height"); |
2854 | 0 | return size; |
2855 | 0 | } |
2856 | | |
2857 | | bool |
2858 | | nsGenericHTMLElement::IsEventAttributeNameInternal(nsAtom *aName) |
2859 | 0 | { |
2860 | 0 | return nsContentUtils::IsEventAttributeName(aName, EventNameType_HTML); |
2861 | 0 | } |
2862 | | |
2863 | | /** |
2864 | | * Construct a URI from a string, as an element.src attribute |
2865 | | * would be set to. Helper for the media elements. |
2866 | | */ |
2867 | | nsresult |
2868 | | nsGenericHTMLElement::NewURIFromString(const nsAString& aURISpec, |
2869 | | nsIURI** aURI) |
2870 | 0 | { |
2871 | 0 | NS_ENSURE_ARG_POINTER(aURI); |
2872 | 0 |
|
2873 | 0 | *aURI = nullptr; |
2874 | 0 |
|
2875 | 0 | nsCOMPtr<nsIDocument> doc = OwnerDoc(); |
2876 | 0 |
|
2877 | 0 | nsCOMPtr<nsIURI> baseURI = GetBaseURI(); |
2878 | 0 | nsresult rv = nsContentUtils::NewURIWithDocumentCharset(aURI, aURISpec, |
2879 | 0 | doc, baseURI); |
2880 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2881 | 0 |
|
2882 | 0 | bool equal; |
2883 | 0 | if (aURISpec.IsEmpty() && |
2884 | 0 | doc->GetDocumentURI() && |
2885 | 0 | NS_SUCCEEDED(doc->GetDocumentURI()->Equals(*aURI, &equal)) && |
2886 | 0 | equal) { |
2887 | 0 | // Assume an element can't point to a fragment of its embedding |
2888 | 0 | // document. Fail here instead of returning the recursive URI |
2889 | 0 | // and waiting for the subsequent load to fail. |
2890 | 0 | NS_RELEASE(*aURI); |
2891 | 0 | return NS_ERROR_DOM_INVALID_STATE_ERR; |
2892 | 0 | } |
2893 | 0 |
|
2894 | 0 | return NS_OK; |
2895 | 0 | } |
2896 | | |
2897 | | // https://html.spec.whatwg.org/#being-rendered |
2898 | | // |
2899 | | // With a gotcha for display contents: |
2900 | | // https://github.com/whatwg/html/issues/3947 |
2901 | | static bool IsRendered(const Element& aElement) |
2902 | 0 | { |
2903 | 0 | return aElement.GetPrimaryFrame() || aElement.IsDisplayContents(); |
2904 | 0 | } |
2905 | | |
2906 | | void |
2907 | | nsGenericHTMLElement::GetInnerText(mozilla::dom::DOMString& aValue, |
2908 | | mozilla::ErrorResult& aError) |
2909 | 0 | { |
2910 | 0 | // innerText depends on layout. For example, white space processing is |
2911 | 0 | // something that happens during reflow and which must be reflected by |
2912 | 0 | // innerText. So for: |
2913 | 0 | // |
2914 | 0 | // <div style="white-space:normal"> A B C </div> |
2915 | 0 | // |
2916 | 0 | // innerText should give "A B C". |
2917 | 0 | // |
2918 | 0 | // The approach taken here to avoid the expense of reflow is to flush style |
2919 | 0 | // and then see whether it's necessary to flush layout afterwards. Flushing |
2920 | 0 | // layout can be skipped if we can detect that the element or its descendants |
2921 | 0 | // are not dirty. |
2922 | 0 |
|
2923 | 0 | // Obtain the composed doc to handle elements in Shadow DOM. |
2924 | 0 | nsIDocument* doc = GetComposedDoc(); |
2925 | 0 | if (doc) { |
2926 | 0 | doc->FlushPendingNotifications(FlushType::Style); |
2927 | 0 | } |
2928 | 0 |
|
2929 | 0 | // Elements with `display: content` will not have a frame. To handle Shadow |
2930 | 0 | // DOM, walk the flattened tree looking for parent frame. |
2931 | 0 | nsIFrame* frame = GetPrimaryFrame(); |
2932 | 0 | if (IsDisplayContents()) { |
2933 | 0 | for (Element* parent = GetFlattenedTreeParentElement(); |
2934 | 0 | parent; |
2935 | 0 | parent = parent->GetFlattenedTreeParentElement()) |
2936 | 0 | { |
2937 | 0 | frame = parent->GetPrimaryFrame(); |
2938 | 0 | if (frame) { |
2939 | 0 | break; |
2940 | 0 | } |
2941 | 0 | } |
2942 | 0 | } |
2943 | 0 |
|
2944 | 0 | // Check for dirty reflow roots in the subtree from targetFrame; this requires |
2945 | 0 | // a reflow flush. |
2946 | 0 | bool dirty = frame && frame->PresShell()->FrameIsAncestorOfDirtyRoot(frame); |
2947 | 0 |
|
2948 | 0 | // The way we do that is by checking whether the element has either of the two |
2949 | 0 | // dirty bits (NS_FRAME_IS_DIRTY or NS_FRAME_HAS_DIRTY_DESCENDANTS) or if any |
2950 | 0 | // ancestor has NS_FRAME_IS_DIRTY. We need to check for NS_FRAME_IS_DIRTY on |
2951 | 0 | // ancestors since that is something that implies NS_FRAME_IS_DIRTY on all |
2952 | 0 | // descendants. |
2953 | 0 | dirty |= frame && frame->HasAnyStateBits(NS_FRAME_HAS_DIRTY_CHILDREN); |
2954 | 0 | while (!dirty && frame) { |
2955 | 0 | dirty |= frame->HasAnyStateBits(NS_FRAME_IS_DIRTY); |
2956 | 0 | frame = frame->GetInFlowParent(); |
2957 | 0 | } |
2958 | 0 |
|
2959 | 0 | // Flush layout if we determined a reflow is required. |
2960 | 0 | if (dirty && doc) { |
2961 | 0 | doc->FlushPendingNotifications(FlushType::Layout); |
2962 | 0 | } |
2963 | 0 |
|
2964 | 0 | if (!IsRendered(*this)) { |
2965 | 0 | GetTextContentInternal(aValue, aError); |
2966 | 0 | } else { |
2967 | 0 | nsRange::GetInnerTextNoFlush(aValue, aError, this); |
2968 | 0 | } |
2969 | 0 | } |
2970 | | |
2971 | | void |
2972 | | nsGenericHTMLElement::SetInnerText(const nsAString& aValue) |
2973 | 0 | { |
2974 | 0 | // Batch possible DOMSubtreeModified events. |
2975 | 0 | mozAutoSubtreeModified subtree(OwnerDoc(), nullptr); |
2976 | 0 | FireNodeRemovedForChildren(); |
2977 | 0 |
|
2978 | 0 | // Might as well stick a batch around this since we're performing several |
2979 | 0 | // mutations. |
2980 | 0 | mozAutoDocUpdate updateBatch(GetComposedDoc(), true); |
2981 | 0 | nsAutoMutationBatch mb; |
2982 | 0 |
|
2983 | 0 | mb.Init(this, true, false); |
2984 | 0 |
|
2985 | 0 | while (HasChildren()) { |
2986 | 0 | RemoveChildNode(nsINode::GetFirstChild(), true); |
2987 | 0 | } |
2988 | 0 |
|
2989 | 0 | mb.RemovalDone(); |
2990 | 0 |
|
2991 | 0 | nsString str; |
2992 | 0 | const char16_t* s = aValue.BeginReading(); |
2993 | 0 | const char16_t* end = aValue.EndReading(); |
2994 | 0 | while (true) { |
2995 | 0 | if (s != end && *s == '\r' && s + 1 != end && s[1] == '\n') { |
2996 | 0 | // a \r\n pair should only generate one <br>, so just skip the \r |
2997 | 0 | ++s; |
2998 | 0 | } |
2999 | 0 | if (s == end || *s == '\r' || *s == '\n') { |
3000 | 0 | if (!str.IsEmpty()) { |
3001 | 0 | RefPtr<nsTextNode> textContent = |
3002 | 0 | new nsTextNode(NodeInfo()->NodeInfoManager()); |
3003 | 0 | textContent->SetText(str, true); |
3004 | 0 | AppendChildTo(textContent, true); |
3005 | 0 | } |
3006 | 0 | if (s == end) { |
3007 | 0 | break; |
3008 | 0 | } |
3009 | 0 | str.Truncate(); |
3010 | 0 | RefPtr<mozilla::dom::NodeInfo> ni = |
3011 | 0 | NodeInfo()->NodeInfoManager()->GetNodeInfo(nsGkAtoms::br, |
3012 | 0 | nullptr, kNameSpaceID_XHTML, ELEMENT_NODE); |
3013 | 0 | RefPtr<HTMLBRElement> br = new HTMLBRElement(ni.forget()); |
3014 | 0 | AppendChildTo(br, true); |
3015 | 0 | } else { |
3016 | 0 | str.Append(*s); |
3017 | 0 | } |
3018 | 0 | ++s; |
3019 | 0 | } |
3020 | 0 |
|
3021 | 0 | mb.NodesAdded(); |
3022 | 0 | } |