|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=2 sw=2 et tw=79: */ |
|
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 /* |
|
8 * Base class for all element classes; this provides an implementation |
|
9 * of DOM Core's nsIDOMElement, implements nsIContent, provides |
|
10 * utility methods for subclasses, and so forth. |
|
11 */ |
|
12 |
|
13 #include "mozilla/dom/ElementInlines.h" |
|
14 |
|
15 #include "mozilla/DebugOnly.h" |
|
16 #include "mozilla/dom/Attr.h" |
|
17 #include "nsDOMAttributeMap.h" |
|
18 #include "nsIAtom.h" |
|
19 #include "nsIContentInlines.h" |
|
20 #include "nsINodeInfo.h" |
|
21 #include "nsIDocumentInlines.h" |
|
22 #include "nsIDOMNodeList.h" |
|
23 #include "nsIDOMDocument.h" |
|
24 #include "nsIContentIterator.h" |
|
25 #include "nsFocusManager.h" |
|
26 #include "nsILinkHandler.h" |
|
27 #include "nsIScriptGlobalObject.h" |
|
28 #include "nsIURL.h" |
|
29 #include "nsNetUtil.h" |
|
30 #include "nsIFrame.h" |
|
31 #include "nsIAnonymousContentCreator.h" |
|
32 #include "nsIPresShell.h" |
|
33 #include "nsPresContext.h" |
|
34 #include "nsStyleConsts.h" |
|
35 #include "nsString.h" |
|
36 #include "nsUnicharUtils.h" |
|
37 #include "nsIDOMEvent.h" |
|
38 #include "nsDOMCID.h" |
|
39 #include "nsIServiceManager.h" |
|
40 #include "nsIDOMCSSStyleDeclaration.h" |
|
41 #include "nsDOMCSSAttrDeclaration.h" |
|
42 #include "nsNameSpaceManager.h" |
|
43 #include "nsContentList.h" |
|
44 #include "nsDOMTokenList.h" |
|
45 #include "nsXBLPrototypeBinding.h" |
|
46 #include "nsError.h" |
|
47 #include "nsDOMString.h" |
|
48 #include "nsIScriptSecurityManager.h" |
|
49 #include "nsIDOMMutationEvent.h" |
|
50 #include "mozilla/AsyncEventDispatcher.h" |
|
51 #include "mozilla/ContentEvents.h" |
|
52 #include "mozilla/EventDispatcher.h" |
|
53 #include "mozilla/EventListenerManager.h" |
|
54 #include "mozilla/EventStateManager.h" |
|
55 #include "mozilla/EventStates.h" |
|
56 #include "mozilla/InternalMutationEvent.h" |
|
57 #include "mozilla/MouseEvents.h" |
|
58 #include "mozilla/TextEvents.h" |
|
59 #include "nsNodeUtils.h" |
|
60 #include "mozilla/dom/DirectionalityUtils.h" |
|
61 #include "nsDocument.h" |
|
62 #include "nsAttrValueOrString.h" |
|
63 #include "nsAttrValueInlines.h" |
|
64 #ifdef MOZ_XUL |
|
65 #include "nsXULElement.h" |
|
66 #endif /* MOZ_XUL */ |
|
67 #include "nsFrameManager.h" |
|
68 #include "nsFrameSelection.h" |
|
69 #ifdef DEBUG |
|
70 #include "nsRange.h" |
|
71 #endif |
|
72 |
|
73 #include "nsBindingManager.h" |
|
74 #include "nsXBLBinding.h" |
|
75 #include "nsPIDOMWindow.h" |
|
76 #include "nsPIBoxObject.h" |
|
77 #include "mozilla/dom/DOMRect.h" |
|
78 #include "nsSVGUtils.h" |
|
79 #include "nsLayoutUtils.h" |
|
80 #include "nsGkAtoms.h" |
|
81 #include "nsContentUtils.h" |
|
82 #include "ChildIterator.h" |
|
83 |
|
84 #include "nsIDOMEventListener.h" |
|
85 #include "nsIWebNavigation.h" |
|
86 #include "nsIBaseWindow.h" |
|
87 #include "nsIWidget.h" |
|
88 |
|
89 #include "nsNodeInfoManager.h" |
|
90 #include "nsICategoryManager.h" |
|
91 #include "nsIDOMDocumentType.h" |
|
92 #include "nsIDOMUserDataHandler.h" |
|
93 #include "nsGenericHTMLElement.h" |
|
94 #include "nsIEditor.h" |
|
95 #include "nsIEditorIMESupport.h" |
|
96 #include "nsContentCreatorFunctions.h" |
|
97 #include "nsIControllers.h" |
|
98 #include "nsView.h" |
|
99 #include "nsViewManager.h" |
|
100 #include "nsIScrollableFrame.h" |
|
101 #include "mozilla/css/StyleRule.h" /* For nsCSSSelectorList */ |
|
102 #include "nsCSSRuleProcessor.h" |
|
103 #include "nsRuleProcessorData.h" |
|
104 #include "nsTextNode.h" |
|
105 |
|
106 #ifdef MOZ_XUL |
|
107 #include "nsIXULDocument.h" |
|
108 #endif /* MOZ_XUL */ |
|
109 |
|
110 #include "nsCycleCollectionParticipant.h" |
|
111 #include "nsCCUncollectableMarker.h" |
|
112 |
|
113 #include "mozAutoDocUpdate.h" |
|
114 |
|
115 #include "nsCSSParser.h" |
|
116 #include "prprf.h" |
|
117 #include "nsDOMMutationObserver.h" |
|
118 #include "nsSVGFeatures.h" |
|
119 #include "nsWrapperCacheInlines.h" |
|
120 #include "xpcpublic.h" |
|
121 #include "nsIScriptError.h" |
|
122 #include "mozilla/Telemetry.h" |
|
123 |
|
124 #include "mozilla/CORSMode.h" |
|
125 #include "mozilla/dom/ShadowRoot.h" |
|
126 |
|
127 #include "nsStyledElement.h" |
|
128 #include "nsXBLService.h" |
|
129 #include "nsITextControlElement.h" |
|
130 #include "nsITextControlFrame.h" |
|
131 #include "nsISupportsImpl.h" |
|
132 #include "mozilla/dom/DocumentFragment.h" |
|
133 #include "mozilla/IntegerPrintfMacros.h" |
|
134 |
|
135 using namespace mozilla; |
|
136 using namespace mozilla::dom; |
|
137 |
|
138 NS_IMETHODIMP |
|
139 Element::QueryInterface(REFNSIID aIID, void** aInstancePtr) |
|
140 { |
|
141 NS_ASSERTION(aInstancePtr, |
|
142 "QueryInterface requires a non-NULL destination!"); |
|
143 nsresult rv = FragmentOrElement::QueryInterface(aIID, aInstancePtr); |
|
144 if (NS_SUCCEEDED(rv)) { |
|
145 return NS_OK; |
|
146 } |
|
147 |
|
148 // Give the binding manager a chance to get an interface for this element. |
|
149 return OwnerDoc()->BindingManager()->GetBindingImplementation(this, aIID, |
|
150 aInstancePtr); |
|
151 } |
|
152 |
|
153 EventStates |
|
154 Element::IntrinsicState() const |
|
155 { |
|
156 return IsEditable() ? NS_EVENT_STATE_MOZ_READWRITE : |
|
157 NS_EVENT_STATE_MOZ_READONLY; |
|
158 } |
|
159 |
|
160 void |
|
161 Element::NotifyStateChange(EventStates aStates) |
|
162 { |
|
163 nsIDocument* doc = GetCurrentDoc(); |
|
164 if (doc) { |
|
165 nsAutoScriptBlocker scriptBlocker; |
|
166 doc->ContentStateChanged(this, aStates); |
|
167 } |
|
168 } |
|
169 |
|
170 void |
|
171 Element::UpdateLinkState(EventStates aState) |
|
172 { |
|
173 NS_ABORT_IF_FALSE(!aState.HasAtLeastOneOfStates(~(NS_EVENT_STATE_VISITED | |
|
174 NS_EVENT_STATE_UNVISITED)), |
|
175 "Unexpected link state bits"); |
|
176 mState = |
|
177 (mState & ~(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED)) | |
|
178 aState; |
|
179 } |
|
180 |
|
181 void |
|
182 Element::UpdateState(bool aNotify) |
|
183 { |
|
184 EventStates oldState = mState; |
|
185 mState = IntrinsicState() | (oldState & ESM_MANAGED_STATES); |
|
186 if (aNotify) { |
|
187 EventStates changedStates = oldState ^ mState; |
|
188 if (!changedStates.IsEmpty()) { |
|
189 nsIDocument* doc = GetCurrentDoc(); |
|
190 if (doc) { |
|
191 nsAutoScriptBlocker scriptBlocker; |
|
192 doc->ContentStateChanged(this, changedStates); |
|
193 } |
|
194 } |
|
195 } |
|
196 } |
|
197 |
|
198 void |
|
199 nsIContent::UpdateEditableState(bool aNotify) |
|
200 { |
|
201 // Guaranteed to be non-element content |
|
202 NS_ASSERTION(!IsElement(), "What happened here?"); |
|
203 nsIContent *parent = GetParent(); |
|
204 |
|
205 // Skip over unknown native anonymous content to avoid setting a flag we |
|
206 // can't clear later |
|
207 bool isUnknownNativeAnon = false; |
|
208 if (IsInNativeAnonymousSubtree()) { |
|
209 isUnknownNativeAnon = true; |
|
210 nsCOMPtr<nsIContent> root = this; |
|
211 while (root && !root->IsRootOfNativeAnonymousSubtree()) { |
|
212 root = root->GetParent(); |
|
213 } |
|
214 // root should always be true here, but isn't -- bug 999416 |
|
215 if (root) { |
|
216 nsIFrame* rootFrame = root->GetPrimaryFrame(); |
|
217 if (rootFrame) { |
|
218 nsIFrame* parentFrame = rootFrame->GetParent(); |
|
219 nsITextControlFrame* textCtrl = do_QueryFrame(parentFrame); |
|
220 isUnknownNativeAnon = !textCtrl; |
|
221 } |
|
222 } |
|
223 } |
|
224 |
|
225 SetEditableFlag(parent && parent->HasFlag(NODE_IS_EDITABLE) && |
|
226 !isUnknownNativeAnon); |
|
227 } |
|
228 |
|
229 void |
|
230 Element::UpdateEditableState(bool aNotify) |
|
231 { |
|
232 nsIContent *parent = GetParent(); |
|
233 |
|
234 SetEditableFlag(parent && parent->HasFlag(NODE_IS_EDITABLE)); |
|
235 if (aNotify) { |
|
236 UpdateState(aNotify); |
|
237 } else { |
|
238 // Avoid calling UpdateState in this very common case, because |
|
239 // this gets called for pretty much every single element on |
|
240 // insertion into the document and UpdateState can be slow for |
|
241 // some kinds of elements even when not notifying. |
|
242 if (IsEditable()) { |
|
243 RemoveStatesSilently(NS_EVENT_STATE_MOZ_READONLY); |
|
244 AddStatesSilently(NS_EVENT_STATE_MOZ_READWRITE); |
|
245 } else { |
|
246 RemoveStatesSilently(NS_EVENT_STATE_MOZ_READWRITE); |
|
247 AddStatesSilently(NS_EVENT_STATE_MOZ_READONLY); |
|
248 } |
|
249 } |
|
250 } |
|
251 |
|
252 EventStates |
|
253 Element::StyleStateFromLocks() const |
|
254 { |
|
255 EventStates locks = LockedStyleStates(); |
|
256 EventStates state = mState | locks; |
|
257 |
|
258 if (locks.HasState(NS_EVENT_STATE_VISITED)) { |
|
259 return state & ~NS_EVENT_STATE_UNVISITED; |
|
260 } |
|
261 if (locks.HasState(NS_EVENT_STATE_UNVISITED)) { |
|
262 return state & ~NS_EVENT_STATE_VISITED; |
|
263 } |
|
264 return state; |
|
265 } |
|
266 |
|
267 EventStates |
|
268 Element::LockedStyleStates() const |
|
269 { |
|
270 EventStates* locks = |
|
271 static_cast<EventStates*>(GetProperty(nsGkAtoms::lockedStyleStates)); |
|
272 if (locks) { |
|
273 return *locks; |
|
274 } |
|
275 return EventStates(); |
|
276 } |
|
277 |
|
278 void |
|
279 Element::NotifyStyleStateChange(EventStates aStates) |
|
280 { |
|
281 nsIDocument* doc = GetCurrentDoc(); |
|
282 if (doc) { |
|
283 nsIPresShell *presShell = doc->GetShell(); |
|
284 if (presShell) { |
|
285 nsAutoScriptBlocker scriptBlocker; |
|
286 presShell->ContentStateChanged(doc, this, aStates); |
|
287 } |
|
288 } |
|
289 } |
|
290 |
|
291 void |
|
292 Element::LockStyleStates(EventStates aStates) |
|
293 { |
|
294 EventStates* locks = new EventStates(LockedStyleStates()); |
|
295 |
|
296 *locks |= aStates; |
|
297 |
|
298 if (aStates.HasState(NS_EVENT_STATE_VISITED)) { |
|
299 *locks &= ~NS_EVENT_STATE_UNVISITED; |
|
300 } |
|
301 if (aStates.HasState(NS_EVENT_STATE_UNVISITED)) { |
|
302 *locks &= ~NS_EVENT_STATE_VISITED; |
|
303 } |
|
304 |
|
305 SetProperty(nsGkAtoms::lockedStyleStates, locks, |
|
306 nsINode::DeleteProperty<EventStates>); |
|
307 SetHasLockedStyleStates(); |
|
308 |
|
309 NotifyStyleStateChange(aStates); |
|
310 } |
|
311 |
|
312 void |
|
313 Element::UnlockStyleStates(EventStates aStates) |
|
314 { |
|
315 EventStates* locks = new EventStates(LockedStyleStates()); |
|
316 |
|
317 *locks &= ~aStates; |
|
318 |
|
319 if (locks->IsEmpty()) { |
|
320 DeleteProperty(nsGkAtoms::lockedStyleStates); |
|
321 ClearHasLockedStyleStates(); |
|
322 delete locks; |
|
323 } |
|
324 else { |
|
325 SetProperty(nsGkAtoms::lockedStyleStates, locks, |
|
326 nsINode::DeleteProperty<EventStates>); |
|
327 } |
|
328 |
|
329 NotifyStyleStateChange(aStates); |
|
330 } |
|
331 |
|
332 void |
|
333 Element::ClearStyleStateLocks() |
|
334 { |
|
335 EventStates locks = LockedStyleStates(); |
|
336 |
|
337 DeleteProperty(nsGkAtoms::lockedStyleStates); |
|
338 ClearHasLockedStyleStates(); |
|
339 |
|
340 NotifyStyleStateChange(locks); |
|
341 } |
|
342 |
|
343 bool |
|
344 Element::GetBindingURL(nsIDocument *aDocument, css::URLValue **aResult) |
|
345 { |
|
346 // If we have a frame the frame has already loaded the binding. And |
|
347 // otherwise, don't do anything else here unless we're dealing with |
|
348 // XUL or an HTML element that may have a plugin-related overlay |
|
349 // (i.e. object, embed, or applet). |
|
350 bool isXULorPluginElement = (IsXUL() || |
|
351 IsHTML(nsGkAtoms::object) || |
|
352 IsHTML(nsGkAtoms::embed) || |
|
353 IsHTML(nsGkAtoms::applet)); |
|
354 nsIPresShell *shell = aDocument->GetShell(); |
|
355 if (!shell || GetPrimaryFrame() || !isXULorPluginElement) { |
|
356 *aResult = nullptr; |
|
357 |
|
358 return true; |
|
359 } |
|
360 |
|
361 // Get the computed -moz-binding directly from the style context |
|
362 nsPresContext *pctx = shell->GetPresContext(); |
|
363 NS_ENSURE_TRUE(pctx, false); |
|
364 |
|
365 nsRefPtr<nsStyleContext> sc = pctx->StyleSet()->ResolveStyleFor(this, |
|
366 nullptr); |
|
367 NS_ENSURE_TRUE(sc, false); |
|
368 |
|
369 *aResult = sc->StyleDisplay()->mBinding; |
|
370 |
|
371 return true; |
|
372 } |
|
373 |
|
374 JSObject* |
|
375 Element::WrapObject(JSContext *aCx) |
|
376 { |
|
377 JS::Rooted<JSObject*> obj(aCx, nsINode::WrapObject(aCx)); |
|
378 if (!obj) { |
|
379 return nullptr; |
|
380 } |
|
381 |
|
382 // Custom element prototype swizzling. |
|
383 CustomElementData* data = GetCustomElementData(); |
|
384 if (obj && data) { |
|
385 // If this is a registered custom element then fix the prototype. |
|
386 JSAutoCompartment ac(aCx, obj); |
|
387 nsDocument* document = static_cast<nsDocument*>(OwnerDoc()); |
|
388 JS::Rooted<JSObject*> prototype(aCx); |
|
389 document->GetCustomPrototype(NodeInfo()->NamespaceID(), data->mType, &prototype); |
|
390 if (prototype) { |
|
391 if (!JS_WrapObject(aCx, &prototype) || !JS_SetPrototype(aCx, obj, prototype)) { |
|
392 dom::Throw(aCx, NS_ERROR_FAILURE); |
|
393 return nullptr; |
|
394 } |
|
395 } |
|
396 } |
|
397 |
|
398 nsIDocument* doc; |
|
399 if (HasFlag(NODE_FORCE_XBL_BINDINGS)) { |
|
400 doc = OwnerDoc(); |
|
401 } |
|
402 else { |
|
403 doc = GetCurrentDoc(); |
|
404 } |
|
405 |
|
406 if (!doc) { |
|
407 // There's no baseclass that cares about this call so we just |
|
408 // return here. |
|
409 return obj; |
|
410 } |
|
411 |
|
412 // We must ensure that the XBL Binding is installed before we hand |
|
413 // back this object. |
|
414 |
|
415 if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) && GetXBLBinding()) { |
|
416 // There's already a binding for this element so nothing left to |
|
417 // be done here. |
|
418 |
|
419 // In theory we could call ExecuteAttachedHandler here when it's safe to |
|
420 // run script if we also removed the binding from the PAQ queue, but that |
|
421 // seems like a scary change that would mosly just add more |
|
422 // inconsistencies. |
|
423 return obj; |
|
424 } |
|
425 |
|
426 // Make sure the style context goes away _before_ we load the binding |
|
427 // since that can destroy the relevant presshell. |
|
428 mozilla::css::URLValue *bindingURL; |
|
429 bool ok = GetBindingURL(doc, &bindingURL); |
|
430 if (!ok) { |
|
431 dom::Throw(aCx, NS_ERROR_FAILURE); |
|
432 return nullptr; |
|
433 } |
|
434 |
|
435 if (!bindingURL) { |
|
436 // No binding, nothing left to do here. |
|
437 return obj; |
|
438 } |
|
439 |
|
440 nsCOMPtr<nsIURI> uri = bindingURL->GetURI(); |
|
441 nsCOMPtr<nsIPrincipal> principal = bindingURL->mOriginPrincipal; |
|
442 |
|
443 // We have a binding that must be installed. |
|
444 bool dummy; |
|
445 |
|
446 nsXBLService* xblService = nsXBLService::GetInstance(); |
|
447 if (!xblService) { |
|
448 dom::Throw(aCx, NS_ERROR_NOT_AVAILABLE); |
|
449 return nullptr; |
|
450 } |
|
451 |
|
452 { |
|
453 // Make a scope so that ~nsRefPtr can GC before returning obj. |
|
454 nsRefPtr<nsXBLBinding> binding; |
|
455 xblService->LoadBindings(this, uri, principal, getter_AddRefs(binding), &dummy); |
|
456 |
|
457 if (binding) { |
|
458 if (nsContentUtils::IsSafeToRunScript()) { |
|
459 binding->ExecuteAttachedHandler(); |
|
460 } |
|
461 else { |
|
462 nsContentUtils::AddScriptRunner( |
|
463 NS_NewRunnableMethod(binding, &nsXBLBinding::ExecuteAttachedHandler)); |
|
464 } |
|
465 } |
|
466 } |
|
467 |
|
468 return obj; |
|
469 } |
|
470 |
|
471 nsDOMTokenList* |
|
472 Element::GetClassList() |
|
473 { |
|
474 Element::nsDOMSlots *slots = DOMSlots(); |
|
475 |
|
476 if (!slots->mClassList) { |
|
477 nsIAtom* classAttr = GetClassAttributeName(); |
|
478 if (classAttr) { |
|
479 slots->mClassList = new nsDOMTokenList(this, classAttr); |
|
480 } |
|
481 } |
|
482 |
|
483 return slots->mClassList; |
|
484 } |
|
485 |
|
486 void |
|
487 Element::GetClassList(nsISupports** aClassList) |
|
488 { |
|
489 NS_IF_ADDREF(*aClassList = GetClassList()); |
|
490 } |
|
491 |
|
492 already_AddRefed<nsIHTMLCollection> |
|
493 Element::GetElementsByTagName(const nsAString& aLocalName) |
|
494 { |
|
495 return NS_GetContentList(this, kNameSpaceID_Unknown, aLocalName); |
|
496 } |
|
497 |
|
498 void |
|
499 Element::GetElementsByTagName(const nsAString& aLocalName, |
|
500 nsIDOMHTMLCollection** aResult) |
|
501 { |
|
502 *aResult = GetElementsByTagName(aLocalName).take(); |
|
503 } |
|
504 |
|
505 nsIFrame* |
|
506 Element::GetStyledFrame() |
|
507 { |
|
508 nsIFrame *frame = GetPrimaryFrame(Flush_Layout); |
|
509 return frame ? nsLayoutUtils::GetStyleFrame(frame) : nullptr; |
|
510 } |
|
511 |
|
512 nsIScrollableFrame* |
|
513 Element::GetScrollFrame(nsIFrame **aStyledFrame, bool aFlushLayout) |
|
514 { |
|
515 // it isn't clear what to return for SVG nodes, so just return nothing |
|
516 if (IsSVG()) { |
|
517 if (aStyledFrame) { |
|
518 *aStyledFrame = nullptr; |
|
519 } |
|
520 return nullptr; |
|
521 } |
|
522 |
|
523 // Inline version of GetStyledFrame to use Flush_None if needed. |
|
524 nsIFrame* frame = GetPrimaryFrame(aFlushLayout ? Flush_Layout : Flush_None); |
|
525 if (frame) { |
|
526 frame = nsLayoutUtils::GetStyleFrame(frame); |
|
527 } |
|
528 |
|
529 if (aStyledFrame) { |
|
530 *aStyledFrame = frame; |
|
531 } |
|
532 if (!frame) { |
|
533 return nullptr; |
|
534 } |
|
535 |
|
536 // menu frames implement GetScrollTargetFrame but we don't want |
|
537 // to use it here. Similar for comboboxes. |
|
538 nsIAtom* type = frame->GetType(); |
|
539 if (type != nsGkAtoms::menuFrame && type != nsGkAtoms::comboboxControlFrame) { |
|
540 nsIScrollableFrame *scrollFrame = frame->GetScrollTargetFrame(); |
|
541 if (scrollFrame) |
|
542 return scrollFrame; |
|
543 } |
|
544 |
|
545 nsIDocument* doc = OwnerDoc(); |
|
546 bool quirksMode = doc->GetCompatibilityMode() == eCompatibility_NavQuirks; |
|
547 Element* elementWithRootScrollInfo = |
|
548 quirksMode ? doc->GetBodyElement() : doc->GetRootElement(); |
|
549 if (this == elementWithRootScrollInfo) { |
|
550 // In quirks mode, the scroll info for the body element should map to the |
|
551 // root scrollable frame. |
|
552 // In strict mode, the scroll info for the root element should map to the |
|
553 // the root scrollable frame. |
|
554 return frame->PresContext()->PresShell()->GetRootScrollFrameAsScrollable(); |
|
555 } |
|
556 |
|
557 return nullptr; |
|
558 } |
|
559 |
|
560 void |
|
561 Element::ScrollIntoView(bool aTop) |
|
562 { |
|
563 nsIDocument *document = GetCurrentDoc(); |
|
564 if (!document) { |
|
565 return; |
|
566 } |
|
567 |
|
568 // Get the presentation shell |
|
569 nsCOMPtr<nsIPresShell> presShell = document->GetShell(); |
|
570 if (!presShell) { |
|
571 return; |
|
572 } |
|
573 |
|
574 int16_t vpercent = aTop ? nsIPresShell::SCROLL_TOP : |
|
575 nsIPresShell::SCROLL_BOTTOM; |
|
576 |
|
577 presShell->ScrollContentIntoView(this, |
|
578 nsIPresShell::ScrollAxis( |
|
579 vpercent, |
|
580 nsIPresShell::SCROLL_ALWAYS), |
|
581 nsIPresShell::ScrollAxis(), |
|
582 nsIPresShell::SCROLL_OVERFLOW_HIDDEN); |
|
583 } |
|
584 |
|
585 bool |
|
586 Element::ScrollByNoFlush(int32_t aDx, int32_t aDy) |
|
587 { |
|
588 nsIScrollableFrame* sf = GetScrollFrame(nullptr, false); |
|
589 if (!sf) { |
|
590 return false; |
|
591 } |
|
592 |
|
593 nsWeakFrame weakRef(sf->GetScrolledFrame()); |
|
594 |
|
595 CSSIntPoint before = sf->GetScrollPositionCSSPixels(); |
|
596 sf->ScrollToCSSPixelsApproximate(CSSIntPoint(before.x + aDx, before.y + aDy)); |
|
597 |
|
598 // The frame was destroyed, can't keep on scrolling. |
|
599 if (!weakRef.IsAlive()) { |
|
600 return false; |
|
601 } |
|
602 |
|
603 CSSIntPoint after = sf->GetScrollPositionCSSPixels(); |
|
604 return (before != after); |
|
605 } |
|
606 |
|
607 static nsSize GetScrollRectSizeForOverflowVisibleFrame(nsIFrame* aFrame) |
|
608 { |
|
609 if (!aFrame) { |
|
610 return nsSize(0,0); |
|
611 } |
|
612 |
|
613 nsRect paddingRect = aFrame->GetPaddingRectRelativeToSelf(); |
|
614 nsOverflowAreas overflowAreas(paddingRect, paddingRect); |
|
615 // Add the scrollable overflow areas of children (if any) to the paddingRect. |
|
616 // It's important to start with the paddingRect, otherwise if there are no |
|
617 // children the overflow rect will be 0,0,0,0 which will force the point 0,0 |
|
618 // to be included in the final rect. |
|
619 nsLayoutUtils::UnionChildOverflow(aFrame, overflowAreas); |
|
620 // Make sure that an empty padding-rect's edges are included, by adding |
|
621 // the padding-rect in again with UnionEdges. |
|
622 nsRect overflowRect = |
|
623 overflowAreas.ScrollableOverflow().UnionEdges(paddingRect); |
|
624 return nsLayoutUtils::GetScrolledRect(aFrame, |
|
625 overflowRect, paddingRect.Size(), |
|
626 aFrame->StyleVisibility()->mDirection).Size(); |
|
627 } |
|
628 |
|
629 int32_t |
|
630 Element::ScrollHeight() |
|
631 { |
|
632 if (IsSVG()) |
|
633 return 0; |
|
634 |
|
635 nsIScrollableFrame* sf = GetScrollFrame(); |
|
636 nscoord height; |
|
637 if (sf) { |
|
638 height = sf->GetScrollRange().height + sf->GetScrollPortRect().height; |
|
639 } else { |
|
640 height = GetScrollRectSizeForOverflowVisibleFrame(GetStyledFrame()).height; |
|
641 } |
|
642 |
|
643 return nsPresContext::AppUnitsToIntCSSPixels(height); |
|
644 } |
|
645 |
|
646 int32_t |
|
647 Element::ScrollWidth() |
|
648 { |
|
649 if (IsSVG()) |
|
650 return 0; |
|
651 |
|
652 nsIScrollableFrame* sf = GetScrollFrame(); |
|
653 nscoord width; |
|
654 if (sf) { |
|
655 width = sf->GetScrollRange().width + sf->GetScrollPortRect().width; |
|
656 } else { |
|
657 width = GetScrollRectSizeForOverflowVisibleFrame(GetStyledFrame()).width; |
|
658 } |
|
659 |
|
660 return nsPresContext::AppUnitsToIntCSSPixels(width); |
|
661 } |
|
662 |
|
663 nsRect |
|
664 Element::GetClientAreaRect() |
|
665 { |
|
666 nsIFrame* styledFrame; |
|
667 nsIScrollableFrame* sf = GetScrollFrame(&styledFrame); |
|
668 |
|
669 if (sf) { |
|
670 return sf->GetScrollPortRect(); |
|
671 } |
|
672 |
|
673 if (styledFrame && |
|
674 (styledFrame->StyleDisplay()->mDisplay != NS_STYLE_DISPLAY_INLINE || |
|
675 styledFrame->IsFrameOfType(nsIFrame::eReplaced))) { |
|
676 // Special case code to make client area work even when there isn't |
|
677 // a scroll view, see bug 180552, bug 227567. |
|
678 return styledFrame->GetPaddingRect() - styledFrame->GetPositionIgnoringScrolling(); |
|
679 } |
|
680 |
|
681 // SVG nodes reach here and just return 0 |
|
682 return nsRect(0, 0, 0, 0); |
|
683 } |
|
684 |
|
685 already_AddRefed<DOMRect> |
|
686 Element::GetBoundingClientRect() |
|
687 { |
|
688 nsRefPtr<DOMRect> rect = new DOMRect(this); |
|
689 |
|
690 nsIFrame* frame = GetPrimaryFrame(Flush_Layout); |
|
691 if (!frame) { |
|
692 // display:none, perhaps? Return the empty rect |
|
693 return rect.forget(); |
|
694 } |
|
695 |
|
696 nsRect r = nsLayoutUtils::GetAllInFlowRectsUnion(frame, |
|
697 nsLayoutUtils::GetContainingBlockForClientRect(frame), |
|
698 nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS); |
|
699 rect->SetLayoutRect(r); |
|
700 return rect.forget(); |
|
701 } |
|
702 |
|
703 already_AddRefed<DOMRectList> |
|
704 Element::GetClientRects() |
|
705 { |
|
706 nsRefPtr<DOMRectList> rectList = new DOMRectList(this); |
|
707 |
|
708 nsIFrame* frame = GetPrimaryFrame(Flush_Layout); |
|
709 if (!frame) { |
|
710 // display:none, perhaps? Return an empty list |
|
711 return rectList.forget(); |
|
712 } |
|
713 |
|
714 nsLayoutUtils::RectListBuilder builder(rectList); |
|
715 nsLayoutUtils::GetAllInFlowRects(frame, |
|
716 nsLayoutUtils::GetContainingBlockForClientRect(frame), &builder, |
|
717 nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS); |
|
718 return rectList.forget(); |
|
719 } |
|
720 |
|
721 |
|
722 //---------------------------------------------------------------------- |
|
723 |
|
724 void |
|
725 Element::AddToIdTable(nsIAtom* aId) |
|
726 { |
|
727 NS_ASSERTION(HasID(), "Node doesn't have an ID?"); |
|
728 if (HasFlag(NODE_IS_IN_SHADOW_TREE)) { |
|
729 ShadowRoot* containingShadow = GetContainingShadow(); |
|
730 containingShadow->AddToIdTable(this, aId); |
|
731 } else { |
|
732 nsIDocument* doc = GetCurrentDoc(); |
|
733 if (doc && (!IsInAnonymousSubtree() || doc->IsXUL())) { |
|
734 doc->AddToIdTable(this, aId); |
|
735 } |
|
736 } |
|
737 } |
|
738 |
|
739 void |
|
740 Element::RemoveFromIdTable() |
|
741 { |
|
742 if (HasID()) { |
|
743 RemoveFromIdTable(DoGetID()); |
|
744 } |
|
745 } |
|
746 |
|
747 void |
|
748 Element::RemoveFromIdTable(nsIAtom* aId) |
|
749 { |
|
750 NS_ASSERTION(HasID(), "Node doesn't have an ID?"); |
|
751 if (HasFlag(NODE_IS_IN_SHADOW_TREE)) { |
|
752 ShadowRoot* containingShadow = GetContainingShadow(); |
|
753 // Check for containingShadow because it may have |
|
754 // been deleted during unlinking. |
|
755 if (containingShadow) { |
|
756 containingShadow->RemoveFromIdTable(this, aId); |
|
757 } |
|
758 } else { |
|
759 nsIDocument* doc = GetCurrentDoc(); |
|
760 if (doc && (!IsInAnonymousSubtree() || doc->IsXUL())) { |
|
761 // id can be null during mutation events evilness. Also, XUL elements |
|
762 // loose their proto attributes during cc-unlink, so this can happen |
|
763 // during cc-unlink too. |
|
764 if (aId) { |
|
765 doc->RemoveFromIdTable(this, aId); |
|
766 } |
|
767 } |
|
768 } |
|
769 } |
|
770 |
|
771 already_AddRefed<ShadowRoot> |
|
772 Element::CreateShadowRoot(ErrorResult& aError) |
|
773 { |
|
774 nsAutoScriptBlocker scriptBlocker; |
|
775 |
|
776 nsCOMPtr<nsINodeInfo> nodeInfo; |
|
777 nodeInfo = mNodeInfo->NodeInfoManager()->GetNodeInfo( |
|
778 nsGkAtoms::documentFragmentNodeName, nullptr, kNameSpaceID_None, |
|
779 nsIDOMNode::DOCUMENT_FRAGMENT_NODE); |
|
780 |
|
781 nsRefPtr<nsXBLDocumentInfo> docInfo = new nsXBLDocumentInfo(OwnerDoc()); |
|
782 |
|
783 nsXBLPrototypeBinding* protoBinding = new nsXBLPrototypeBinding(); |
|
784 aError = protoBinding->Init(NS_LITERAL_CSTRING("shadowroot"), |
|
785 docInfo, this, true); |
|
786 if (aError.Failed()) { |
|
787 delete protoBinding; |
|
788 return nullptr; |
|
789 } |
|
790 |
|
791 // Unlike for XBL, false is the default for inheriting style. |
|
792 protoBinding->SetInheritsStyle(false); |
|
793 |
|
794 // Calling SetPrototypeBinding takes ownership of protoBinding. |
|
795 docInfo->SetPrototypeBinding(NS_LITERAL_CSTRING("shadowroot"), protoBinding); |
|
796 |
|
797 nsRefPtr<ShadowRoot> shadowRoot = new ShadowRoot(this, nodeInfo.forget(), |
|
798 protoBinding); |
|
799 |
|
800 // Replace the old ShadowRoot with the new one and let the old |
|
801 // ShadowRoot know about the younger ShadowRoot because the old |
|
802 // ShadowRoot is projected into the younger ShadowRoot's shadow |
|
803 // insertion point (if it exists). |
|
804 ShadowRoot* olderShadow = GetShadowRoot(); |
|
805 SetShadowRoot(shadowRoot); |
|
806 if (olderShadow) { |
|
807 olderShadow->SetYoungerShadow(shadowRoot); |
|
808 } |
|
809 |
|
810 // xblBinding takes ownership of docInfo. |
|
811 nsRefPtr<nsXBLBinding> xblBinding = new nsXBLBinding(shadowRoot, protoBinding); |
|
812 shadowRoot->SetAssociatedBinding(xblBinding); |
|
813 xblBinding->SetBoundElement(this); |
|
814 |
|
815 SetXBLBinding(xblBinding); |
|
816 |
|
817 // Recreate the frame for the bound content because binding a ShadowRoot |
|
818 // changes how things are rendered. |
|
819 nsIDocument* doc = GetCurrentDoc(); |
|
820 if (doc) { |
|
821 nsIPresShell *shell = doc->GetShell(); |
|
822 if (shell) { |
|
823 shell->RecreateFramesFor(this); |
|
824 } |
|
825 } |
|
826 |
|
827 return shadowRoot.forget(); |
|
828 } |
|
829 |
|
830 void |
|
831 Element::GetAttribute(const nsAString& aName, DOMString& aReturn) |
|
832 { |
|
833 const nsAttrValue* val = |
|
834 mAttrsAndChildren.GetAttr(aName, |
|
835 IsHTML() && IsInHTMLDocument() ? |
|
836 eIgnoreCase : eCaseMatters); |
|
837 if (val) { |
|
838 val->ToString(aReturn); |
|
839 } else { |
|
840 if (IsXUL()) { |
|
841 // XXX should be SetDOMStringToNull(aReturn); |
|
842 // See bug 232598 |
|
843 // aReturn is already empty |
|
844 } else { |
|
845 aReturn.SetNull(); |
|
846 } |
|
847 } |
|
848 } |
|
849 |
|
850 void |
|
851 Element::SetAttribute(const nsAString& aName, |
|
852 const nsAString& aValue, |
|
853 ErrorResult& aError) |
|
854 { |
|
855 const nsAttrName* name = InternalGetExistingAttrNameFromQName(aName); |
|
856 |
|
857 if (!name) { |
|
858 aError = nsContentUtils::CheckQName(aName, false); |
|
859 if (aError.Failed()) { |
|
860 return; |
|
861 } |
|
862 |
|
863 nsCOMPtr<nsIAtom> nameAtom; |
|
864 if (IsHTML() && IsInHTMLDocument()) { |
|
865 nsAutoString lower; |
|
866 nsresult rv = nsContentUtils::ASCIIToLower(aName, lower); |
|
867 if (NS_SUCCEEDED(rv)) { |
|
868 nameAtom = do_GetAtom(lower); |
|
869 } |
|
870 } |
|
871 else { |
|
872 nameAtom = do_GetAtom(aName); |
|
873 } |
|
874 if (!nameAtom) { |
|
875 aError.Throw(NS_ERROR_OUT_OF_MEMORY); |
|
876 return; |
|
877 } |
|
878 aError = SetAttr(kNameSpaceID_None, nameAtom, aValue, true); |
|
879 return; |
|
880 } |
|
881 |
|
882 aError = SetAttr(name->NamespaceID(), name->LocalName(), name->GetPrefix(), |
|
883 aValue, true); |
|
884 return; |
|
885 } |
|
886 |
|
887 void |
|
888 Element::RemoveAttribute(const nsAString& aName, ErrorResult& aError) |
|
889 { |
|
890 const nsAttrName* name = InternalGetExistingAttrNameFromQName(aName); |
|
891 |
|
892 if (!name) { |
|
893 // If there is no canonical nsAttrName for this attribute name, then the |
|
894 // attribute does not exist and we can't get its namespace ID and |
|
895 // local name below, so we return early. |
|
896 return; |
|
897 } |
|
898 |
|
899 // Hold a strong reference here so that the atom or nodeinfo doesn't go |
|
900 // away during UnsetAttr. If it did UnsetAttr would be left with a |
|
901 // dangling pointer as argument without knowing it. |
|
902 nsAttrName tmp(*name); |
|
903 |
|
904 aError = UnsetAttr(name->NamespaceID(), name->LocalName(), true); |
|
905 } |
|
906 |
|
907 Attr* |
|
908 Element::GetAttributeNode(const nsAString& aName) |
|
909 { |
|
910 OwnerDoc()->WarnOnceAbout(nsIDocument::eGetAttributeNode); |
|
911 return Attributes()->GetNamedItem(aName); |
|
912 } |
|
913 |
|
914 already_AddRefed<Attr> |
|
915 Element::SetAttributeNode(Attr& aNewAttr, ErrorResult& aError) |
|
916 { |
|
917 OwnerDoc()->WarnOnceAbout(nsIDocument::eSetAttributeNode); |
|
918 |
|
919 return Attributes()->SetNamedItem(aNewAttr, aError); |
|
920 } |
|
921 |
|
922 already_AddRefed<Attr> |
|
923 Element::RemoveAttributeNode(Attr& aAttribute, |
|
924 ErrorResult& aError) |
|
925 { |
|
926 OwnerDoc()->WarnOnceAbout(nsIDocument::eRemoveAttributeNode); |
|
927 return Attributes()->RemoveNamedItem(aAttribute.NodeName(), aError); |
|
928 } |
|
929 |
|
930 void |
|
931 Element::GetAttributeNS(const nsAString& aNamespaceURI, |
|
932 const nsAString& aLocalName, |
|
933 nsAString& aReturn) |
|
934 { |
|
935 int32_t nsid = |
|
936 nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI); |
|
937 |
|
938 if (nsid == kNameSpaceID_Unknown) { |
|
939 // Unknown namespace means no attribute. |
|
940 SetDOMStringToNull(aReturn); |
|
941 return; |
|
942 } |
|
943 |
|
944 nsCOMPtr<nsIAtom> name = do_GetAtom(aLocalName); |
|
945 bool hasAttr = GetAttr(nsid, name, aReturn); |
|
946 if (!hasAttr) { |
|
947 SetDOMStringToNull(aReturn); |
|
948 } |
|
949 } |
|
950 |
|
951 void |
|
952 Element::SetAttributeNS(const nsAString& aNamespaceURI, |
|
953 const nsAString& aQualifiedName, |
|
954 const nsAString& aValue, |
|
955 ErrorResult& aError) |
|
956 { |
|
957 nsCOMPtr<nsINodeInfo> ni; |
|
958 aError = |
|
959 nsContentUtils::GetNodeInfoFromQName(aNamespaceURI, aQualifiedName, |
|
960 mNodeInfo->NodeInfoManager(), |
|
961 nsIDOMNode::ATTRIBUTE_NODE, |
|
962 getter_AddRefs(ni)); |
|
963 if (aError.Failed()) { |
|
964 return; |
|
965 } |
|
966 |
|
967 aError = SetAttr(ni->NamespaceID(), ni->NameAtom(), ni->GetPrefixAtom(), |
|
968 aValue, true); |
|
969 } |
|
970 |
|
971 void |
|
972 Element::RemoveAttributeNS(const nsAString& aNamespaceURI, |
|
973 const nsAString& aLocalName, |
|
974 ErrorResult& aError) |
|
975 { |
|
976 nsCOMPtr<nsIAtom> name = do_GetAtom(aLocalName); |
|
977 int32_t nsid = |
|
978 nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI); |
|
979 |
|
980 if (nsid == kNameSpaceID_Unknown) { |
|
981 // If the namespace ID is unknown, it means there can't possibly be an |
|
982 // existing attribute. We would need a known namespace ID to pass into |
|
983 // UnsetAttr, so we return early if we don't have one. |
|
984 return; |
|
985 } |
|
986 |
|
987 aError = UnsetAttr(nsid, name, true); |
|
988 } |
|
989 |
|
990 Attr* |
|
991 Element::GetAttributeNodeNS(const nsAString& aNamespaceURI, |
|
992 const nsAString& aLocalName) |
|
993 { |
|
994 OwnerDoc()->WarnOnceAbout(nsIDocument::eGetAttributeNodeNS); |
|
995 |
|
996 return GetAttributeNodeNSInternal(aNamespaceURI, aLocalName); |
|
997 } |
|
998 |
|
999 Attr* |
|
1000 Element::GetAttributeNodeNSInternal(const nsAString& aNamespaceURI, |
|
1001 const nsAString& aLocalName) |
|
1002 { |
|
1003 return Attributes()->GetNamedItemNS(aNamespaceURI, aLocalName); |
|
1004 } |
|
1005 |
|
1006 already_AddRefed<Attr> |
|
1007 Element::SetAttributeNodeNS(Attr& aNewAttr, |
|
1008 ErrorResult& aError) |
|
1009 { |
|
1010 OwnerDoc()->WarnOnceAbout(nsIDocument::eSetAttributeNodeNS); |
|
1011 return Attributes()->SetNamedItemNS(aNewAttr, aError); |
|
1012 } |
|
1013 |
|
1014 already_AddRefed<nsIHTMLCollection> |
|
1015 Element::GetElementsByTagNameNS(const nsAString& aNamespaceURI, |
|
1016 const nsAString& aLocalName, |
|
1017 ErrorResult& aError) |
|
1018 { |
|
1019 int32_t nameSpaceId = kNameSpaceID_Wildcard; |
|
1020 |
|
1021 if (!aNamespaceURI.EqualsLiteral("*")) { |
|
1022 aError = |
|
1023 nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI, |
|
1024 nameSpaceId); |
|
1025 if (aError.Failed()) { |
|
1026 return nullptr; |
|
1027 } |
|
1028 } |
|
1029 |
|
1030 NS_ASSERTION(nameSpaceId != kNameSpaceID_Unknown, "Unexpected namespace ID!"); |
|
1031 |
|
1032 return NS_GetContentList(this, nameSpaceId, aLocalName); |
|
1033 } |
|
1034 |
|
1035 nsresult |
|
1036 Element::GetElementsByTagNameNS(const nsAString& namespaceURI, |
|
1037 const nsAString& localName, |
|
1038 nsIDOMHTMLCollection** aResult) |
|
1039 { |
|
1040 mozilla::ErrorResult rv; |
|
1041 nsCOMPtr<nsIHTMLCollection> list = |
|
1042 GetElementsByTagNameNS(namespaceURI, localName, rv); |
|
1043 if (rv.Failed()) { |
|
1044 return rv.ErrorCode(); |
|
1045 } |
|
1046 list.forget(aResult); |
|
1047 return NS_OK; |
|
1048 } |
|
1049 |
|
1050 bool |
|
1051 Element::HasAttributeNS(const nsAString& aNamespaceURI, |
|
1052 const nsAString& aLocalName) const |
|
1053 { |
|
1054 int32_t nsid = |
|
1055 nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI); |
|
1056 |
|
1057 if (nsid == kNameSpaceID_Unknown) { |
|
1058 // Unknown namespace means no attr... |
|
1059 return false; |
|
1060 } |
|
1061 |
|
1062 nsCOMPtr<nsIAtom> name = do_GetAtom(aLocalName); |
|
1063 return HasAttr(nsid, name); |
|
1064 } |
|
1065 |
|
1066 already_AddRefed<nsIHTMLCollection> |
|
1067 Element::GetElementsByClassName(const nsAString& aClassNames) |
|
1068 { |
|
1069 return nsContentUtils::GetElementsByClassName(this, aClassNames); |
|
1070 } |
|
1071 |
|
1072 nsresult |
|
1073 Element::GetElementsByClassName(const nsAString& aClassNames, |
|
1074 nsIDOMHTMLCollection** aResult) |
|
1075 { |
|
1076 *aResult = |
|
1077 nsContentUtils::GetElementsByClassName(this, aClassNames).take(); |
|
1078 return NS_OK; |
|
1079 } |
|
1080 |
|
1081 nsresult |
|
1082 Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent, |
|
1083 nsIContent* aBindingParent, |
|
1084 bool aCompileEventHandlers) |
|
1085 { |
|
1086 NS_PRECONDITION(aParent || aDocument, "Must have document if no parent!"); |
|
1087 NS_PRECONDITION((NODE_FROM(aParent, aDocument)->OwnerDoc() == OwnerDoc()), |
|
1088 "Must have the same owner document"); |
|
1089 NS_PRECONDITION(!aParent || aDocument == aParent->GetCurrentDoc(), |
|
1090 "aDocument must be current doc of aParent"); |
|
1091 NS_PRECONDITION(!GetCurrentDoc(), "Already have a document. Unbind first!"); |
|
1092 // Note that as we recurse into the kids, they'll have a non-null parent. So |
|
1093 // only assert if our parent is _changing_ while we have a parent. |
|
1094 NS_PRECONDITION(!GetParent() || aParent == GetParent(), |
|
1095 "Already have a parent. Unbind first!"); |
|
1096 NS_PRECONDITION(!GetBindingParent() || |
|
1097 aBindingParent == GetBindingParent() || |
|
1098 (!aBindingParent && aParent && |
|
1099 aParent->GetBindingParent() == GetBindingParent()), |
|
1100 "Already have a binding parent. Unbind first!"); |
|
1101 NS_PRECONDITION(!aParent || !aDocument || |
|
1102 !aParent->HasFlag(NODE_FORCE_XBL_BINDINGS), |
|
1103 "Parent in document but flagged as forcing XBL"); |
|
1104 NS_PRECONDITION(aBindingParent != this, |
|
1105 "Content must not be its own binding parent"); |
|
1106 NS_PRECONDITION(!IsRootOfNativeAnonymousSubtree() || |
|
1107 aBindingParent == aParent, |
|
1108 "Native anonymous content must have its parent as its " |
|
1109 "own binding parent"); |
|
1110 NS_PRECONDITION(aBindingParent || !aParent || |
|
1111 aBindingParent == aParent->GetBindingParent(), |
|
1112 "We should be passed the right binding parent"); |
|
1113 |
|
1114 #ifdef MOZ_XUL |
|
1115 // First set the binding parent |
|
1116 nsXULElement* xulElem = nsXULElement::FromContent(this); |
|
1117 if (xulElem) { |
|
1118 xulElem->SetXULBindingParent(aBindingParent); |
|
1119 } |
|
1120 else |
|
1121 #endif |
|
1122 { |
|
1123 if (aBindingParent) { |
|
1124 nsDOMSlots *slots = DOMSlots(); |
|
1125 |
|
1126 slots->mBindingParent = aBindingParent; // Weak, so no addref happens. |
|
1127 } |
|
1128 } |
|
1129 NS_ASSERTION(!aBindingParent || IsRootOfNativeAnonymousSubtree() || |
|
1130 !HasFlag(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE) || |
|
1131 (aParent && aParent->IsInNativeAnonymousSubtree()), |
|
1132 "Trying to re-bind content from native anonymous subtree to " |
|
1133 "non-native anonymous parent!"); |
|
1134 if (aParent) { |
|
1135 if (aParent->IsInNativeAnonymousSubtree()) { |
|
1136 SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE); |
|
1137 } |
|
1138 if (aParent->HasFlag(NODE_CHROME_ONLY_ACCESS)) { |
|
1139 SetFlags(NODE_CHROME_ONLY_ACCESS); |
|
1140 } |
|
1141 if (aParent->HasFlag(NODE_IS_IN_SHADOW_TREE)) { |
|
1142 SetFlags(NODE_IS_IN_SHADOW_TREE); |
|
1143 } |
|
1144 ShadowRoot* parentContainingShadow = aParent->GetContainingShadow(); |
|
1145 if (parentContainingShadow) { |
|
1146 DOMSlots()->mContainingShadow = parentContainingShadow; |
|
1147 } |
|
1148 } |
|
1149 |
|
1150 bool hadForceXBL = HasFlag(NODE_FORCE_XBL_BINDINGS); |
|
1151 |
|
1152 // Now set the parent and set the "Force attach xbl" flag if needed. |
|
1153 if (aParent) { |
|
1154 if (!GetParent()) { |
|
1155 NS_ADDREF(aParent); |
|
1156 } |
|
1157 mParent = aParent; |
|
1158 |
|
1159 if (aParent->HasFlag(NODE_FORCE_XBL_BINDINGS)) { |
|
1160 SetFlags(NODE_FORCE_XBL_BINDINGS); |
|
1161 } |
|
1162 } |
|
1163 else { |
|
1164 mParent = aDocument; |
|
1165 } |
|
1166 SetParentIsContent(aParent); |
|
1167 |
|
1168 // XXXbz sXBL/XBL2 issue! |
|
1169 |
|
1170 // Finally, set the document |
|
1171 if (aDocument) { |
|
1172 // Notify XBL- & nsIAnonymousContentCreator-generated |
|
1173 // anonymous content that the document is changing. |
|
1174 // XXXbz ordering issues here? Probably not, since ChangeDocumentFor is |
|
1175 // just pretty broken anyway.... Need to get it working. |
|
1176 // XXXbz XBL doesn't handle this (asserts), and we don't really want |
|
1177 // to be doing this during parsing anyway... sort this out. |
|
1178 // aDocument->BindingManager()->ChangeDocumentFor(this, nullptr, |
|
1179 // aDocument); |
|
1180 |
|
1181 // We no longer need to track the subtree pointer (and in fact we'll assert |
|
1182 // if we do this any later). |
|
1183 ClearSubtreeRootPointer(); |
|
1184 |
|
1185 // Being added to a document. |
|
1186 SetInDocument(); |
|
1187 |
|
1188 if (GetCustomElementData()) { |
|
1189 // Enqueue an attached callback for the custom element. |
|
1190 aDocument->EnqueueLifecycleCallback(nsIDocument::eAttached, this); |
|
1191 } |
|
1192 |
|
1193 // Unset this flag since we now really are in a document. |
|
1194 UnsetFlags(NODE_FORCE_XBL_BINDINGS | |
|
1195 // And clear the lazy frame construction bits. |
|
1196 NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES | |
|
1197 // And the restyle bits |
|
1198 ELEMENT_ALL_RESTYLE_FLAGS); |
|
1199 |
|
1200 // Propagate scoped style sheet tracking bit. |
|
1201 SetIsElementInStyleScope(mParent->IsElementInStyleScope()); |
|
1202 } else { |
|
1203 // If we're not in the doc, update our subtree pointer. |
|
1204 SetSubtreeRootPointer(aParent->SubtreeRoot()); |
|
1205 } |
|
1206 |
|
1207 // This has to be here, rather than in nsGenericHTMLElement::BindToTree, |
|
1208 // because it has to happen after updating the parent pointer, but before |
|
1209 // recursively binding the kids. |
|
1210 if (IsHTML()) { |
|
1211 SetDirOnBind(this, aParent); |
|
1212 } |
|
1213 |
|
1214 // If NODE_FORCE_XBL_BINDINGS was set we might have anonymous children |
|
1215 // that also need to be told that they are moving. |
|
1216 nsresult rv; |
|
1217 if (hadForceXBL) { |
|
1218 nsBindingManager* bmgr = OwnerDoc()->BindingManager(); |
|
1219 |
|
1220 nsXBLBinding* contBinding = bmgr->GetBindingWithContent(this); |
|
1221 // First check if we have a binding... |
|
1222 if (contBinding) { |
|
1223 nsCOMPtr<nsIContent> anonRoot = contBinding->GetAnonymousContent(); |
|
1224 bool allowScripts = contBinding->AllowScripts(); |
|
1225 for (nsCOMPtr<nsIContent> child = anonRoot->GetFirstChild(); |
|
1226 child; |
|
1227 child = child->GetNextSibling()) { |
|
1228 rv = child->BindToTree(aDocument, this, this, allowScripts); |
|
1229 NS_ENSURE_SUCCESS(rv, rv); |
|
1230 } |
|
1231 } |
|
1232 } |
|
1233 |
|
1234 UpdateEditableState(false); |
|
1235 |
|
1236 // Now recurse into our kids |
|
1237 for (nsIContent* child = GetFirstChild(); child; |
|
1238 child = child->GetNextSibling()) { |
|
1239 rv = child->BindToTree(aDocument, this, aBindingParent, |
|
1240 aCompileEventHandlers); |
|
1241 NS_ENSURE_SUCCESS(rv, rv); |
|
1242 } |
|
1243 |
|
1244 nsNodeUtils::ParentChainChanged(this); |
|
1245 |
|
1246 if (HasID()) { |
|
1247 AddToIdTable(DoGetID()); |
|
1248 } |
|
1249 |
|
1250 if (MayHaveStyle() && !IsXUL()) { |
|
1251 // XXXbz if we already have a style attr parsed, this won't do |
|
1252 // anything... need to fix that. |
|
1253 // If MayHaveStyle() is true, we must be an nsStyledElement |
|
1254 static_cast<nsStyledElement*>(this)->ReparseStyleAttribute(false); |
|
1255 } |
|
1256 |
|
1257 if (aDocument) { |
|
1258 // If we're in a document now, let our mapped attrs know what their new |
|
1259 // sheet is. This is safe to run for non-mapped-attribute elements too; |
|
1260 // it'll just do a small bit of unnecessary work. But most elements in |
|
1261 // practice are mapped-attribute elements. |
|
1262 nsHTMLStyleSheet* sheet = aDocument->GetAttributeStyleSheet(); |
|
1263 if (sheet) { |
|
1264 mAttrsAndChildren.SetMappedAttrStyleSheet(sheet); |
|
1265 } |
|
1266 } |
|
1267 |
|
1268 // XXXbz script execution during binding can trigger some of these |
|
1269 // postcondition asserts.... But we do want that, since things will |
|
1270 // generally be quite broken when that happens. |
|
1271 NS_POSTCONDITION(aDocument == GetCurrentDoc(), "Bound to wrong document"); |
|
1272 NS_POSTCONDITION(aParent == GetParent(), "Bound to wrong parent"); |
|
1273 NS_POSTCONDITION(aBindingParent == GetBindingParent(), |
|
1274 "Bound to wrong binding parent"); |
|
1275 |
|
1276 return NS_OK; |
|
1277 } |
|
1278 |
|
1279 class RemoveFromBindingManagerRunnable : public nsRunnable { |
|
1280 public: |
|
1281 RemoveFromBindingManagerRunnable(nsBindingManager* aManager, |
|
1282 Element* aElement, |
|
1283 nsIDocument* aDoc): |
|
1284 mManager(aManager), mElement(aElement), mDoc(aDoc) |
|
1285 {} |
|
1286 |
|
1287 NS_IMETHOD Run() |
|
1288 { |
|
1289 // It may be the case that the element was removed from the |
|
1290 // DOM, causing this runnable to be created, then inserted back |
|
1291 // into the document before the this runnable had a chance to |
|
1292 // tear down the binding. Only tear down the binding if the element |
|
1293 // is still no longer in the DOM. nsXBLService::LoadBinding tears |
|
1294 // down the old binding if the element is inserted back into the |
|
1295 // DOM and loads a different binding. |
|
1296 if (!mElement->IsInDoc()) { |
|
1297 mManager->RemovedFromDocumentInternal(mElement, mDoc); |
|
1298 } |
|
1299 |
|
1300 return NS_OK; |
|
1301 } |
|
1302 |
|
1303 private: |
|
1304 nsRefPtr<nsBindingManager> mManager; |
|
1305 nsRefPtr<Element> mElement; |
|
1306 nsCOMPtr<nsIDocument> mDoc; |
|
1307 }; |
|
1308 |
|
1309 void |
|
1310 Element::UnbindFromTree(bool aDeep, bool aNullParent) |
|
1311 { |
|
1312 NS_PRECONDITION(aDeep || (!GetCurrentDoc() && !GetBindingParent()), |
|
1313 "Shallow unbind won't clear document and binding parent on " |
|
1314 "kids!"); |
|
1315 |
|
1316 RemoveFromIdTable(); |
|
1317 |
|
1318 // Make sure to unbind this node before doing the kids |
|
1319 nsIDocument *document = |
|
1320 HasFlag(NODE_FORCE_XBL_BINDINGS) ? OwnerDoc() : GetCurrentDoc(); |
|
1321 |
|
1322 if (aNullParent) { |
|
1323 if (IsFullScreenAncestor()) { |
|
1324 // The element being removed is an ancestor of the full-screen element, |
|
1325 // exit full-screen state. |
|
1326 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
|
1327 NS_LITERAL_CSTRING("DOM"), OwnerDoc(), |
|
1328 nsContentUtils::eDOM_PROPERTIES, |
|
1329 "RemovedFullScreenElement"); |
|
1330 // Fully exit full-screen. |
|
1331 nsIDocument::ExitFullscreen(OwnerDoc(), /* async */ false); |
|
1332 } |
|
1333 if (HasPointerLock()) { |
|
1334 nsIDocument::UnlockPointer(); |
|
1335 } |
|
1336 if (GetParent()) { |
|
1337 nsINode* p = mParent; |
|
1338 mParent = nullptr; |
|
1339 NS_RELEASE(p); |
|
1340 } else { |
|
1341 mParent = nullptr; |
|
1342 } |
|
1343 SetParentIsContent(false); |
|
1344 } |
|
1345 ClearInDocument(); |
|
1346 |
|
1347 // Begin keeping track of our subtree root. |
|
1348 SetSubtreeRootPointer(aNullParent ? this : mParent->SubtreeRoot()); |
|
1349 |
|
1350 if (document) { |
|
1351 // Notify XBL- & nsIAnonymousContentCreator-generated |
|
1352 // anonymous content that the document is changing. |
|
1353 if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) { |
|
1354 nsContentUtils::AddScriptRunner( |
|
1355 new RemoveFromBindingManagerRunnable(document->BindingManager(), this, |
|
1356 document)); |
|
1357 } |
|
1358 |
|
1359 document->ClearBoxObjectFor(this); |
|
1360 |
|
1361 if (GetCustomElementData()) { |
|
1362 // Enqueue a detached callback for the custom element. |
|
1363 document->EnqueueLifecycleCallback(nsIDocument::eDetached, this); |
|
1364 } |
|
1365 } |
|
1366 |
|
1367 // Ensure that CSS transitions don't continue on an element at a |
|
1368 // different place in the tree (even if reinserted before next |
|
1369 // animation refresh). |
|
1370 // FIXME (Bug 522599): Need a test for this. |
|
1371 if (HasFlag(NODE_HAS_PROPERTIES)) { |
|
1372 DeleteProperty(nsGkAtoms::transitionsOfBeforeProperty); |
|
1373 DeleteProperty(nsGkAtoms::transitionsOfAfterProperty); |
|
1374 DeleteProperty(nsGkAtoms::transitionsProperty); |
|
1375 DeleteProperty(nsGkAtoms::animationsOfBeforeProperty); |
|
1376 DeleteProperty(nsGkAtoms::animationsOfAfterProperty); |
|
1377 DeleteProperty(nsGkAtoms::animationsProperty); |
|
1378 } |
|
1379 |
|
1380 // Unset this since that's what the old code effectively did. |
|
1381 UnsetFlags(NODE_FORCE_XBL_BINDINGS | NODE_IS_IN_SHADOW_TREE); |
|
1382 |
|
1383 #ifdef MOZ_XUL |
|
1384 nsXULElement* xulElem = nsXULElement::FromContent(this); |
|
1385 if (xulElem) { |
|
1386 xulElem->SetXULBindingParent(nullptr); |
|
1387 } |
|
1388 else |
|
1389 #endif |
|
1390 { |
|
1391 nsDOMSlots *slots = GetExistingDOMSlots(); |
|
1392 if (slots) { |
|
1393 slots->mBindingParent = nullptr; |
|
1394 slots->mContainingShadow = nullptr; |
|
1395 } |
|
1396 } |
|
1397 |
|
1398 // This has to be here, rather than in nsGenericHTMLElement::UnbindFromTree, |
|
1399 // because it has to happen after unsetting the parent pointer, but before |
|
1400 // recursively unbinding the kids. |
|
1401 if (IsHTML()) { |
|
1402 ResetDir(this); |
|
1403 } |
|
1404 |
|
1405 if (aDeep) { |
|
1406 // Do the kids. Don't call GetChildCount() here since that'll force |
|
1407 // XUL to generate template children, which there is no need for since |
|
1408 // all we're going to do is unbind them anyway. |
|
1409 uint32_t i, n = mAttrsAndChildren.ChildCount(); |
|
1410 |
|
1411 for (i = 0; i < n; ++i) { |
|
1412 // Note that we pass false for aNullParent here, since we don't want |
|
1413 // the kids to forget us. We _do_ want them to forget their binding |
|
1414 // parent, though, since this only walks non-anonymous kids. |
|
1415 mAttrsAndChildren.ChildAt(i)->UnbindFromTree(true, false); |
|
1416 } |
|
1417 } |
|
1418 |
|
1419 nsNodeUtils::ParentChainChanged(this); |
|
1420 } |
|
1421 |
|
1422 nsICSSDeclaration* |
|
1423 Element::GetSMILOverrideStyle() |
|
1424 { |
|
1425 Element::nsDOMSlots *slots = DOMSlots(); |
|
1426 |
|
1427 if (!slots->mSMILOverrideStyle) { |
|
1428 slots->mSMILOverrideStyle = new nsDOMCSSAttributeDeclaration(this, true); |
|
1429 } |
|
1430 |
|
1431 return slots->mSMILOverrideStyle; |
|
1432 } |
|
1433 |
|
1434 css::StyleRule* |
|
1435 Element::GetSMILOverrideStyleRule() |
|
1436 { |
|
1437 Element::nsDOMSlots *slots = GetExistingDOMSlots(); |
|
1438 return slots ? slots->mSMILOverrideStyleRule.get() : nullptr; |
|
1439 } |
|
1440 |
|
1441 nsresult |
|
1442 Element::SetSMILOverrideStyleRule(css::StyleRule* aStyleRule, |
|
1443 bool aNotify) |
|
1444 { |
|
1445 Element::nsDOMSlots *slots = DOMSlots(); |
|
1446 |
|
1447 slots->mSMILOverrideStyleRule = aStyleRule; |
|
1448 |
|
1449 if (aNotify) { |
|
1450 nsIDocument* doc = GetCurrentDoc(); |
|
1451 // Only need to request a restyle if we're in a document. (We might not |
|
1452 // be in a document, if we're clearing animation effects on a target node |
|
1453 // that's been detached since the previous animation sample.) |
|
1454 if (doc) { |
|
1455 nsCOMPtr<nsIPresShell> shell = doc->GetShell(); |
|
1456 if (shell) { |
|
1457 shell->RestyleForAnimation(this, eRestyle_Self); |
|
1458 } |
|
1459 } |
|
1460 } |
|
1461 |
|
1462 return NS_OK; |
|
1463 } |
|
1464 |
|
1465 bool |
|
1466 Element::IsLabelable() const |
|
1467 { |
|
1468 return false; |
|
1469 } |
|
1470 |
|
1471 css::StyleRule* |
|
1472 Element::GetInlineStyleRule() |
|
1473 { |
|
1474 return nullptr; |
|
1475 } |
|
1476 |
|
1477 nsresult |
|
1478 Element::SetInlineStyleRule(css::StyleRule* aStyleRule, |
|
1479 const nsAString* aSerialized, |
|
1480 bool aNotify) |
|
1481 { |
|
1482 NS_NOTYETIMPLEMENTED("Element::SetInlineStyleRule"); |
|
1483 return NS_ERROR_NOT_IMPLEMENTED; |
|
1484 } |
|
1485 |
|
1486 NS_IMETHODIMP_(bool) |
|
1487 Element::IsAttributeMapped(const nsIAtom* aAttribute) const |
|
1488 { |
|
1489 return false; |
|
1490 } |
|
1491 |
|
1492 nsChangeHint |
|
1493 Element::GetAttributeChangeHint(const nsIAtom* aAttribute, |
|
1494 int32_t aModType) const |
|
1495 { |
|
1496 return nsChangeHint(0); |
|
1497 } |
|
1498 |
|
1499 nsIAtom * |
|
1500 Element::GetClassAttributeName() const |
|
1501 { |
|
1502 return nullptr; |
|
1503 } |
|
1504 |
|
1505 bool |
|
1506 Element::FindAttributeDependence(const nsIAtom* aAttribute, |
|
1507 const MappedAttributeEntry* const aMaps[], |
|
1508 uint32_t aMapCount) |
|
1509 { |
|
1510 for (uint32_t mapindex = 0; mapindex < aMapCount; ++mapindex) { |
|
1511 for (const MappedAttributeEntry* map = aMaps[mapindex]; |
|
1512 map->attribute; ++map) { |
|
1513 if (aAttribute == *map->attribute) { |
|
1514 return true; |
|
1515 } |
|
1516 } |
|
1517 } |
|
1518 |
|
1519 return false; |
|
1520 } |
|
1521 |
|
1522 already_AddRefed<nsINodeInfo> |
|
1523 Element::GetExistingAttrNameFromQName(const nsAString& aStr) const |
|
1524 { |
|
1525 const nsAttrName* name = InternalGetExistingAttrNameFromQName(aStr); |
|
1526 if (!name) { |
|
1527 return nullptr; |
|
1528 } |
|
1529 |
|
1530 nsCOMPtr<nsINodeInfo> nodeInfo; |
|
1531 if (name->IsAtom()) { |
|
1532 nodeInfo = mNodeInfo->NodeInfoManager()-> |
|
1533 GetNodeInfo(name->Atom(), nullptr, kNameSpaceID_None, |
|
1534 nsIDOMNode::ATTRIBUTE_NODE); |
|
1535 } |
|
1536 else { |
|
1537 nodeInfo = name->NodeInfo(); |
|
1538 } |
|
1539 |
|
1540 return nodeInfo.forget(); |
|
1541 } |
|
1542 |
|
1543 // static |
|
1544 bool |
|
1545 Element::ShouldBlur(nsIContent *aContent) |
|
1546 { |
|
1547 // Determine if the current element is focused, if it is not focused |
|
1548 // then we should not try to blur |
|
1549 nsIDocument *document = aContent->GetDocument(); |
|
1550 if (!document) |
|
1551 return false; |
|
1552 |
|
1553 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(document->GetWindow()); |
|
1554 if (!window) |
|
1555 return false; |
|
1556 |
|
1557 nsCOMPtr<nsPIDOMWindow> focusedFrame; |
|
1558 nsIContent* contentToBlur = |
|
1559 nsFocusManager::GetFocusedDescendant(window, false, getter_AddRefs(focusedFrame)); |
|
1560 if (contentToBlur == aContent) |
|
1561 return true; |
|
1562 |
|
1563 // if focus on this element would get redirected, then check the redirected |
|
1564 // content as well when blurring. |
|
1565 return (contentToBlur && nsFocusManager::GetRedirectedFocus(aContent) == contentToBlur); |
|
1566 } |
|
1567 |
|
1568 bool |
|
1569 Element::IsNodeOfType(uint32_t aFlags) const |
|
1570 { |
|
1571 return !(aFlags & ~eCONTENT); |
|
1572 } |
|
1573 |
|
1574 /* static */ |
|
1575 nsresult |
|
1576 Element::DispatchEvent(nsPresContext* aPresContext, |
|
1577 WidgetEvent* aEvent, |
|
1578 nsIContent* aTarget, |
|
1579 bool aFullDispatch, |
|
1580 nsEventStatus* aStatus) |
|
1581 { |
|
1582 NS_PRECONDITION(aTarget, "Must have target"); |
|
1583 NS_PRECONDITION(aEvent, "Must have source event"); |
|
1584 NS_PRECONDITION(aStatus, "Null out param?"); |
|
1585 |
|
1586 if (!aPresContext) { |
|
1587 return NS_OK; |
|
1588 } |
|
1589 |
|
1590 nsCOMPtr<nsIPresShell> shell = aPresContext->GetPresShell(); |
|
1591 if (!shell) { |
|
1592 return NS_OK; |
|
1593 } |
|
1594 |
|
1595 if (aFullDispatch) { |
|
1596 return shell->HandleEventWithTarget(aEvent, nullptr, aTarget, aStatus); |
|
1597 } |
|
1598 |
|
1599 return shell->HandleDOMEventWithTarget(aTarget, aEvent, aStatus); |
|
1600 } |
|
1601 |
|
1602 /* static */ |
|
1603 nsresult |
|
1604 Element::DispatchClickEvent(nsPresContext* aPresContext, |
|
1605 WidgetInputEvent* aSourceEvent, |
|
1606 nsIContent* aTarget, |
|
1607 bool aFullDispatch, |
|
1608 const EventFlags* aExtraEventFlags, |
|
1609 nsEventStatus* aStatus) |
|
1610 { |
|
1611 NS_PRECONDITION(aTarget, "Must have target"); |
|
1612 NS_PRECONDITION(aSourceEvent, "Must have source event"); |
|
1613 NS_PRECONDITION(aStatus, "Null out param?"); |
|
1614 |
|
1615 WidgetMouseEvent event(aSourceEvent->mFlags.mIsTrusted, NS_MOUSE_CLICK, |
|
1616 aSourceEvent->widget, WidgetMouseEvent::eReal); |
|
1617 event.refPoint = aSourceEvent->refPoint; |
|
1618 uint32_t clickCount = 1; |
|
1619 float pressure = 0; |
|
1620 uint16_t inputSource = 0; |
|
1621 WidgetMouseEvent* sourceMouseEvent = aSourceEvent->AsMouseEvent(); |
|
1622 if (sourceMouseEvent) { |
|
1623 clickCount = sourceMouseEvent->clickCount; |
|
1624 pressure = sourceMouseEvent->pressure; |
|
1625 inputSource = sourceMouseEvent->inputSource; |
|
1626 } else if (aSourceEvent->eventStructType == NS_KEY_EVENT) { |
|
1627 inputSource = nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD; |
|
1628 } |
|
1629 event.pressure = pressure; |
|
1630 event.clickCount = clickCount; |
|
1631 event.inputSource = inputSource; |
|
1632 event.modifiers = aSourceEvent->modifiers; |
|
1633 if (aExtraEventFlags) { |
|
1634 // Be careful not to overwrite existing flags! |
|
1635 event.mFlags.Union(*aExtraEventFlags); |
|
1636 } |
|
1637 |
|
1638 return DispatchEvent(aPresContext, &event, aTarget, aFullDispatch, aStatus); |
|
1639 } |
|
1640 |
|
1641 nsIFrame* |
|
1642 Element::GetPrimaryFrame(mozFlushType aType) |
|
1643 { |
|
1644 nsIDocument* doc = GetCurrentDoc(); |
|
1645 if (!doc) { |
|
1646 return nullptr; |
|
1647 } |
|
1648 |
|
1649 // Cause a flush, so we get up-to-date frame |
|
1650 // information |
|
1651 if (aType != Flush_None) { |
|
1652 doc->FlushPendingNotifications(aType); |
|
1653 } |
|
1654 |
|
1655 return GetPrimaryFrame(); |
|
1656 } |
|
1657 |
|
1658 //---------------------------------------------------------------------- |
|
1659 nsresult |
|
1660 Element::LeaveLink(nsPresContext* aPresContext) |
|
1661 { |
|
1662 nsILinkHandler *handler = aPresContext->GetLinkHandler(); |
|
1663 if (!handler) { |
|
1664 return NS_OK; |
|
1665 } |
|
1666 |
|
1667 return handler->OnLeaveLink(); |
|
1668 } |
|
1669 |
|
1670 nsresult |
|
1671 Element::SetEventHandler(nsIAtom* aEventName, |
|
1672 const nsAString& aValue, |
|
1673 bool aDefer) |
|
1674 { |
|
1675 nsIDocument *ownerDoc = OwnerDoc(); |
|
1676 if (ownerDoc->IsLoadedAsData()) { |
|
1677 // Make this a no-op rather than throwing an error to avoid |
|
1678 // the error causing problems setting the attribute. |
|
1679 return NS_OK; |
|
1680 } |
|
1681 |
|
1682 NS_PRECONDITION(aEventName, "Must have event name!"); |
|
1683 bool defer = true; |
|
1684 EventListenerManager* manager = |
|
1685 GetEventListenerManagerForAttr(aEventName, &defer); |
|
1686 if (!manager) { |
|
1687 return NS_OK; |
|
1688 } |
|
1689 |
|
1690 defer = defer && aDefer; // only defer if everyone agrees... |
|
1691 manager->SetEventHandler(aEventName, aValue, |
|
1692 nsIProgrammingLanguage::JAVASCRIPT, |
|
1693 defer, !nsContentUtils::IsChromeDoc(ownerDoc), |
|
1694 this); |
|
1695 return NS_OK; |
|
1696 } |
|
1697 |
|
1698 |
|
1699 //---------------------------------------------------------------------- |
|
1700 |
|
1701 const nsAttrName* |
|
1702 Element::InternalGetExistingAttrNameFromQName(const nsAString& aStr) const |
|
1703 { |
|
1704 return mAttrsAndChildren.GetExistingAttrNameFromQName(aStr); |
|
1705 } |
|
1706 |
|
1707 bool |
|
1708 Element::MaybeCheckSameAttrVal(int32_t aNamespaceID, |
|
1709 nsIAtom* aName, |
|
1710 nsIAtom* aPrefix, |
|
1711 const nsAttrValueOrString& aValue, |
|
1712 bool aNotify, |
|
1713 nsAttrValue& aOldValue, |
|
1714 uint8_t* aModType, |
|
1715 bool* aHasListeners) |
|
1716 { |
|
1717 bool modification = false; |
|
1718 *aHasListeners = aNotify && |
|
1719 nsContentUtils::HasMutationListeners(this, |
|
1720 NS_EVENT_BITS_MUTATION_ATTRMODIFIED, |
|
1721 this); |
|
1722 |
|
1723 // If we have no listeners and aNotify is false, we are almost certainly |
|
1724 // coming from the content sink and will almost certainly have no previous |
|
1725 // value. Even if we do, setting the value is cheap when we have no |
|
1726 // listeners and don't plan to notify. The check for aNotify here is an |
|
1727 // optimization, the check for *aHasListeners is a correctness issue. |
|
1728 if (*aHasListeners || aNotify) { |
|
1729 nsAttrInfo info(GetAttrInfo(aNamespaceID, aName)); |
|
1730 if (info.mValue) { |
|
1731 // Check whether the old value is the same as the new one. Note that we |
|
1732 // only need to actually _get_ the old value if we have listeners or |
|
1733 // if the element is a custom element (because it may have an |
|
1734 // attribute changed callback). |
|
1735 if (*aHasListeners || GetCustomElementData()) { |
|
1736 // Need to store the old value. |
|
1737 // |
|
1738 // If the current attribute value contains a pointer to some other data |
|
1739 // structure that gets updated in the process of setting the attribute |
|
1740 // we'll no longer have the old value of the attribute. Therefore, we |
|
1741 // should serialize the attribute value now to keep a snapshot. |
|
1742 // |
|
1743 // We have to serialize the value anyway in order to create the |
|
1744 // mutation event so there's no cost in doing it now. |
|
1745 aOldValue.SetToSerialized(*info.mValue); |
|
1746 } |
|
1747 bool valueMatches = aValue.EqualsAsStrings(*info.mValue); |
|
1748 if (valueMatches && aPrefix == info.mName->GetPrefix()) { |
|
1749 return true; |
|
1750 } |
|
1751 modification = true; |
|
1752 } |
|
1753 } |
|
1754 *aModType = modification ? |
|
1755 static_cast<uint8_t>(nsIDOMMutationEvent::MODIFICATION) : |
|
1756 static_cast<uint8_t>(nsIDOMMutationEvent::ADDITION); |
|
1757 return false; |
|
1758 } |
|
1759 |
|
1760 bool |
|
1761 Element::OnlyNotifySameValueSet(int32_t aNamespaceID, nsIAtom* aName, |
|
1762 nsIAtom* aPrefix, |
|
1763 const nsAttrValueOrString& aValue, |
|
1764 bool aNotify, nsAttrValue& aOldValue, |
|
1765 uint8_t* aModType, bool* aHasListeners) |
|
1766 { |
|
1767 if (!MaybeCheckSameAttrVal(aNamespaceID, aName, aPrefix, aValue, aNotify, |
|
1768 aOldValue, aModType, aHasListeners)) { |
|
1769 return false; |
|
1770 } |
|
1771 |
|
1772 nsAutoScriptBlocker scriptBlocker; |
|
1773 nsNodeUtils::AttributeSetToCurrentValue(this, aNamespaceID, aName); |
|
1774 return true; |
|
1775 } |
|
1776 |
|
1777 nsresult |
|
1778 Element::SetAttr(int32_t aNamespaceID, nsIAtom* aName, |
|
1779 nsIAtom* aPrefix, const nsAString& aValue, |
|
1780 bool aNotify) |
|
1781 { |
|
1782 // Keep this in sync with SetParsedAttr below |
|
1783 |
|
1784 NS_ENSURE_ARG_POINTER(aName); |
|
1785 NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown, |
|
1786 "Don't call SetAttr with unknown namespace"); |
|
1787 |
|
1788 if (!mAttrsAndChildren.CanFitMoreAttrs()) { |
|
1789 return NS_ERROR_FAILURE; |
|
1790 } |
|
1791 |
|
1792 uint8_t modType; |
|
1793 bool hasListeners; |
|
1794 nsAttrValueOrString value(aValue); |
|
1795 nsAttrValue oldValue; |
|
1796 |
|
1797 if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, value, aNotify, |
|
1798 oldValue, &modType, &hasListeners)) { |
|
1799 return NS_OK; |
|
1800 } |
|
1801 |
|
1802 nsresult rv = BeforeSetAttr(aNamespaceID, aName, &value, aNotify); |
|
1803 NS_ENSURE_SUCCESS(rv, rv); |
|
1804 |
|
1805 if (aNotify) { |
|
1806 nsNodeUtils::AttributeWillChange(this, aNamespaceID, aName, modType); |
|
1807 } |
|
1808 |
|
1809 // Hold a script blocker while calling ParseAttribute since that can call |
|
1810 // out to id-observers |
|
1811 nsAutoScriptBlocker scriptBlocker; |
|
1812 |
|
1813 nsAttrValue attrValue; |
|
1814 if (!ParseAttribute(aNamespaceID, aName, aValue, attrValue)) { |
|
1815 attrValue.SetTo(aValue); |
|
1816 } |
|
1817 |
|
1818 return SetAttrAndNotify(aNamespaceID, aName, aPrefix, oldValue, |
|
1819 attrValue, modType, hasListeners, aNotify, |
|
1820 kCallAfterSetAttr); |
|
1821 } |
|
1822 |
|
1823 nsresult |
|
1824 Element::SetParsedAttr(int32_t aNamespaceID, nsIAtom* aName, |
|
1825 nsIAtom* aPrefix, nsAttrValue& aParsedValue, |
|
1826 bool aNotify) |
|
1827 { |
|
1828 // Keep this in sync with SetAttr above |
|
1829 |
|
1830 NS_ENSURE_ARG_POINTER(aName); |
|
1831 NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown, |
|
1832 "Don't call SetAttr with unknown namespace"); |
|
1833 |
|
1834 if (!mAttrsAndChildren.CanFitMoreAttrs()) { |
|
1835 return NS_ERROR_FAILURE; |
|
1836 } |
|
1837 |
|
1838 |
|
1839 uint8_t modType; |
|
1840 bool hasListeners; |
|
1841 nsAttrValueOrString value(aParsedValue); |
|
1842 nsAttrValue oldValue; |
|
1843 |
|
1844 if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, value, aNotify, |
|
1845 oldValue, &modType, &hasListeners)) { |
|
1846 return NS_OK; |
|
1847 } |
|
1848 |
|
1849 nsresult rv = BeforeSetAttr(aNamespaceID, aName, &value, aNotify); |
|
1850 NS_ENSURE_SUCCESS(rv, rv); |
|
1851 |
|
1852 if (aNotify) { |
|
1853 nsNodeUtils::AttributeWillChange(this, aNamespaceID, aName, modType); |
|
1854 } |
|
1855 |
|
1856 return SetAttrAndNotify(aNamespaceID, aName, aPrefix, oldValue, |
|
1857 aParsedValue, modType, hasListeners, aNotify, |
|
1858 kCallAfterSetAttr); |
|
1859 } |
|
1860 |
|
1861 nsresult |
|
1862 Element::SetAttrAndNotify(int32_t aNamespaceID, |
|
1863 nsIAtom* aName, |
|
1864 nsIAtom* aPrefix, |
|
1865 const nsAttrValue& aOldValue, |
|
1866 nsAttrValue& aParsedValue, |
|
1867 uint8_t aModType, |
|
1868 bool aFireMutation, |
|
1869 bool aNotify, |
|
1870 bool aCallAfterSetAttr) |
|
1871 { |
|
1872 nsresult rv; |
|
1873 |
|
1874 nsIDocument* document = GetCurrentDoc(); |
|
1875 mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify); |
|
1876 |
|
1877 nsMutationGuard::DidMutate(); |
|
1878 |
|
1879 // Copy aParsedValue for later use since it will be lost when we call |
|
1880 // SetAndTakeMappedAttr below |
|
1881 nsAttrValue aValueForAfterSetAttr; |
|
1882 if (aCallAfterSetAttr) { |
|
1883 aValueForAfterSetAttr.SetTo(aParsedValue); |
|
1884 } |
|
1885 |
|
1886 bool hadValidDir = false; |
|
1887 bool hadDirAuto = false; |
|
1888 |
|
1889 if (aNamespaceID == kNameSpaceID_None) { |
|
1890 if (aName == nsGkAtoms::dir) { |
|
1891 hadValidDir = HasValidDir() || IsHTML(nsGkAtoms::bdi); |
|
1892 hadDirAuto = HasDirAuto(); // already takes bdi into account |
|
1893 } |
|
1894 |
|
1895 // XXXbz Perhaps we should push up the attribute mapping function |
|
1896 // stuff to Element? |
|
1897 if (!IsAttributeMapped(aName) || |
|
1898 !SetMappedAttribute(document, aName, aParsedValue, &rv)) { |
|
1899 rv = mAttrsAndChildren.SetAndTakeAttr(aName, aParsedValue); |
|
1900 } |
|
1901 } |
|
1902 else { |
|
1903 nsCOMPtr<nsINodeInfo> ni; |
|
1904 ni = mNodeInfo->NodeInfoManager()->GetNodeInfo(aName, aPrefix, |
|
1905 aNamespaceID, |
|
1906 nsIDOMNode::ATTRIBUTE_NODE); |
|
1907 |
|
1908 rv = mAttrsAndChildren.SetAndTakeAttr(ni, aParsedValue); |
|
1909 } |
|
1910 NS_ENSURE_SUCCESS(rv, rv); |
|
1911 |
|
1912 if (document || HasFlag(NODE_FORCE_XBL_BINDINGS)) { |
|
1913 nsRefPtr<nsXBLBinding> binding = GetXBLBinding(); |
|
1914 if (binding) { |
|
1915 binding->AttributeChanged(aName, aNamespaceID, false, aNotify); |
|
1916 } |
|
1917 } |
|
1918 |
|
1919 UpdateState(aNotify); |
|
1920 |
|
1921 nsIDocument* ownerDoc = OwnerDoc(); |
|
1922 if (ownerDoc && GetCustomElementData()) { |
|
1923 nsCOMPtr<nsIAtom> oldValueAtom = aOldValue.GetAsAtom(); |
|
1924 nsCOMPtr<nsIAtom> newValueAtom = aValueForAfterSetAttr.GetAsAtom(); |
|
1925 LifecycleCallbackArgs args = { |
|
1926 nsDependentAtomString(aName), |
|
1927 aModType == nsIDOMMutationEvent::ADDITION ? |
|
1928 NullString() : nsDependentAtomString(oldValueAtom), |
|
1929 nsDependentAtomString(newValueAtom) |
|
1930 }; |
|
1931 |
|
1932 ownerDoc->EnqueueLifecycleCallback(nsIDocument::eAttributeChanged, this, &args); |
|
1933 } |
|
1934 |
|
1935 if (aCallAfterSetAttr) { |
|
1936 rv = AfterSetAttr(aNamespaceID, aName, &aValueForAfterSetAttr, aNotify); |
|
1937 NS_ENSURE_SUCCESS(rv, rv); |
|
1938 |
|
1939 if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) { |
|
1940 OnSetDirAttr(this, &aValueForAfterSetAttr, |
|
1941 hadValidDir, hadDirAuto, aNotify); |
|
1942 } |
|
1943 } |
|
1944 |
|
1945 if (aNotify) { |
|
1946 nsNodeUtils::AttributeChanged(this, aNamespaceID, aName, aModType); |
|
1947 } |
|
1948 |
|
1949 if (aFireMutation) { |
|
1950 InternalMutationEvent mutation(true, NS_MUTATION_ATTRMODIFIED); |
|
1951 |
|
1952 nsAutoString ns; |
|
1953 nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNamespaceID, ns); |
|
1954 Attr* attrNode = |
|
1955 GetAttributeNodeNSInternal(ns, nsDependentAtomString(aName)); |
|
1956 mutation.mRelatedNode = attrNode; |
|
1957 |
|
1958 mutation.mAttrName = aName; |
|
1959 nsAutoString newValue; |
|
1960 GetAttr(aNamespaceID, aName, newValue); |
|
1961 if (!newValue.IsEmpty()) { |
|
1962 mutation.mNewAttrValue = do_GetAtom(newValue); |
|
1963 } |
|
1964 if (!aOldValue.IsEmptyString()) { |
|
1965 mutation.mPrevAttrValue = aOldValue.GetAsAtom(); |
|
1966 } |
|
1967 mutation.mAttrChange = aModType; |
|
1968 |
|
1969 mozAutoSubtreeModified subtree(OwnerDoc(), this); |
|
1970 (new AsyncEventDispatcher(this, mutation))->RunDOMEventWhenSafe(); |
|
1971 } |
|
1972 |
|
1973 return NS_OK; |
|
1974 } |
|
1975 |
|
1976 bool |
|
1977 Element::ParseAttribute(int32_t aNamespaceID, |
|
1978 nsIAtom* aAttribute, |
|
1979 const nsAString& aValue, |
|
1980 nsAttrValue& aResult) |
|
1981 { |
|
1982 return false; |
|
1983 } |
|
1984 |
|
1985 bool |
|
1986 Element::SetMappedAttribute(nsIDocument* aDocument, |
|
1987 nsIAtom* aName, |
|
1988 nsAttrValue& aValue, |
|
1989 nsresult* aRetval) |
|
1990 { |
|
1991 *aRetval = NS_OK; |
|
1992 return false; |
|
1993 } |
|
1994 |
|
1995 EventListenerManager* |
|
1996 Element::GetEventListenerManagerForAttr(nsIAtom* aAttrName, |
|
1997 bool* aDefer) |
|
1998 { |
|
1999 *aDefer = true; |
|
2000 return GetOrCreateListenerManager(); |
|
2001 } |
|
2002 |
|
2003 Element::nsAttrInfo |
|
2004 Element::GetAttrInfo(int32_t aNamespaceID, nsIAtom* aName) const |
|
2005 { |
|
2006 NS_ASSERTION(nullptr != aName, "must have attribute name"); |
|
2007 NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown, |
|
2008 "must have a real namespace ID!"); |
|
2009 |
|
2010 int32_t index = mAttrsAndChildren.IndexOfAttr(aName, aNamespaceID); |
|
2011 if (index >= 0) { |
|
2012 return nsAttrInfo(mAttrsAndChildren.AttrNameAt(index), |
|
2013 mAttrsAndChildren.AttrAt(index)); |
|
2014 } |
|
2015 |
|
2016 return nsAttrInfo(nullptr, nullptr); |
|
2017 } |
|
2018 |
|
2019 |
|
2020 bool |
|
2021 Element::GetAttr(int32_t aNameSpaceID, nsIAtom* aName, |
|
2022 nsAString& aResult) const |
|
2023 { |
|
2024 DOMString str; |
|
2025 bool haveAttr = GetAttr(aNameSpaceID, aName, str); |
|
2026 str.ToString(aResult); |
|
2027 return haveAttr; |
|
2028 } |
|
2029 |
|
2030 int32_t |
|
2031 Element::FindAttrValueIn(int32_t aNameSpaceID, |
|
2032 nsIAtom* aName, |
|
2033 AttrValuesArray* aValues, |
|
2034 nsCaseTreatment aCaseSensitive) const |
|
2035 { |
|
2036 NS_ASSERTION(aName, "Must have attr name"); |
|
2037 NS_ASSERTION(aNameSpaceID != kNameSpaceID_Unknown, "Must have namespace"); |
|
2038 NS_ASSERTION(aValues, "Null value array"); |
|
2039 |
|
2040 const nsAttrValue* val = mAttrsAndChildren.GetAttr(aName, aNameSpaceID); |
|
2041 if (val) { |
|
2042 for (int32_t i = 0; aValues[i]; ++i) { |
|
2043 if (val->Equals(*aValues[i], aCaseSensitive)) { |
|
2044 return i; |
|
2045 } |
|
2046 } |
|
2047 return ATTR_VALUE_NO_MATCH; |
|
2048 } |
|
2049 return ATTR_MISSING; |
|
2050 } |
|
2051 |
|
2052 nsresult |
|
2053 Element::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName, |
|
2054 bool aNotify) |
|
2055 { |
|
2056 NS_ASSERTION(nullptr != aName, "must have attribute name"); |
|
2057 |
|
2058 int32_t index = mAttrsAndChildren.IndexOfAttr(aName, aNameSpaceID); |
|
2059 if (index < 0) { |
|
2060 return NS_OK; |
|
2061 } |
|
2062 |
|
2063 nsresult rv = BeforeSetAttr(aNameSpaceID, aName, nullptr, aNotify); |
|
2064 NS_ENSURE_SUCCESS(rv, rv); |
|
2065 |
|
2066 nsIDocument *document = GetCurrentDoc(); |
|
2067 mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify); |
|
2068 |
|
2069 if (aNotify) { |
|
2070 nsNodeUtils::AttributeWillChange(this, aNameSpaceID, aName, |
|
2071 nsIDOMMutationEvent::REMOVAL); |
|
2072 } |
|
2073 |
|
2074 bool hasMutationListeners = aNotify && |
|
2075 nsContentUtils::HasMutationListeners(this, |
|
2076 NS_EVENT_BITS_MUTATION_ATTRMODIFIED, |
|
2077 this); |
|
2078 |
|
2079 // Grab the attr node if needed before we remove it from the attr map |
|
2080 nsRefPtr<Attr> attrNode; |
|
2081 if (hasMutationListeners) { |
|
2082 nsAutoString ns; |
|
2083 nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID, ns); |
|
2084 attrNode = GetAttributeNodeNSInternal(ns, nsDependentAtomString(aName)); |
|
2085 } |
|
2086 |
|
2087 // Clear binding to nsIDOMMozNamedAttrMap |
|
2088 nsDOMSlots *slots = GetExistingDOMSlots(); |
|
2089 if (slots && slots->mAttributeMap) { |
|
2090 slots->mAttributeMap->DropAttribute(aNameSpaceID, aName); |
|
2091 } |
|
2092 |
|
2093 // The id-handling code, and in the future possibly other code, need to |
|
2094 // react to unexpected attribute changes. |
|
2095 nsMutationGuard::DidMutate(); |
|
2096 |
|
2097 bool hadValidDir = false; |
|
2098 bool hadDirAuto = false; |
|
2099 |
|
2100 if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) { |
|
2101 hadValidDir = HasValidDir() || IsHTML(nsGkAtoms::bdi); |
|
2102 hadDirAuto = HasDirAuto(); // already takes bdi into account |
|
2103 } |
|
2104 |
|
2105 nsAttrValue oldValue; |
|
2106 rv = mAttrsAndChildren.RemoveAttrAt(index, oldValue); |
|
2107 NS_ENSURE_SUCCESS(rv, rv); |
|
2108 |
|
2109 if (document || HasFlag(NODE_FORCE_XBL_BINDINGS)) { |
|
2110 nsRefPtr<nsXBLBinding> binding = GetXBLBinding(); |
|
2111 if (binding) { |
|
2112 binding->AttributeChanged(aName, aNameSpaceID, true, aNotify); |
|
2113 } |
|
2114 } |
|
2115 |
|
2116 UpdateState(aNotify); |
|
2117 |
|
2118 nsIDocument* ownerDoc = OwnerDoc(); |
|
2119 if (ownerDoc && GetCustomElementData()) { |
|
2120 nsCOMPtr<nsIAtom> oldValueAtom = oldValue.GetAsAtom(); |
|
2121 LifecycleCallbackArgs args = { |
|
2122 nsDependentAtomString(aName), |
|
2123 nsDependentAtomString(oldValueAtom), |
|
2124 NullString() |
|
2125 }; |
|
2126 |
|
2127 ownerDoc->EnqueueLifecycleCallback(nsIDocument::eAttributeChanged, this, &args); |
|
2128 } |
|
2129 |
|
2130 if (aNotify) { |
|
2131 nsNodeUtils::AttributeChanged(this, aNameSpaceID, aName, |
|
2132 nsIDOMMutationEvent::REMOVAL); |
|
2133 } |
|
2134 |
|
2135 rv = AfterSetAttr(aNameSpaceID, aName, nullptr, aNotify); |
|
2136 NS_ENSURE_SUCCESS(rv, rv); |
|
2137 |
|
2138 if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) { |
|
2139 OnSetDirAttr(this, nullptr, hadValidDir, hadDirAuto, aNotify); |
|
2140 } |
|
2141 |
|
2142 if (hasMutationListeners) { |
|
2143 InternalMutationEvent mutation(true, NS_MUTATION_ATTRMODIFIED); |
|
2144 |
|
2145 mutation.mRelatedNode = attrNode; |
|
2146 mutation.mAttrName = aName; |
|
2147 |
|
2148 nsAutoString value; |
|
2149 oldValue.ToString(value); |
|
2150 if (!value.IsEmpty()) |
|
2151 mutation.mPrevAttrValue = do_GetAtom(value); |
|
2152 mutation.mAttrChange = nsIDOMMutationEvent::REMOVAL; |
|
2153 |
|
2154 mozAutoSubtreeModified subtree(OwnerDoc(), this); |
|
2155 (new AsyncEventDispatcher(this, mutation))->RunDOMEventWhenSafe(); |
|
2156 } |
|
2157 |
|
2158 return NS_OK; |
|
2159 } |
|
2160 |
|
2161 const nsAttrName* |
|
2162 Element::GetAttrNameAt(uint32_t aIndex) const |
|
2163 { |
|
2164 return mAttrsAndChildren.GetSafeAttrNameAt(aIndex); |
|
2165 } |
|
2166 |
|
2167 uint32_t |
|
2168 Element::GetAttrCount() const |
|
2169 { |
|
2170 return mAttrsAndChildren.AttrCount(); |
|
2171 } |
|
2172 |
|
2173 void |
|
2174 Element::DescribeAttribute(uint32_t index, nsAString& aOutDescription) const |
|
2175 { |
|
2176 // name |
|
2177 mAttrsAndChildren.AttrNameAt(index)->GetQualifiedName(aOutDescription); |
|
2178 |
|
2179 // value |
|
2180 aOutDescription.AppendLiteral("=\""); |
|
2181 nsAutoString value; |
|
2182 mAttrsAndChildren.AttrAt(index)->ToString(value); |
|
2183 for (int i = value.Length(); i >= 0; --i) { |
|
2184 if (value[i] == char16_t('"')) |
|
2185 value.Insert(char16_t('\\'), uint32_t(i)); |
|
2186 } |
|
2187 aOutDescription.Append(value); |
|
2188 aOutDescription.AppendLiteral("\""); |
|
2189 } |
|
2190 |
|
2191 #ifdef DEBUG |
|
2192 void |
|
2193 Element::ListAttributes(FILE* out) const |
|
2194 { |
|
2195 uint32_t index, count = mAttrsAndChildren.AttrCount(); |
|
2196 for (index = 0; index < count; index++) { |
|
2197 nsAutoString attributeDescription; |
|
2198 DescribeAttribute(index, attributeDescription); |
|
2199 |
|
2200 fputs(" ", out); |
|
2201 fputs(NS_LossyConvertUTF16toASCII(attributeDescription).get(), out); |
|
2202 } |
|
2203 } |
|
2204 |
|
2205 void |
|
2206 Element::List(FILE* out, int32_t aIndent, |
|
2207 const nsCString& aPrefix) const |
|
2208 { |
|
2209 int32_t indent; |
|
2210 for (indent = aIndent; --indent >= 0; ) fputs(" ", out); |
|
2211 |
|
2212 fputs(aPrefix.get(), out); |
|
2213 |
|
2214 fputs(NS_LossyConvertUTF16toASCII(mNodeInfo->QualifiedName()).get(), out); |
|
2215 |
|
2216 fprintf(out, "@%p", (void *)this); |
|
2217 |
|
2218 ListAttributes(out); |
|
2219 |
|
2220 fprintf(out, " state=[%llx]", |
|
2221 static_cast<unsigned long long>(State().GetInternalValue())); |
|
2222 fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags())); |
|
2223 if (IsCommonAncestorForRangeInSelection()) { |
|
2224 nsRange::RangeHashTable* ranges = |
|
2225 static_cast<nsRange::RangeHashTable*>(GetProperty(nsGkAtoms::range)); |
|
2226 fprintf(out, " ranges:%d", ranges ? ranges->Count() : 0); |
|
2227 } |
|
2228 fprintf(out, " primaryframe=%p", static_cast<void*>(GetPrimaryFrame())); |
|
2229 fprintf(out, " refcount=%" PRIuPTR "<", mRefCnt.get()); |
|
2230 |
|
2231 nsIContent* child = GetFirstChild(); |
|
2232 if (child) { |
|
2233 fputs("\n", out); |
|
2234 |
|
2235 for (; child; child = child->GetNextSibling()) { |
|
2236 child->List(out, aIndent + 1); |
|
2237 } |
|
2238 |
|
2239 for (indent = aIndent; --indent >= 0; ) fputs(" ", out); |
|
2240 } |
|
2241 |
|
2242 fputs(">\n", out); |
|
2243 |
|
2244 Element* nonConstThis = const_cast<Element*>(this); |
|
2245 |
|
2246 // XXX sXBL/XBL2 issue! Owner or current document? |
|
2247 nsIDocument *document = OwnerDoc(); |
|
2248 |
|
2249 // Note: not listing nsIAnonymousContentCreator-created content... |
|
2250 |
|
2251 nsBindingManager* bindingManager = document->BindingManager(); |
|
2252 nsCOMPtr<nsIDOMNodeList> anonymousChildren; |
|
2253 bindingManager->GetAnonymousNodesFor(nonConstThis, |
|
2254 getter_AddRefs(anonymousChildren)); |
|
2255 |
|
2256 if (anonymousChildren) { |
|
2257 uint32_t length = 0; |
|
2258 anonymousChildren->GetLength(&length); |
|
2259 |
|
2260 for (indent = aIndent; --indent >= 0; ) fputs(" ", out); |
|
2261 fputs("anonymous-children<\n", out); |
|
2262 |
|
2263 for (uint32_t i = 0; i < length; ++i) { |
|
2264 nsCOMPtr<nsIDOMNode> node; |
|
2265 anonymousChildren->Item(i, getter_AddRefs(node)); |
|
2266 nsCOMPtr<nsIContent> child = do_QueryInterface(node); |
|
2267 child->List(out, aIndent + 1); |
|
2268 } |
|
2269 |
|
2270 for (indent = aIndent; --indent >= 0; ) fputs(" ", out); |
|
2271 fputs(">\n", out); |
|
2272 |
|
2273 bool outHeader = false; |
|
2274 ExplicitChildIterator iter(nonConstThis); |
|
2275 for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) { |
|
2276 if (!outHeader) { |
|
2277 outHeader = true; |
|
2278 |
|
2279 for (indent = aIndent; --indent >= 0; ) fputs(" ", out); |
|
2280 fputs("content-list<\n", out); |
|
2281 } |
|
2282 |
|
2283 child->List(out, aIndent + 1); |
|
2284 } |
|
2285 |
|
2286 if (outHeader) { |
|
2287 for (indent = aIndent; --indent >= 0; ) fputs(" ", out); |
|
2288 fputs(">\n", out); |
|
2289 } |
|
2290 } |
|
2291 } |
|
2292 |
|
2293 void |
|
2294 Element::DumpContent(FILE* out, int32_t aIndent, |
|
2295 bool aDumpAll) const |
|
2296 { |
|
2297 int32_t indent; |
|
2298 for (indent = aIndent; --indent >= 0; ) fputs(" ", out); |
|
2299 |
|
2300 const nsString& buf = mNodeInfo->QualifiedName(); |
|
2301 fputs("<", out); |
|
2302 fputs(NS_LossyConvertUTF16toASCII(buf).get(), out); |
|
2303 |
|
2304 if(aDumpAll) ListAttributes(out); |
|
2305 |
|
2306 fputs(">", out); |
|
2307 |
|
2308 if(aIndent) fputs("\n", out); |
|
2309 |
|
2310 for (nsIContent* child = GetFirstChild(); |
|
2311 child; |
|
2312 child = child->GetNextSibling()) { |
|
2313 int32_t indent = aIndent ? aIndent + 1 : 0; |
|
2314 child->DumpContent(out, indent, aDumpAll); |
|
2315 } |
|
2316 for (indent = aIndent; --indent >= 0; ) fputs(" ", out); |
|
2317 fputs("</", out); |
|
2318 fputs(NS_LossyConvertUTF16toASCII(buf).get(), out); |
|
2319 fputs(">", out); |
|
2320 |
|
2321 if(aIndent) fputs("\n", out); |
|
2322 } |
|
2323 #endif |
|
2324 |
|
2325 void |
|
2326 Element::Describe(nsAString& aOutDescription) const |
|
2327 { |
|
2328 aOutDescription.Append(mNodeInfo->QualifiedName()); |
|
2329 aOutDescription.AppendPrintf("@%p", (void *)this); |
|
2330 |
|
2331 uint32_t index, count = mAttrsAndChildren.AttrCount(); |
|
2332 for (index = 0; index < count; index++) { |
|
2333 aOutDescription.Append(' '); |
|
2334 nsAutoString attributeDescription; |
|
2335 DescribeAttribute(index, attributeDescription); |
|
2336 aOutDescription.Append(attributeDescription); |
|
2337 } |
|
2338 } |
|
2339 |
|
2340 bool |
|
2341 Element::CheckHandleEventForLinksPrecondition(EventChainVisitor& aVisitor, |
|
2342 nsIURI** aURI) const |
|
2343 { |
|
2344 if (aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault || |
|
2345 (!aVisitor.mEvent->mFlags.mIsTrusted && |
|
2346 (aVisitor.mEvent->message != NS_MOUSE_CLICK) && |
|
2347 (aVisitor.mEvent->message != NS_KEY_PRESS) && |
|
2348 (aVisitor.mEvent->message != NS_UI_ACTIVATE)) || |
|
2349 !aVisitor.mPresContext || |
|
2350 aVisitor.mEvent->mFlags.mMultipleActionsPrevented) { |
|
2351 return false; |
|
2352 } |
|
2353 |
|
2354 // Make sure we actually are a link |
|
2355 return IsLink(aURI); |
|
2356 } |
|
2357 |
|
2358 nsresult |
|
2359 Element::PreHandleEventForLinks(EventChainPreVisitor& aVisitor) |
|
2360 { |
|
2361 // Optimisation: return early if this event doesn't interest us. |
|
2362 // IMPORTANT: this switch and the switch below it must be kept in sync! |
|
2363 switch (aVisitor.mEvent->message) { |
|
2364 case NS_MOUSE_ENTER_SYNTH: |
|
2365 case NS_FOCUS_CONTENT: |
|
2366 case NS_MOUSE_EXIT_SYNTH: |
|
2367 case NS_BLUR_CONTENT: |
|
2368 break; |
|
2369 default: |
|
2370 return NS_OK; |
|
2371 } |
|
2372 |
|
2373 // Make sure we meet the preconditions before continuing |
|
2374 nsCOMPtr<nsIURI> absURI; |
|
2375 if (!CheckHandleEventForLinksPrecondition(aVisitor, getter_AddRefs(absURI))) { |
|
2376 return NS_OK; |
|
2377 } |
|
2378 |
|
2379 nsresult rv = NS_OK; |
|
2380 |
|
2381 // We do the status bar updates in PreHandleEvent so that the status bar gets |
|
2382 // updated even if the event is consumed before we have a chance to set it. |
|
2383 switch (aVisitor.mEvent->message) { |
|
2384 // Set the status bar similarly for mouseover and focus |
|
2385 case NS_MOUSE_ENTER_SYNTH: |
|
2386 aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; |
|
2387 // FALL THROUGH |
|
2388 case NS_FOCUS_CONTENT: { |
|
2389 InternalFocusEvent* focusEvent = aVisitor.mEvent->AsFocusEvent(); |
|
2390 if (!focusEvent || !focusEvent->isRefocus) { |
|
2391 nsAutoString target; |
|
2392 GetLinkTarget(target); |
|
2393 nsContentUtils::TriggerLink(this, aVisitor.mPresContext, absURI, target, |
|
2394 false, true, true); |
|
2395 // Make sure any ancestor links don't also TriggerLink |
|
2396 aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true; |
|
2397 } |
|
2398 break; |
|
2399 } |
|
2400 case NS_MOUSE_EXIT_SYNTH: |
|
2401 aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; |
|
2402 // FALL THROUGH |
|
2403 case NS_BLUR_CONTENT: |
|
2404 rv = LeaveLink(aVisitor.mPresContext); |
|
2405 if (NS_SUCCEEDED(rv)) { |
|
2406 aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true; |
|
2407 } |
|
2408 break; |
|
2409 |
|
2410 default: |
|
2411 // switch not in sync with the optimization switch earlier in this function |
|
2412 NS_NOTREACHED("switch statements not in sync"); |
|
2413 return NS_ERROR_UNEXPECTED; |
|
2414 } |
|
2415 |
|
2416 return rv; |
|
2417 } |
|
2418 |
|
2419 nsresult |
|
2420 Element::PostHandleEventForLinks(EventChainPostVisitor& aVisitor) |
|
2421 { |
|
2422 // Optimisation: return early if this event doesn't interest us. |
|
2423 // IMPORTANT: this switch and the switch below it must be kept in sync! |
|
2424 switch (aVisitor.mEvent->message) { |
|
2425 case NS_MOUSE_BUTTON_DOWN: |
|
2426 case NS_MOUSE_CLICK: |
|
2427 case NS_UI_ACTIVATE: |
|
2428 case NS_KEY_PRESS: |
|
2429 break; |
|
2430 default: |
|
2431 return NS_OK; |
|
2432 } |
|
2433 |
|
2434 // Make sure we meet the preconditions before continuing |
|
2435 nsCOMPtr<nsIURI> absURI; |
|
2436 if (!CheckHandleEventForLinksPrecondition(aVisitor, getter_AddRefs(absURI))) { |
|
2437 return NS_OK; |
|
2438 } |
|
2439 |
|
2440 nsresult rv = NS_OK; |
|
2441 |
|
2442 switch (aVisitor.mEvent->message) { |
|
2443 case NS_MOUSE_BUTTON_DOWN: |
|
2444 { |
|
2445 if (aVisitor.mEvent->AsMouseEvent()->button == |
|
2446 WidgetMouseEvent::eLeftButton) { |
|
2447 // don't make the link grab the focus if there is no link handler |
|
2448 nsILinkHandler *handler = aVisitor.mPresContext->GetLinkHandler(); |
|
2449 nsIDocument *document = GetCurrentDoc(); |
|
2450 if (handler && document) { |
|
2451 nsIFocusManager* fm = nsFocusManager::GetFocusManager(); |
|
2452 if (fm) { |
|
2453 aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true; |
|
2454 nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(this); |
|
2455 fm->SetFocus(elem, nsIFocusManager::FLAG_BYMOUSE | |
|
2456 nsIFocusManager::FLAG_NOSCROLL); |
|
2457 } |
|
2458 |
|
2459 EventStateManager::SetActiveManager( |
|
2460 aVisitor.mPresContext->EventStateManager(), this); |
|
2461 } |
|
2462 } |
|
2463 } |
|
2464 break; |
|
2465 |
|
2466 case NS_MOUSE_CLICK: { |
|
2467 WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent(); |
|
2468 if (mouseEvent->IsLeftClickEvent()) { |
|
2469 if (mouseEvent->IsControl() || mouseEvent->IsMeta() || |
|
2470 mouseEvent->IsAlt() ||mouseEvent->IsShift()) { |
|
2471 break; |
|
2472 } |
|
2473 |
|
2474 // The default action is simply to dispatch DOMActivate |
|
2475 nsCOMPtr<nsIPresShell> shell = aVisitor.mPresContext->GetPresShell(); |
|
2476 if (shell) { |
|
2477 // single-click |
|
2478 nsEventStatus status = nsEventStatus_eIgnore; |
|
2479 InternalUIEvent actEvent(mouseEvent->mFlags.mIsTrusted, NS_UI_ACTIVATE); |
|
2480 actEvent.detail = 1; |
|
2481 |
|
2482 rv = shell->HandleDOMEventWithTarget(this, &actEvent, &status); |
|
2483 if (NS_SUCCEEDED(rv)) { |
|
2484 aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; |
|
2485 } |
|
2486 } |
|
2487 } |
|
2488 break; |
|
2489 } |
|
2490 case NS_UI_ACTIVATE: |
|
2491 { |
|
2492 if (aVisitor.mEvent->originalTarget == this) { |
|
2493 nsAutoString target; |
|
2494 GetLinkTarget(target); |
|
2495 nsContentUtils::TriggerLink(this, aVisitor.mPresContext, absURI, target, |
|
2496 true, true, |
|
2497 aVisitor.mEvent->mFlags.mIsTrusted); |
|
2498 aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; |
|
2499 } |
|
2500 } |
|
2501 break; |
|
2502 |
|
2503 case NS_KEY_PRESS: |
|
2504 { |
|
2505 WidgetKeyboardEvent* keyEvent = aVisitor.mEvent->AsKeyboardEvent(); |
|
2506 if (keyEvent && keyEvent->keyCode == NS_VK_RETURN) { |
|
2507 nsEventStatus status = nsEventStatus_eIgnore; |
|
2508 rv = DispatchClickEvent(aVisitor.mPresContext, keyEvent, this, |
|
2509 false, nullptr, &status); |
|
2510 if (NS_SUCCEEDED(rv)) { |
|
2511 aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; |
|
2512 } |
|
2513 } |
|
2514 } |
|
2515 break; |
|
2516 |
|
2517 default: |
|
2518 // switch not in sync with the optimization switch earlier in this function |
|
2519 NS_NOTREACHED("switch statements not in sync"); |
|
2520 return NS_ERROR_UNEXPECTED; |
|
2521 } |
|
2522 |
|
2523 return rv; |
|
2524 } |
|
2525 |
|
2526 void |
|
2527 Element::GetLinkTarget(nsAString& aTarget) |
|
2528 { |
|
2529 aTarget.Truncate(); |
|
2530 } |
|
2531 |
|
2532 bool |
|
2533 Element::MozMatchesSelector(const nsAString& aSelector, |
|
2534 ErrorResult& aError) |
|
2535 { |
|
2536 nsCSSSelectorList* selectorList = ParseSelectorList(aSelector, aError); |
|
2537 if (!selectorList) { |
|
2538 // Either we failed (and aError already has the exception), or this |
|
2539 // is a pseudo-element-only selector that matches nothing. |
|
2540 return false; |
|
2541 } |
|
2542 |
|
2543 OwnerDoc()->FlushPendingLinkUpdates(); |
|
2544 TreeMatchContext matchingContext(false, |
|
2545 nsRuleWalker::eRelevantLinkUnvisited, |
|
2546 OwnerDoc(), |
|
2547 TreeMatchContext::eNeverMatchVisited); |
|
2548 matchingContext.SetHasSpecifiedScope(); |
|
2549 matchingContext.AddScopeElement(this); |
|
2550 return nsCSSRuleProcessor::SelectorListMatches(this, matchingContext, |
|
2551 selectorList); |
|
2552 } |
|
2553 |
|
2554 static const nsAttrValue::EnumTable kCORSAttributeTable[] = { |
|
2555 // Order matters here |
|
2556 // See ParseCORSValue |
|
2557 { "anonymous", CORS_ANONYMOUS }, |
|
2558 { "use-credentials", CORS_USE_CREDENTIALS }, |
|
2559 { 0 } |
|
2560 }; |
|
2561 |
|
2562 /* static */ void |
|
2563 Element::ParseCORSValue(const nsAString& aValue, |
|
2564 nsAttrValue& aResult) |
|
2565 { |
|
2566 DebugOnly<bool> success = |
|
2567 aResult.ParseEnumValue(aValue, kCORSAttributeTable, false, |
|
2568 // default value is anonymous if aValue is |
|
2569 // not a value we understand |
|
2570 &kCORSAttributeTable[0]); |
|
2571 MOZ_ASSERT(success); |
|
2572 } |
|
2573 |
|
2574 /* static */ CORSMode |
|
2575 Element::StringToCORSMode(const nsAString& aValue) |
|
2576 { |
|
2577 if (aValue.IsVoid()) { |
|
2578 return CORS_NONE; |
|
2579 } |
|
2580 |
|
2581 nsAttrValue val; |
|
2582 Element::ParseCORSValue(aValue, val); |
|
2583 return CORSMode(val.GetEnumValue()); |
|
2584 } |
|
2585 |
|
2586 /* static */ CORSMode |
|
2587 Element::AttrValueToCORSMode(const nsAttrValue* aValue) |
|
2588 { |
|
2589 if (!aValue) { |
|
2590 return CORS_NONE; |
|
2591 } |
|
2592 |
|
2593 return CORSMode(aValue->GetEnumValue()); |
|
2594 } |
|
2595 |
|
2596 static const char* |
|
2597 GetFullScreenError(nsIDocument* aDoc) |
|
2598 { |
|
2599 // Block fullscreen requests in the chrome document when the fullscreen API |
|
2600 // is configured for content only. |
|
2601 if (nsContentUtils::IsFullscreenApiContentOnly() && |
|
2602 nsContentUtils::IsChromeDoc(aDoc)) { |
|
2603 return "FullScreenDeniedContentOnly"; |
|
2604 } |
|
2605 |
|
2606 nsCOMPtr<nsPIDOMWindow> win = aDoc->GetWindow(); |
|
2607 if (aDoc->NodePrincipal()->GetAppStatus() >= nsIPrincipal::APP_STATUS_INSTALLED) { |
|
2608 // Request is in a web app and in the same origin as the web app. |
|
2609 // Don't enforce as strict security checks for web apps, the user |
|
2610 // is supposed to have trust in them. However documents cross-origin |
|
2611 // to the web app must still confirm to the normal security checks. |
|
2612 return nullptr; |
|
2613 } |
|
2614 |
|
2615 if (!nsContentUtils::IsRequestFullScreenAllowed()) { |
|
2616 return "FullScreenDeniedNotInputDriven"; |
|
2617 } |
|
2618 |
|
2619 if (nsContentUtils::IsSitePermDeny(aDoc->NodePrincipal(), "fullscreen")) { |
|
2620 return "FullScreenDeniedBlocked"; |
|
2621 } |
|
2622 |
|
2623 return nullptr; |
|
2624 } |
|
2625 |
|
2626 void |
|
2627 Element::MozRequestFullScreen() |
|
2628 { |
|
2629 // Only grant full-screen requests if this is called from inside a trusted |
|
2630 // event handler (i.e. inside an event handler for a user initiated event). |
|
2631 // This stops the full-screen from being abused similar to the popups of old, |
|
2632 // and it also makes it harder for bad guys' script to go full-screen and |
|
2633 // spoof the browser chrome/window and phish logins etc. |
|
2634 // Note that requests for fullscreen inside a web app's origin are exempt |
|
2635 // from this restriction. |
|
2636 const char* error = GetFullScreenError(OwnerDoc()); |
|
2637 if (error) { |
|
2638 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
|
2639 NS_LITERAL_CSTRING("DOM"), OwnerDoc(), |
|
2640 nsContentUtils::eDOM_PROPERTIES, |
|
2641 error); |
|
2642 nsRefPtr<AsyncEventDispatcher> asyncDispatcher = |
|
2643 new AsyncEventDispatcher(OwnerDoc(), |
|
2644 NS_LITERAL_STRING("mozfullscreenerror"), |
|
2645 true, |
|
2646 false); |
|
2647 asyncDispatcher->PostDOMEvent(); |
|
2648 return; |
|
2649 } |
|
2650 |
|
2651 OwnerDoc()->AsyncRequestFullScreen(this); |
|
2652 |
|
2653 return; |
|
2654 } |
|
2655 |
|
2656 void |
|
2657 Element::MozRequestPointerLock() |
|
2658 { |
|
2659 OwnerDoc()->RequestPointerLock(this); |
|
2660 } |
|
2661 |
|
2662 NS_IMETHODIMP |
|
2663 Element::GetInnerHTML(nsAString& aInnerHTML) |
|
2664 { |
|
2665 GetMarkup(false, aInnerHTML); |
|
2666 return NS_OK; |
|
2667 } |
|
2668 |
|
2669 void |
|
2670 Element::SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError) |
|
2671 { |
|
2672 SetInnerHTMLInternal(aInnerHTML, aError); |
|
2673 } |
|
2674 |
|
2675 void |
|
2676 Element::GetOuterHTML(nsAString& aOuterHTML) |
|
2677 { |
|
2678 GetMarkup(true, aOuterHTML); |
|
2679 } |
|
2680 |
|
2681 void |
|
2682 Element::SetOuterHTML(const nsAString& aOuterHTML, ErrorResult& aError) |
|
2683 { |
|
2684 nsCOMPtr<nsINode> parent = GetParentNode(); |
|
2685 if (!parent) { |
|
2686 return; |
|
2687 } |
|
2688 |
|
2689 if (parent->NodeType() == nsIDOMNode::DOCUMENT_NODE) { |
|
2690 aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); |
|
2691 return; |
|
2692 } |
|
2693 |
|
2694 if (OwnerDoc()->IsHTML()) { |
|
2695 nsIAtom* localName; |
|
2696 int32_t namespaceID; |
|
2697 if (parent->IsElement()) { |
|
2698 localName = static_cast<nsIContent*>(parent.get())->Tag(); |
|
2699 namespaceID = static_cast<nsIContent*>(parent.get())->GetNameSpaceID(); |
|
2700 } else { |
|
2701 NS_ASSERTION(parent->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE, |
|
2702 "How come the parent isn't a document, a fragment or an element?"); |
|
2703 localName = nsGkAtoms::body; |
|
2704 namespaceID = kNameSpaceID_XHTML; |
|
2705 } |
|
2706 nsRefPtr<DocumentFragment> fragment = |
|
2707 new DocumentFragment(OwnerDoc()->NodeInfoManager()); |
|
2708 nsContentUtils::ParseFragmentHTML(aOuterHTML, |
|
2709 fragment, |
|
2710 localName, |
|
2711 namespaceID, |
|
2712 OwnerDoc()->GetCompatibilityMode() == |
|
2713 eCompatibility_NavQuirks, |
|
2714 true); |
|
2715 parent->ReplaceChild(*fragment, *this, aError); |
|
2716 return; |
|
2717 } |
|
2718 |
|
2719 nsCOMPtr<nsINode> context; |
|
2720 if (parent->IsElement()) { |
|
2721 context = parent; |
|
2722 } else { |
|
2723 NS_ASSERTION(parent->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE, |
|
2724 "How come the parent isn't a document, a fragment or an element?"); |
|
2725 nsCOMPtr<nsINodeInfo> info = |
|
2726 OwnerDoc()->NodeInfoManager()->GetNodeInfo(nsGkAtoms::body, |
|
2727 nullptr, |
|
2728 kNameSpaceID_XHTML, |
|
2729 nsIDOMNode::ELEMENT_NODE); |
|
2730 context = NS_NewHTMLBodyElement(info.forget(), FROM_PARSER_FRAGMENT); |
|
2731 } |
|
2732 |
|
2733 nsCOMPtr<nsIDOMDocumentFragment> df; |
|
2734 aError = nsContentUtils::CreateContextualFragment(context, |
|
2735 aOuterHTML, |
|
2736 true, |
|
2737 getter_AddRefs(df)); |
|
2738 if (aError.Failed()) { |
|
2739 return; |
|
2740 } |
|
2741 nsCOMPtr<nsINode> fragment = do_QueryInterface(df); |
|
2742 parent->ReplaceChild(*fragment, *this, aError); |
|
2743 } |
|
2744 |
|
2745 enum nsAdjacentPosition { |
|
2746 eBeforeBegin, |
|
2747 eAfterBegin, |
|
2748 eBeforeEnd, |
|
2749 eAfterEnd |
|
2750 }; |
|
2751 |
|
2752 void |
|
2753 Element::InsertAdjacentHTML(const nsAString& aPosition, const nsAString& aText, |
|
2754 ErrorResult& aError) |
|
2755 { |
|
2756 nsAdjacentPosition position; |
|
2757 if (aPosition.LowerCaseEqualsLiteral("beforebegin")) { |
|
2758 position = eBeforeBegin; |
|
2759 } else if (aPosition.LowerCaseEqualsLiteral("afterbegin")) { |
|
2760 position = eAfterBegin; |
|
2761 } else if (aPosition.LowerCaseEqualsLiteral("beforeend")) { |
|
2762 position = eBeforeEnd; |
|
2763 } else if (aPosition.LowerCaseEqualsLiteral("afterend")) { |
|
2764 position = eAfterEnd; |
|
2765 } else { |
|
2766 aError.Throw(NS_ERROR_DOM_SYNTAX_ERR); |
|
2767 return; |
|
2768 } |
|
2769 |
|
2770 nsCOMPtr<nsIContent> destination; |
|
2771 if (position == eBeforeBegin || position == eAfterEnd) { |
|
2772 destination = GetParent(); |
|
2773 if (!destination) { |
|
2774 aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); |
|
2775 return; |
|
2776 } |
|
2777 } else { |
|
2778 destination = this; |
|
2779 } |
|
2780 |
|
2781 nsIDocument* doc = OwnerDoc(); |
|
2782 |
|
2783 // Needed when insertAdjacentHTML is used in combination with contenteditable |
|
2784 mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, true); |
|
2785 nsAutoScriptLoaderDisabler sld(doc); |
|
2786 |
|
2787 // Batch possible DOMSubtreeModified events. |
|
2788 mozAutoSubtreeModified subtree(doc, nullptr); |
|
2789 |
|
2790 // Parse directly into destination if possible |
|
2791 if (doc->IsHTML() && !OwnerDoc()->MayHaveDOMMutationObservers() && |
|
2792 (position == eBeforeEnd || |
|
2793 (position == eAfterEnd && !GetNextSibling()) || |
|
2794 (position == eAfterBegin && !GetFirstChild()))) { |
|
2795 int32_t oldChildCount = destination->GetChildCount(); |
|
2796 int32_t contextNs = destination->GetNameSpaceID(); |
|
2797 nsIAtom* contextLocal = destination->Tag(); |
|
2798 if (contextLocal == nsGkAtoms::html && contextNs == kNameSpaceID_XHTML) { |
|
2799 // For compat with IE6 through IE9. Willful violation of HTML5 as of |
|
2800 // 2011-04-06. CreateContextualFragment does the same already. |
|
2801 // Spec bug: http://www.w3.org/Bugs/Public/show_bug.cgi?id=12434 |
|
2802 contextLocal = nsGkAtoms::body; |
|
2803 } |
|
2804 aError = nsContentUtils::ParseFragmentHTML(aText, |
|
2805 destination, |
|
2806 contextLocal, |
|
2807 contextNs, |
|
2808 doc->GetCompatibilityMode() == |
|
2809 eCompatibility_NavQuirks, |
|
2810 true); |
|
2811 // HTML5 parser has notified, but not fired mutation events. |
|
2812 nsContentUtils::FireMutationEventsForDirectParsing(doc, destination, |
|
2813 oldChildCount); |
|
2814 return; |
|
2815 } |
|
2816 |
|
2817 // couldn't parse directly |
|
2818 nsCOMPtr<nsIDOMDocumentFragment> df; |
|
2819 aError = nsContentUtils::CreateContextualFragment(destination, |
|
2820 aText, |
|
2821 true, |
|
2822 getter_AddRefs(df)); |
|
2823 if (aError.Failed()) { |
|
2824 return; |
|
2825 } |
|
2826 |
|
2827 nsCOMPtr<nsINode> fragment = do_QueryInterface(df); |
|
2828 |
|
2829 // Suppress assertion about node removal mutation events that can't have |
|
2830 // listeners anyway, because no one has had the chance to register mutation |
|
2831 // listeners on the fragment that comes from the parser. |
|
2832 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker; |
|
2833 |
|
2834 nsAutoMutationBatch mb(destination, true, false); |
|
2835 switch (position) { |
|
2836 case eBeforeBegin: |
|
2837 destination->InsertBefore(*fragment, this, aError); |
|
2838 break; |
|
2839 case eAfterBegin: |
|
2840 static_cast<nsINode*>(this)->InsertBefore(*fragment, GetFirstChild(), |
|
2841 aError); |
|
2842 break; |
|
2843 case eBeforeEnd: |
|
2844 static_cast<nsINode*>(this)->AppendChild(*fragment, aError); |
|
2845 break; |
|
2846 case eAfterEnd: |
|
2847 destination->InsertBefore(*fragment, GetNextSibling(), aError); |
|
2848 break; |
|
2849 } |
|
2850 } |
|
2851 |
|
2852 nsIEditor* |
|
2853 Element::GetEditorInternal() |
|
2854 { |
|
2855 nsCOMPtr<nsITextControlElement> textCtrl = do_QueryInterface(this); |
|
2856 return textCtrl ? textCtrl->GetTextEditor() : nullptr; |
|
2857 } |
|
2858 |
|
2859 nsresult |
|
2860 Element::SetBoolAttr(nsIAtom* aAttr, bool aValue) |
|
2861 { |
|
2862 if (aValue) { |
|
2863 return SetAttr(kNameSpaceID_None, aAttr, EmptyString(), true); |
|
2864 } |
|
2865 |
|
2866 return UnsetAttr(kNameSpaceID_None, aAttr, true); |
|
2867 } |
|
2868 |
|
2869 Directionality |
|
2870 Element::GetComputedDirectionality() const |
|
2871 { |
|
2872 nsIFrame* frame = GetPrimaryFrame(); |
|
2873 if (frame) { |
|
2874 return frame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR |
|
2875 ? eDir_LTR : eDir_RTL; |
|
2876 } |
|
2877 |
|
2878 return GetDirectionality(); |
|
2879 } |
|
2880 |
|
2881 float |
|
2882 Element::FontSizeInflation() |
|
2883 { |
|
2884 nsIFrame* frame = GetPrimaryFrame(); |
|
2885 if (!frame) { |
|
2886 return -1.0; |
|
2887 } |
|
2888 |
|
2889 if (nsLayoutUtils::FontSizeInflationEnabled(frame->PresContext())) { |
|
2890 return nsLayoutUtils::FontSizeInflationFor(frame); |
|
2891 } |
|
2892 |
|
2893 return 1.0; |
|
2894 } |