nsresult HTMLLinkElement::BindToTree(Document* aDocument, nsIContent* aParent, nsIContent* aBindingParent) { Link::ResetLinkState(false, Link::ElementHasHref()); nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent, aBindingParent); NS_ENSURE_SUCCESS(rv, rv); if (Document* doc = GetComposedDoc()) { if (!doc->NodePrincipal()->IsSystemPrincipal()) { doc->RegisterPendingLinkUpdate(this); } TryDNSPrefetchOrPreconnectOrPrefetchOrPreloadOrPrerender(); } void (HTMLLinkElement::*update)() = &HTMLLinkElement::UpdateStyleSheetInternal; nsContentUtils::AddScriptRunner( NewRunnableMethod("dom::HTMLLinkElement::BindToTree", this, update)); if (aDocument && AttrValueIs(kNameSpaceID_None, nsGkAtoms::rel, nsGkAtoms::localization, eIgnoreCase)) { aDocument->LocalizationLinkAdded(this); } CreateAndDispatchEvent(aDocument, NS_LITERAL_STRING("DOMLinkAdded")); return rv; }
bool HTMLAnchorElement::IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, int32_t *aTabIndex) { if (nsGenericHTMLElement::IsHTMLFocusable(aWithMouse, aIsFocusable, aTabIndex)) { return true; } // cannot focus links if there is no link handler nsIDocument* doc = GetComposedDoc(); if (doc) { nsIPresShell* presShell = doc->GetShell(); if (presShell) { nsPresContext* presContext = presShell->GetPresContext(); if (presContext && !presContext->GetLinkHandler()) { *aIsFocusable = false; return false; } } } // Links that are in an editable region should never be focusable, even if // they are in a contenteditable="false" region. if (IsNodeInEditableRegion(this)) { if (aTabIndex) { *aTabIndex = -1; } *aIsFocusable = false; return true; } if (!HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) { // check whether we're actually a link if (!Link::HasURI()) { // Not tabbable or focusable without href (bug 17605), unless // forced to be via presence of nonnegative tabindex attribute if (aTabIndex) { *aTabIndex = -1; } *aIsFocusable = false; return false; } } if (aTabIndex && (sTabFocusModel & eTabFocus_linksMask) == 0) { *aTabIndex = -1; } *aIsFocusable = true; return false; }
void SVGUseElement::TriggerReclone() { nsIDocument *doc = GetComposedDoc(); if (!doc) return; nsIPresShell *presShell = doc->GetShell(); if (!presShell) return; presShell->PostRecreateFramesFor(this); }
bool HTMLObjectElement::IsFocusableForTabIndex() { nsIDocument* doc = GetComposedDoc(); if (!doc || doc->HasFlag(NODE_IS_EDITABLE)) { return false; } return IsEditableRoot() || (Type() == eType_Document && nsContentUtils::IsSubDocumentTabbable(this)); }
nsresult HTMLAreaElement::BindToTree(Document* aDocument, nsIContent* aParent, nsIContent* aBindingParent) { Link::ResetLinkState(false, Link::ElementHasHref()); nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent, aBindingParent); NS_ENSURE_SUCCESS(rv, rv); Document* doc = GetComposedDoc(); if (doc) { doc->RegisterPendingLinkUpdate(this); } return rv; }
void CharacterData::UnbindFromTree(bool aDeep, bool aNullParent) { // Unset frame flags; if we need them again later, they'll get set again. UnsetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE | NS_REFRAME_IF_WHITESPACE); nsIDocument* document = GetComposedDoc(); if (aNullParent) { if (this->IsRootOfNativeAnonymousSubtree()) { nsNodeUtils::NativeAnonymousChildListChange(this, true); } if (GetParent()) { NS_RELEASE(mParent); } else { mParent = nullptr; } SetParentIsContent(false); } ClearInDocument(); if (aNullParent || !mParent->IsInShadowTree()) { UnsetFlags(NODE_IS_IN_SHADOW_TREE); // Begin keeping track of our subtree root. SetSubtreeRootPointer(aNullParent ? this : mParent->SubtreeRoot()); } if (document && !GetContainingShadow()) { // Notify XBL- & nsIAnonymousContentCreator-generated // anonymous content that the document is changing. // Unlike XBL, bindings for web components shadow DOM // do not get uninstalled. if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) { nsContentUtils::AddScriptRunner( new RemoveFromBindingManagerRunnable(document->BindingManager(), this, document)); } } nsExtendedContentSlots* slots = GetExistingExtendedContentSlots(); if (slots) { slots->mBindingParent = nullptr; if (aNullParent || !mParent->IsInShadowTree()) { slots->mContainingShadow = nullptr; } } nsNodeUtils::ParentChainChanged(this); }
bool nsMappedAttributeElement::SetMappedAttribute(nsIDocument* aDocument, nsIAtom* aName, nsAttrValue& aValue, nsresult* aRetval) { NS_PRECONDITION(aDocument == GetComposedDoc(), "Unexpected document"); nsHTMLStyleSheet* sheet = aDocument ? aDocument->GetAttributeStyleSheet() : nullptr; *aRetval = mAttrsAndChildren.SetAndTakeMappedAttr(aName, aValue, this, sheet); return true; }
void SVGUseElement::LookupHref() { nsAutoString href; mStringAttributes[HREF].GetAnimValue(href, this); if (href.IsEmpty()) return; nsCOMPtr<nsIURI> targetURI; nsCOMPtr<nsIURI> baseURI = mOriginal ? mOriginal->GetBaseURI() : GetBaseURI(); nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href, GetComposedDoc(), baseURI); mSource.Reset(this, targetURI); }
void HTMLStyleElement::UnbindFromTree(bool aDeep, bool aNullParent) { nsCOMPtr<nsIDocument> oldDoc = GetUncomposedDoc(); nsCOMPtr<nsIDocument> oldComposedDoc = GetComposedDoc(); ShadowRoot* oldShadow = GetContainingShadow(); nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); if (GetContainingShadow() && !oldComposedDoc) { // The style is in a shadow tree and was already not // in the composed document. Thus the sheet does not // need to be updated. return; } UpdateStyleSheetInternal(oldDoc, oldShadow); }
bool HTMLObjectElement::IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, int32_t *aTabIndex) { // TODO: this should probably be managed directly by IsHTMLFocusable. // See bug 597242. nsIDocument *doc = GetComposedDoc(); if (!doc || doc->HasFlag(NODE_IS_EDITABLE)) { if (aTabIndex) { GetTabIndex(aTabIndex); } *aIsFocusable = false; return false; } // This method doesn't call nsGenericHTMLFormElement intentionally. // TODO: It should probably be changed when bug 597242 will be fixed. if (Type() == eType_Plugin || IsEditableRoot() || (Type() == eType_Document && nsContentUtils::IsSubDocumentTabbable(this))) { // Has plugin content: let the plugin decide what to do in terms of // internal focus from mouse clicks if (aTabIndex) { GetTabIndex(aTabIndex); } *aIsFocusable = true; return false; } // TODO: this should probably be managed directly by IsHTMLFocusable. // See bug 597242. const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(nsGkAtoms::tabindex); *aIsFocusable = attrVal && attrVal->Type() == nsAttrValue::eInteger; if (aTabIndex && *aIsFocusable) { *aTabIndex = attrVal->GetIntegerValue(); } return false; }
nsresult SVGAElement::BindToTree(nsIDocument *aDocument, nsIContent *aParent, nsIContent *aBindingParent, bool aCompileEventHandlers) { Link::ResetLinkState(false, Link::ElementHasHref()); nsresult rv = SVGAElementBase::BindToTree(aDocument, aParent, aBindingParent, aCompileEventHandlers); NS_ENSURE_SUCCESS(rv, rv); nsIDocument* doc = GetComposedDoc(); if (doc) { doc->RegisterPendingLinkUpdate(this); } return NS_OK; }
nsresult HTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, bool aCompileEventHandlers) { Link::ResetLinkState(false, Link::ElementHasHref()); nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent, aBindingParent, aCompileEventHandlers); NS_ENSURE_SUCCESS(rv, rv); // Prefetch links nsIDocument* doc = GetComposedDoc(); if (doc) { doc->RegisterPendingLinkUpdate(this); TryDNSPrefetch(); } return rv; }
nsIContent* SVGUseElement::CreateAnonymousContent() { mClone = nullptr; if (mSource.get()) { mSource.get()->RemoveMutationObserver(this); } LookupHref(); nsIContent* targetContent = mSource.get(); if (!targetContent || !targetContent->IsSVG()) return nullptr; // make sure target is valid type for <use> // QIable nsSVGGraphicsElement would eliminate enumerating all elements nsIAtom *tag = targetContent->Tag(); if (tag != nsGkAtoms::svg && tag != nsGkAtoms::symbol && tag != nsGkAtoms::g && tag != nsGkAtoms::path && tag != nsGkAtoms::text && tag != nsGkAtoms::rect && tag != nsGkAtoms::circle && tag != nsGkAtoms::ellipse && tag != nsGkAtoms::line && tag != nsGkAtoms::polyline && tag != nsGkAtoms::polygon && tag != nsGkAtoms::image && tag != nsGkAtoms::use) return nullptr; // circular loop detection // check 1 - check if we're a document descendent of the target if (nsContentUtils::ContentIsDescendantOf(this, targetContent)) return nullptr; // check 2 - check if we're a clone, and if we already exist in the hierarchy if (GetParent() && mOriginal) { for (nsCOMPtr<nsIContent> content = GetParent(); content; content = content->GetParent()) { if (content->IsSVG(nsGkAtoms::use) && static_cast<SVGUseElement*>(content.get())->mOriginal == mOriginal) { return nullptr; } } } nsCOMPtr<nsINode> newnode; nsCOMArray<nsINode> unused; nsNodeInfoManager* nodeInfoManager = targetContent->OwnerDoc() == OwnerDoc() ? nullptr : OwnerDoc()->NodeInfoManager(); nsNodeUtils::Clone(targetContent, true, nodeInfoManager, unused, getter_AddRefs(newnode)); nsCOMPtr<nsIContent> newcontent = do_QueryInterface(newnode); if (!newcontent) return nullptr; if (newcontent->IsSVG(nsGkAtoms::symbol)) { nsIDocument *document = GetComposedDoc(); if (!document) return nullptr; nsNodeInfoManager *nodeInfoManager = document->NodeInfoManager(); if (!nodeInfoManager) return nullptr; nsRefPtr<mozilla::dom::NodeInfo> nodeInfo; nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::svg, nullptr, kNameSpaceID_SVG, nsIDOMNode::ELEMENT_NODE); nsCOMPtr<nsIContent> svgNode; NS_NewSVGSVGElement(getter_AddRefs(svgNode), nodeInfo.forget(), NOT_FROM_PARSER); if (!svgNode) return nullptr; // copy attributes const nsAttrName* name; uint32_t i; for (i = 0; (name = newcontent->GetAttrNameAt(i)); i++) { nsAutoString value; int32_t nsID = name->NamespaceID(); nsIAtom* lname = name->LocalName(); newcontent->GetAttr(nsID, lname, value); svgNode->SetAttr(nsID, lname, name->GetPrefix(), value, false); } // move the children over uint32_t num = newcontent->GetChildCount(); for (i = 0; i < num; i++) { nsCOMPtr<nsIContent> child = newcontent->GetFirstChild(); newcontent->RemoveChildAt(0, false); svgNode->InsertChildAt(child, i, true); } newcontent = svgNode; } if (newcontent->IsSVG() && (newcontent->Tag() == nsGkAtoms::svg || newcontent->Tag() == nsGkAtoms::symbol)) { nsSVGElement *newElement = static_cast<nsSVGElement*>(newcontent.get()); if (mLengthAttributes[ATTR_WIDTH].IsExplicitlySet()) newElement->SetLength(nsGkAtoms::width, mLengthAttributes[ATTR_WIDTH]); if (mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet()) newElement->SetLength(nsGkAtoms::height, mLengthAttributes[ATTR_HEIGHT]); } // Set up its base URI correctly nsCOMPtr<nsIURI> baseURI = targetContent->GetBaseURI(); if (!baseURI) return nullptr; newcontent->SetExplicitBaseURI(baseURI); targetContent->AddMutationObserver(this); mClone = newcontent; return mClone; }
void RestyleTracker::DoProcessRestyles() { nsAutoCString docURL; if (profiler_is_active()) { nsIURI *uri = Document()->GetDocumentURI(); if (uri) { uri->GetSpec(docURL); } else { docURL = "N/A"; } } PROFILER_LABEL_PRINTF("RestyleTracker", "ProcessRestyles", js::ProfileEntry::Category::CSS, "(%s)", docURL.get()); nsDocShell* docShell = static_cast<nsDocShell*>(mRestyleManager->PresContext()->GetDocShell()); RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get(); bool isTimelineRecording = timelines && timelines->HasConsumer(docShell); // Create a AnimationsWithDestroyedFrame during restyling process to // stop animations and transitions on elements that have no frame at the end // of the restyling process. RestyleManager::AnimationsWithDestroyedFrame animationsWithDestroyedFrame(mRestyleManager); // Create a ReframingStyleContexts struct on the stack and put it in our // mReframingStyleContexts for almost all of the remaining scope of // this function. // // It needs to be *in* scope during BeginProcessingRestyles, which // might (if mDoRebuildAllStyleData is true) do substantial amounts of // restyle processing. // // However, it needs to be *out* of scope during // EndProcessingRestyles, since we should release the style contexts // it holds prior to any EndReconstruct call that // EndProcessingRestyles makes. This is because in EndReconstruct we // try to destroy the old rule tree using the GC mechanism, which // means it only gets destroyed if it's unreferenced (and if it's // referenced, we assert). So we want the ReframingStyleContexts // (which holds old style contexts) to be destroyed before the // EndReconstruct so those style contexts go away before // EndReconstruct. { RestyleManager::ReframingStyleContexts reframingStyleContexts(mRestyleManager); mRestyleManager->BeginProcessingRestyles(*this); LOG_RESTYLE("Processing %d pending %srestyles with %d restyle roots for %s", mPendingRestyles.Count(), mRestyleManager->PresContext()->TransitionManager()-> InAnimationOnlyStyleUpdate() ? (const char*) "animation " : (const char*) "", static_cast<int>(mRestyleRoots.Length()), GetDocumentURI(Document()).get()); LOG_RESTYLE_INDENT(); // loop so that we process any restyle events generated by processing while (mPendingRestyles.Count()) { if (mHaveLaterSiblingRestyles) { // Convert them to individual restyles on all the later siblings AutoTArray<RefPtr<Element>, RESTYLE_ARRAY_STACKSIZE> laterSiblingArr; for (auto iter = mPendingRestyles.Iter(); !iter.Done(); iter.Next()) { auto element = static_cast<dom::Element*>(iter.Key()); MOZ_ASSERT(!element->IsStyledByServo(), "Should not have Servo-styled elements here"); // Only collect the entries that actually need restyling by us (and // haven't, for example, already been restyled). // It's important to not mess with the flags on entries not in our // document. if (element->GetComposedDoc() == Document() && element->HasFlag(RestyleBit()) && (iter.Data()->mRestyleHint & eRestyle_LaterSiblings)) { laterSiblingArr.AppendElement(element); } } for (uint32_t i = 0; i < laterSiblingArr.Length(); ++i) { Element* element = laterSiblingArr[i]; MOZ_ASSERT(!element->IsStyledByServo()); for (nsIContent* sibling = element->GetNextSibling(); sibling; sibling = sibling->GetNextSibling()) { if (sibling->IsElement()) { LOG_RESTYLE("adding pending restyle for %s due to " "eRestyle_LaterSiblings hint on %s", FrameTagToString(sibling->AsElement()).get(), FrameTagToString(element->AsElement()).get()); if (AddPendingRestyle(sibling->AsElement(), eRestyle_Subtree, nsChangeHint(0))) { // Nothing else to do here; we'll handle the following // siblings when we get to |sibling| in laterSiblingArr. break; } } } } // Now remove all those eRestyle_LaterSiblings bits for (uint32_t i = 0; i < laterSiblingArr.Length(); ++i) { Element* element = laterSiblingArr[i]; NS_ASSERTION(element->HasFlag(RestyleBit()), "How did that happen?"); RestyleData* data; #ifdef DEBUG bool found = #endif mPendingRestyles.Get(element, &data); NS_ASSERTION(found, "Where did our entry go?"); data->mRestyleHint = nsRestyleHint(data->mRestyleHint & ~eRestyle_LaterSiblings); } LOG_RESTYLE("%d pending restyles after expanding out " "eRestyle_LaterSiblings", mPendingRestyles.Count()); mHaveLaterSiblingRestyles = false; } uint32_t rootCount; while ((rootCount = mRestyleRoots.Length())) { // Make sure to pop the element off our restyle root array, so // that we can freely append to the array as we process this // element. RefPtr<Element> element; element.swap(mRestyleRoots[rootCount - 1]); mRestyleRoots.RemoveElementAt(rootCount - 1); LOG_RESTYLE("processing style root %s at index %d", FrameTagToString(element).get(), rootCount - 1); LOG_RESTYLE_INDENT(); // Do the document check before calling GetRestyleData, since we // don't want to do the sibling-processing GetRestyleData does if // the node is no longer relevant. if (element->GetComposedDoc() != Document()) { // Content node has been removed from our document; nothing else // to do here LOG_RESTYLE("skipping, no longer in the document"); continue; } nsAutoPtr<RestyleData> data; if (!GetRestyleData(element, data)) { LOG_RESTYLE("skipping, already restyled"); continue; } if (isTimelineRecording) { timelines->AddMarkerForDocShell(docShell, Move( MakeUnique<RestyleTimelineMarker>( data->mRestyleHint, MarkerTracingType::START))); } #if defined(MOZ_ENABLE_PROFILER_SPS) Maybe<GeckoProfilerTracingRAII> profilerRAII; if (profiler_feature_active("restyle")) { profilerRAII.emplace("Paint", "Styles", Move(data->mBacktrace)); } #endif ProcessOneRestyle(element, data->mRestyleHint, data->mChangeHint, data->mRestyleHintData); AddRestyleRootsIfAwaitingRestyle(data->mDescendants); if (isTimelineRecording) { timelines->AddMarkerForDocShell(docShell, Move( MakeUnique<RestyleTimelineMarker>( data->mRestyleHint, MarkerTracingType::END))); } } if (mHaveLaterSiblingRestyles) { // Keep processing restyles for now continue; } // Now we only have entries with change hints left. To be safe in // case of reentry from the handing of the change hint, use a // scratch array instead of calling out to ProcessOneRestyle while // enumerating the hashtable. Use the stack if we can, otherwise // fall back on heap-allocation. AutoTArray<RestyleEnumerateData, RESTYLE_ARRAY_STACKSIZE> restyleArr; RestyleEnumerateData* restylesToProcess = restyleArr.AppendElements(mPendingRestyles.Count()); if (restylesToProcess) { RestyleEnumerateData* restyle = restylesToProcess; #ifdef RESTYLE_LOGGING uint32_t count = 0; #endif for (auto iter = mPendingRestyles.Iter(); !iter.Done(); iter.Next()) { auto element = static_cast<dom::Element*>(iter.Key()); RestyleTracker::RestyleData* data = iter.Data(); // Only collect the entries that actually need restyling by us (and // haven't, for example, already been restyled). // It's important to not mess with the flags on entries not in our // document. if (element->GetComposedDoc() != Document() || !element->HasFlag(RestyleBit())) { LOG_RESTYLE("skipping pending restyle %s, already restyled or no " "longer in the document", FrameTagToString(element).get()); continue; } NS_ASSERTION( !element->HasFlag(RootBit()) || // Maybe we're just not reachable via the frame tree? (element->GetFlattenedTreeParent() && (!element->GetFlattenedTreeParent()->GetPrimaryFrame() || element->GetFlattenedTreeParent()->GetPrimaryFrame()->IsLeaf() || element->GetComposedDoc()->GetShell()->FrameManager() ->GetDisplayContentsStyleFor(element))) || // Or not reachable due to an async reinsert we have // pending? If so, we'll have a reframe hint around. // That incidentally makes it safe that we still have // the bit, since any descendants that didn't get added // to the roots list because we had the bits will be // completely restyled in a moment. (data->mChangeHint & nsChangeHint_ReconstructFrame), "Why did this not get handled while processing mRestyleRoots?"); // Unset the restyle bits now, so if they get readded later as we // process we won't clobber that adding of the bit. element->UnsetFlags(RestyleBit() | RootBit() | ConditionalDescendantsBit()); restyle->mElement = element; restyle->mRestyleHint = data->mRestyleHint; restyle->mChangeHint = data->mChangeHint; // We can move data since we'll be clearing mPendingRestyles after // we finish enumerating it. restyle->mRestyleHintData = Move(data->mRestyleHintData); #if defined(MOZ_ENABLE_PROFILER_SPS) restyle->mBacktrace = Move(data->mBacktrace); #endif #ifdef RESTYLE_LOGGING count++; #endif // Increment to the next slot in the array restyle++; } RestyleEnumerateData* lastRestyle = restyle; // Clear the hashtable now that we don't need it anymore mPendingRestyles.Clear(); #ifdef RESTYLE_LOGGING uint32_t index = 0; #endif for (RestyleEnumerateData* currentRestyle = restylesToProcess; currentRestyle != lastRestyle; ++currentRestyle) { LOG_RESTYLE("processing pending restyle %s at index %d/%d", FrameTagToString(currentRestyle->mElement).get(), index++, count); LOG_RESTYLE_INDENT(); #if defined(MOZ_ENABLE_PROFILER_SPS) Maybe<GeckoProfilerTracingRAII> profilerRAII; if (profiler_feature_active("restyle")) { profilerRAII.emplace("Paint", "Styles", Move(currentRestyle->mBacktrace)); } #endif if (isTimelineRecording) { timelines->AddMarkerForDocShell(docShell, Move( MakeUnique<RestyleTimelineMarker>( currentRestyle->mRestyleHint, MarkerTracingType::START))); } ProcessOneRestyle(currentRestyle->mElement, currentRestyle->mRestyleHint, currentRestyle->mChangeHint, currentRestyle->mRestyleHintData); if (isTimelineRecording) { timelines->AddMarkerForDocShell(docShell, Move( MakeUnique<RestyleTimelineMarker>( currentRestyle->mRestyleHint, MarkerTracingType::END))); } } } } } // mPendingRestyles is now empty. mHaveSelectors = false; mRestyleManager->EndProcessingRestyles(); }
nsresult HTMLLinkElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName, const nsAttrValue* aValue, const nsAttrValue* aOldValue, nsIPrincipal* aSubjectPrincipal, bool aNotify) { // It's safe to call ResetLinkState here because our new attr value has // already been set or unset. ResetLinkState needs the updated attribute // value because notifying the document that content states have changed will // call IntrinsicState, which will try to get updated information about the // visitedness from Link. if (aName == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) { bool hasHref = aValue; Link::ResetLinkState(!!aNotify, hasHref); if (IsInUncomposedDoc()) { CreateAndDispatchEvent(OwnerDoc(), NS_LITERAL_STRING("DOMLinkChanged")); } } if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::href) { mTriggeringPrincipal = nsContentUtils::GetAttrTriggeringPrincipal( this, aValue ? aValue->GetStringValue() : EmptyString(), aSubjectPrincipal); } // If a link's `rel` attribute was changed from or to `localization`, // update the list of localization links. if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::rel) { Document* doc = GetComposedDoc(); if (doc) { if ((aValue && aValue->Equals(nsGkAtoms::localization, eIgnoreCase)) && (!aOldValue || !aOldValue->Equals(nsGkAtoms::localization, eIgnoreCase))) { doc->LocalizationLinkAdded(this); } else if ((aOldValue && aOldValue->Equals(nsGkAtoms::localization, eIgnoreCase)) && (!aValue || !aValue->Equals(nsGkAtoms::localization, eIgnoreCase))) { doc->LocalizationLinkRemoved(this); } } } // If the link has `rel=localization` and its `href` attribute is changed, // update the list of localization links. if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::href && AttrValueIs(kNameSpaceID_None, nsGkAtoms::rel, nsGkAtoms::localization, eIgnoreCase)) { Document* doc = GetComposedDoc(); if (doc) { if (aOldValue) { doc->LocalizationLinkRemoved(this); } if (aValue) { doc->LocalizationLinkAdded(this); } } } if (aValue) { if (aNameSpaceID == kNameSpaceID_None && (aName == nsGkAtoms::href || aName == nsGkAtoms::rel || aName == nsGkAtoms::title || aName == nsGkAtoms::media || aName == nsGkAtoms::type || aName == nsGkAtoms::as || aName == nsGkAtoms::crossorigin)) { bool dropSheet = false; if (aName == nsGkAtoms::rel) { nsAutoString value; aValue->ToString(value); uint32_t linkTypes = nsStyleLinkElement::ParseLinkTypes(value); if (GetSheet()) { dropSheet = !(linkTypes & nsStyleLinkElement::eSTYLESHEET); } } if ((aName == nsGkAtoms::rel || aName == nsGkAtoms::href) && IsInComposedDoc()) { TryDNSPrefetchOrPreconnectOrPrefetchOrPreloadOrPrerender(); } if ((aName == nsGkAtoms::as || aName == nsGkAtoms::type || aName == nsGkAtoms::crossorigin || aName == nsGkAtoms::media) && IsInComposedDoc()) { UpdatePreload(aName, aValue, aOldValue); } const bool forceUpdate = dropSheet || aName == nsGkAtoms::title || aName == nsGkAtoms::media || aName == nsGkAtoms::type; Unused << UpdateStyleSheetInternal( nullptr, nullptr, forceUpdate ? ForceUpdate::Yes : ForceUpdate::No); } } else { // Since removing href or rel makes us no longer link to a // stylesheet, force updates for those too. if (aNameSpaceID == kNameSpaceID_None) { if (aName == nsGkAtoms::href || aName == nsGkAtoms::rel || aName == nsGkAtoms::title || aName == nsGkAtoms::media || aName == nsGkAtoms::type) { Unused << UpdateStyleSheetInternal(nullptr, nullptr, ForceUpdate::Yes); } if ((aName == nsGkAtoms::as || aName == nsGkAtoms::type || aName == nsGkAtoms::crossorigin || aName == nsGkAtoms::media) && IsInComposedDoc()) { UpdatePreload(aName, aValue, aOldValue); } } } return nsGenericHTMLElement::AfterSetAttr( aNameSpaceID, aName, aValue, aOldValue, aSubjectPrincipal, aNotify); }
nsresult CharacterData::SetTextInternal( uint32_t aOffset, uint32_t aCount, const char16_t* aBuffer, uint32_t aLength, bool aNotify, CharacterDataChangeInfo::Details* aDetails) { MOZ_ASSERT(aBuffer || !aLength, "Null buffer passed to SetTextInternal!"); // sanitize arguments uint32_t textLength = mText.GetLength(); if (aOffset > textLength) { return NS_ERROR_DOM_INDEX_SIZE_ERR; } if (aCount > textLength - aOffset) { aCount = textLength - aOffset; } uint32_t endOffset = aOffset + aCount; // Make sure the text fragment can hold the new data. if (aLength > aCount && !mText.CanGrowBy(aLength - aCount)) { return NS_ERROR_OUT_OF_MEMORY; } Document* document = GetComposedDoc(); mozAutoDocUpdate updateBatch(document, aNotify); bool haveMutationListeners = aNotify && nsContentUtils::HasMutationListeners( this, NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED, this); RefPtr<nsAtom> oldValue; if (haveMutationListeners) { oldValue = GetCurrentValueAtom(); } if (aNotify) { CharacterDataChangeInfo info = {aOffset == textLength, aOffset, endOffset, aLength, aDetails}; nsNodeUtils::CharacterDataWillChange(this, info); } Directionality oldDir = eDir_NotSet; bool dirAffectsAncestor = (NodeType() == TEXT_NODE && TextNodeWillChangeDirection(static_cast<nsTextNode*>(this), &oldDir, aOffset)); if (aOffset == 0 && endOffset == textLength) { // Replacing whole text or old text was empty. Don't bother to check for // bidi in this string if the document already has bidi enabled. // If this is marked as "maybe modified frequently", the text should be // stored as char16_t since converting char* to char16_t* is expensive. bool ok = mText.SetTo(aBuffer, aLength, !document || !document->GetBidiEnabled(), HasFlag(NS_MAYBE_MODIFIED_FREQUENTLY)); NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); } else if (aOffset == textLength) { // Appending to existing bool ok = mText.Append(aBuffer, aLength, !document || !document->GetBidiEnabled(), HasFlag(NS_MAYBE_MODIFIED_FREQUENTLY)); NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); } else { // Merging old and new bool bidi = mText.IsBidi(); // Allocate new buffer int32_t newLength = textLength - aCount + aLength; // Use nsString and not nsAutoString so that we get a nsStringBuffer which // can be just AddRefed in nsTextFragment. nsString to; to.SetCapacity(newLength); // Copy over appropriate data if (aOffset) { mText.AppendTo(to, 0, aOffset); } if (aLength) { to.Append(aBuffer, aLength); if (!bidi && (!document || !document->GetBidiEnabled())) { bidi = HasRTLChars(MakeSpan(aBuffer, aLength)); } } if (endOffset != textLength) { mText.AppendTo(to, endOffset, textLength - endOffset); } // If this is marked as "maybe modified frequently", the text should be // stored as char16_t since converting char* to char16_t* is expensive. // Use char16_t also when we have bidi characters. bool use2b = HasFlag(NS_MAYBE_MODIFIED_FREQUENTLY) || bidi; bool ok = mText.SetTo(to, false, use2b); mText.SetBidi(bidi); NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); } UnsetFlags(NS_CACHED_TEXT_IS_ONLY_WHITESPACE); if (document && mText.IsBidi()) { // If we found bidi characters in mText.SetTo() above, indicate that the // document contains bidi characters. document->SetBidiEnabled(); } if (dirAffectsAncestor) { // dirAffectsAncestor being true implies that we have a text node, see // above. MOZ_ASSERT(NodeType() == TEXT_NODE); TextNodeChangedDirection(static_cast<nsTextNode*>(this), oldDir, aNotify); } // Notify observers if (aNotify) { CharacterDataChangeInfo info = {aOffset == textLength, aOffset, endOffset, aLength, aDetails}; nsNodeUtils::CharacterDataChanged(this, info); if (haveMutationListeners) { InternalMutationEvent mutation(true, eLegacyCharacterDataModified); mutation.mPrevAttrValue = oldValue; if (aLength > 0) { nsAutoString val; mText.AppendTo(val); mutation.mNewAttrValue = NS_Atomize(val); } mozAutoSubtreeModified subtree(OwnerDoc(), this); (new AsyncEventDispatcher(this, mutation))->RunDOMEventWhenSafe(); } } return NS_OK; }