dom/html/HTMLSharedObjectElement.cpp
author Nicholas Nethercote <nnethercote@mozilla.com>
Fri, 03 Mar 2017 15:32:11 +1100
changeset 374836 4e196d802c7be7f3a3c147cc6c5b6406656584b1
parent 370723 6d9e64633666894d73161bf00fb82ea975c602b8
permissions -rw-r--r--
Bug 1344118 - Fix the profiler's sleeping threads optimization. r=jseward. When ProfilerBuffer::reset() is called, DuplicateLastSample() will start failing for all sleeping threads because there will be no prior thread data in the buffer to duplicate. But the sampling loop doesn't detect such failure. This causes two problems: - Missing samples. - CPU usage goes through the roof, because each time around the sampling loop the length of the failing search increases. The fix is simple: detect failure in the sampling loop and do a normal sample in that case. The patch also removes ThreadInfo::DuplicateLastSample(), because it just calls onto ProfileBuffer::DuplicateLastSample().

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "mozilla/EventStates.h"
#include "mozilla/dom/HTMLSharedObjectElement.h"
#include "mozilla/dom/HTMLEmbedElementBinding.h"
#include "mozilla/dom/HTMLAppletElementBinding.h"
#include "mozilla/dom/ElementInlines.h"

#include "nsIDocument.h"
#include "nsIPluginDocument.h"
#include "nsIDOMDocument.h"
#include "nsThreadUtils.h"
#include "nsIScriptError.h"
#include "nsIWidget.h"
#include "nsContentUtils.h"
#ifdef XP_MACOSX
#include "mozilla/EventDispatcher.h"
#include "mozilla/dom/Event.h"
#endif
#include "mozilla/dom/HTMLObjectElement.h"


NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(SharedObject)

namespace mozilla {
namespace dom {

HTMLSharedObjectElement::HTMLSharedObjectElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
                                                 FromParser aFromParser)
  : nsGenericHTMLElement(aNodeInfo),
    mIsDoneAddingChildren(mNodeInfo->Equals(nsGkAtoms::embed) || !aFromParser)
{
  RegisterActivityObserver();
  SetIsNetworkCreated(aFromParser == FROM_PARSER_NETWORK);

  // By default we're in the loading state
  AddStatesSilently(NS_EVENT_STATE_LOADING);
}

HTMLSharedObjectElement::~HTMLSharedObjectElement()
{
#ifdef XP_MACOSX
  HTMLObjectElement::OnFocusBlurPlugin(this, false);
#endif
  UnregisterActivityObserver();
  DestroyImageLoadingContent();
}

bool
HTMLSharedObjectElement::IsDoneAddingChildren()
{
  return mIsDoneAddingChildren;
}

void
HTMLSharedObjectElement::DoneAddingChildren(bool aHaveNotified)
{
  if (!mIsDoneAddingChildren) {
    mIsDoneAddingChildren = true;

    // If we're already in a document, we need to trigger the load
    // Otherwise, BindToTree takes care of that.
    if (IsInComposedDoc()) {
      StartObjectLoad(aHaveNotified, false);
    }
  }
}

NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLSharedObjectElement)

NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLSharedObjectElement,
                                                  nsGenericHTMLElement)
  nsObjectLoadingContent::Traverse(tmp, cb);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

NS_IMPL_ADDREF_INHERITED(HTMLSharedObjectElement, Element)
NS_IMPL_RELEASE_INHERITED(HTMLSharedObjectElement, Element)

NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLSharedObjectElement)
  NS_INTERFACE_TABLE_INHERITED(HTMLSharedObjectElement,
                               nsIRequestObserver,
                               nsIStreamListener,
                               nsIFrameLoaderOwner,
                               nsIObjectLoadingContent,
                               imgINotificationObserver,
                               nsIImageLoadingContent,
                               imgIOnloadBlocker,
                               nsIChannelEventSink)
  NS_INTERFACE_TABLE_TO_MAP_SEGUE
  NS_INTERFACE_MAP_ENTRY_IF_TAG(nsIDOMHTMLAppletElement, applet)
  NS_INTERFACE_MAP_ENTRY_IF_TAG(nsIDOMHTMLEmbedElement, embed)
NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)

NS_IMPL_ELEMENT_CLONE(HTMLSharedObjectElement)

#ifdef XP_MACOSX

NS_IMETHODIMP
HTMLSharedObjectElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
{
  HTMLObjectElement::HandleFocusBlurPlugin(this, aVisitor.mEvent);
  return NS_OK;
}

#endif // #ifdef XP_MACOSX

void
HTMLSharedObjectElement::AsyncEventRunning(AsyncEventDispatcher* aEvent)
{
  nsImageLoadingContent::AsyncEventRunning(aEvent);
}

