예제 #1
0
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;
}
예제 #2
0
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;
}
예제 #3
0
void
SVGUseElement::TriggerReclone()
{
  nsIDocument *doc = GetComposedDoc();
  if (!doc)
    return;
  nsIPresShell *presShell = doc->GetShell();
  if (!presShell)
    return;
  presShell->PostRecreateFramesFor(this);
}
예제 #4
0
bool
HTMLObjectElement::IsFocusableForTabIndex()
{
  nsIDocument* doc = GetComposedDoc();
  if (!doc || doc->HasFlag(NODE_IS_EDITABLE)) {
    return false;
  }

  return IsEditableRoot() ||
         (Type() == eType_Document &&
          nsContentUtils::IsSubDocumentTabbable(this));
}
예제 #5
0
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;
}
예제 #6
0
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);
}
예제 #7
0
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;
}
예제 #8
0
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);
}
예제 #9
0
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);
}
예제 #10
0
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;
}
예제 #11
0
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;
}
예제 #12
0
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;
}
예제 #13
0
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;
}
예제 #14
0
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();
}
예제 #15
0
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);
}
예제 #16
0
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;
}