content/base/src/Element.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

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

mercurial