nsresult
HTMLSharedObjectElement::BindToTree(nsIDocument *aDocument,
                                    nsIContent *aParent,
                                    nsIContent *aBindingParent,
                                    bool aCompileEventHandlers)
{
  nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
                                                 aBindingParent,
                                                 aCompileEventHandlers);
  NS_ENSURE_SUCCESS(rv, rv);

  rv = nsObjectLoadingContent::BindToTree(aDocument, aParent,
                                          aBindingParent,
                                          aCompileEventHandlers);
  NS_ENSURE_SUCCESS(rv, rv);

  // Don't kick off load from being bound to a plugin document - the plugin
  // document will call nsObjectLoadingContent::InitializeFromChannel() for the
  // initial load.
  nsCOMPtr<nsIPluginDocument> pluginDoc = do_QueryInterface(aDocument);

  // If we already have all the children, start the load.
  if (mIsDoneAddingChildren && !pluginDoc) {
    void (HTMLSharedObjectElement::*start)() =
      &HTMLSharedObjectElement::StartObjectLoad;
    nsContentUtils::AddScriptRunner(NewRunnableMethod(this, start));
  }

  return NS_OK;
}

void
HTMLSharedObjectElement::UnbindFromTree(bool aDeep,
                                        bool aNullParent)
{
#ifdef XP_MACOSX
  // When a page is reloaded (when an nsIDocument's content is removed), the
  // focused element isn't necessarily sent an eBlur event. See
  // nsFocusManager::ContentRemoved(). This means that a widget may think it
  // still contains a focused plugin when it doesn't -- which in turn can
  // disable text input in the browser window. See bug 1137229.
  HTMLObjectElement::OnFocusBlurPlugin(this, false);
#endif
  nsObjectLoadingContent::UnbindFromTree(aDeep, aNullParent);
  nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
}


nsresult
HTMLSharedObjectElement::SetAttr(int32_t aNameSpaceID, nsIAtom *aName,
                                 nsIAtom *aPrefix, const nsAString &aValue,
                                 bool aNotify)
{
  nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix,
                                              aValue, aNotify);
  NS_ENSURE_SUCCESS(rv, rv);

  // if aNotify is false, we are coming from the parser or some such place;
  // we'll get bound after all the attributes have been set, so we'll do the
  // object load from BindToTree/DoneAddingChildren.
  // Skip the LoadObject call in that case.
  // We also don't want to start loading the object when we're not yet in
  // a document, just in case that the caller wants to set additional
  // attributes before inserting the node into the document.
  if (aNotify && IsInComposedDoc() && mIsDoneAddingChildren &&
      aNameSpaceID == kNameSpaceID_None && aName == URIAttrName()
      && !BlockEmbedOrObjectContentLoading()) {
    return LoadObject(aNotify, true);
  }

  return NS_OK;
}

bool
HTMLSharedObjectElement::IsHTMLFocusable(bool aWithMouse,
                                         bool *aIsFocusable,
                                         int32_t *aTabIndex)
{
  if (mNodeInfo->Equals(nsGkAtoms::embed) || Type() == eType_Plugin) {
    // Has plugin content: let the plugin decide what to do in terms of
    // internal focus from mouse clicks
    if (aTabIndex) {
      GetTabIndex(aTabIndex);
    }

    *aIsFocusable = true;

    // Let the plugin decide, so override.
    return true;
  }

  return nsGenericHTMLElement::IsHTMLFocusable(aWithMouse, aIsFocusable, aTabIndex);
}

nsIContent::IMEState
HTMLSharedObjectElement::GetDesiredIMEState()
{
  if (Type() == eType_Plugin) {
    return IMEState(IMEState::PLUGIN);
  }
   
  return nsGenericHTMLElement::GetDesiredIMEState();
}

NS_IMPL_STRING_ATTR(HTMLSharedObjectElement, Align, align)
NS_IMPL_STRING_ATTR(HTMLSharedObjectElement, Alt, alt)
NS_IMPL_STRING_ATTR(HTMLSharedObjectElement, Archive, archive)
NS_IMPL_STRING_ATTR(HTMLSharedObjectElement, Code, code)
NS_IMPL_URI_ATTR(HTMLSharedObjectElement, CodeBase, codebase)
NS_IMPL_STRING_ATTR(HTMLSharedObjectElement, Height, height)
NS_IMPL_INT_ATTR(HTMLSharedObjectElement, Hspace, hspace)
NS_IMPL_STRING_ATTR(HTMLSharedObjectElement, Name, name)
NS_IMPL_URI_ATTR_WITH_BASE(HTMLSharedObjectElement, Object, object, codebase)
NS_IMPL_URI_ATTR(HTMLSharedObjectElement, Src, src)
NS_IMPL_STRING_ATTR(HTMLSharedObjectElement, Type, type)
NS_IMPL_INT_ATTR(HTMLSharedObjectElement, Vspace, vspace)
NS_IMPL_STRING_ATTR(HTMLSharedObjectElement, Width, width)

int32_t
HTMLSharedObjectElement::TabIndexDefault()
{
  return -1;
}

bool
HTMLSharedObjectElement::ParseAttribute(int32_t aNamespaceID,
                                        nsIAtom *aAttribute,
                                        const nsAString &aValue,
                                        nsAttrValue &aResult)
{
  if (aNamespaceID == kNameSpaceID_None) {
    if (aAttribute == nsGkAtoms::align) {
      return ParseAlignValue(aValue, aResult);
    }
    if (ParseImageAttribute(aAttribute, aValue, aResult)) {
      return true;
    }
  }

  return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                              aResult);
}

static void
MapAttributesIntoRuleBase(const nsMappedAttributes *aAttributes,
                          GenericSpecifiedValues* aData)
{
  nsGenericHTMLElement::MapImageBorderAttributeInto(aAttributes, aData);
  nsGenericHTMLElement::MapImageMarginAttributeInto(aAttributes, aData);
  nsGenericHTMLElement::MapImageSizeAttributesInto(aAttributes, aData);
  nsGenericHTMLElement::MapImageAlignAttributeInto(aAttributes, aData);
}

static void
MapAttributesIntoRuleExceptHidden(const nsMappedAttributes *aAttributes,
                                  GenericSpecifiedValues* aData)
{
  MapAttributesIntoRuleBase(aAttributes, aData);
  nsGenericHTMLElement::MapCommonAttributesIntoExceptHidden(aAttributes, aData);
}

void
HTMLSharedObjectElement::MapAttributesIntoRule(const nsMappedAttributes *aAttributes,
                                               GenericSpecifiedValues* aData)
{
  MapAttributesIntoRuleBase(aAttributes, aData);
  nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aData);
}

NS_IMETHODIMP_(bool)
HTMLSharedObjectElement::IsAttributeMapped(const nsIAtom *aAttribute) const
{
  static const MappedAttributeEntry* const map[] = {
    sCommonAttributeMap,
    sImageMarginSizeAttributeMap,
    sImageBorderAttributeMap,
    sImageAlignAttributeMap,
  };

  return FindAttributeDependence(aAttribute, map);
}


nsMapRuleToAttributesFunc
HTMLSharedObjectElement::GetAttributeMappingFunction() const
{
  if (mNodeInfo->Equals(nsGkAtoms::embed)) {
    return &MapAttributesIntoRuleExceptHidden;
  }

  return &MapAttributesIntoRule;
}

void
HTMLSharedObjectElement::StartObjectLoad(bool aNotify, bool aForceLoad)
{
  // BindToTree can call us asynchronously, and we may be removed from the tree
  // in the interim
  if (!IsInComposedDoc() || !OwnerDoc()->IsActive() ||
      BlockEmbedOrObjectContentLoading()) {
    return;
  }

  LoadObject(aNotify, aForceLoad);
  SetIsNetworkCreated(false);
}

EventStates
HTMLSharedObjectElement::IntrinsicState() const
{
  return nsGenericHTMLElement::IntrinsicState() | ObjectState();
}

uint32_t
HTMLSharedObjectElement::GetCapabilities() const
{
  uint32_t capabilities = eSupportPlugins | eAllowPluginSkipChannel;
  if (mNodeInfo->Equals(nsGkAtoms::embed)) {
    capabilities |= eSupportImages | eSupportDocuments;
  }

  return capabilities;
}

void
HTMLSharedObjectElement::DestroyContent()
{
  nsObjectLoadingContent::DestroyContent();
  nsGenericHTMLElement::DestroyContent();
}

nsresult
HTMLSharedObjectElement::CopyInnerTo(Element* aDest)
{
  nsresult rv = nsGenericHTMLElement::CopyInnerTo(aDest);
  NS_ENSURE_SUCCESS(rv, rv);

  if (aDest->OwnerDoc()->IsStaticDocument()) {
    CreateStaticClone(static_cast<HTMLSharedObjectElement*>(aDest));
  }

  return rv;
}

JSObject*
HTMLSharedObjectElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
  JSObject* obj;
  if (mNodeInfo->Equals(nsGkAtoms::applet)) {
    obj = HTMLAppletElementBinding::Wrap(aCx, this, aGivenProto);
  } else {
    MOZ_ASSERT(mNodeInfo->Equals(nsGkAtoms::embed));
    obj = HTMLEmbedElementBinding::Wrap(aCx, this, aGivenProto);
  }
  if (!obj) {
    return nullptr;
  }
  JS::Rooted<JSObject*> rootedObj(aCx, obj);
  SetupProtoChain(aCx, rootedObj);
  return rootedObj;
}

nsContentPolicyType
HTMLSharedObjectElement::GetContentPolicyType() const
{
  if (mNodeInfo->Equals(nsGkAtoms::applet)) {
    // We use TYPE_INTERNAL_OBJECT for applet too, since it is not exposed
    // through RequestContext yet.
    return nsIContentPolicy::TYPE_INTERNAL_OBJECT;
  } else {
    MOZ_ASSERT(mNodeInfo->Equals(nsGkAtoms::embed));
    return nsIContentPolicy::TYPE_INTERNAL_EMBED;
  }
}

} // namespace dom
} // namespace mozilla