Commit message: None
CVE: CVE-2017-5016
CWE: CWE-1021
Vulnerability description: Blink in Google Chrome prior to 56.0.2924.76 for Linux, Windows and Mac, and 56.0.2924.87 for Android, failed to prevent certain UI elements from being displayed by non-visible pages, which allowed a remote attacker to show certain UI elements on a page they don't control via a crafted HTML page.

Project: Chrome

DEFINE_TRACE(HTMLFormControlElement) {
  ListedElement::trace(visitor);
  LabelableElement::trace(visitor);
}

Project: Chrome

HTMLFormControlElement::HTMLFormControlElement(const QualifiedName& tagName,
                                               Document& document)
    : LabelableElement(tagName, document),
      m_ancestorDisabledState(AncestorDisabledStateUnknown),
      m_dataListAncestorState(Unknown),
      m_isAutofilled(false),
      m_hasValidationMessage(false),
      m_willValidateInitialized(false),
      m_willValidate(true),
      m_isValid(true),
      m_validityIsDirty(false),
      m_wasFocusedByMouse(false) {
  setHasCustomStyleCallbacks();
}

Project: Chrome

void HTMLFormControlElement::associateWith(HTMLFormElement* form) {
  associateByParser(form);
};

Project: Chrome

void HTMLFormControlElement::attachLayoutTree(const AttachContext& context) {
  HTMLElement::attachLayoutTree(context);

  if (!layoutObject())
    return;

  layoutObject()->updateFromElement();

  if (shouldAutofocusOnAttach(this))
    document().setAutofocusElement(this);
}

Project: Chrome

bool HTMLFormControlElement::checkValidity(
    HeapVector<Member<HTMLFormControlElement>>* unhandledInvalidControls,
    CheckValidityEventBehavior eventBehavior) {
  if (!willValidate())
    return true;
  if (isValidElement())
    return true;
  if (eventBehavior != CheckValidityDispatchInvalidEvent)
    return false;
  Document* originalDocument = &document();
  DispatchEventResult dispatchResult =
      dispatchEvent(Event::createCancelable(EventTypeNames::invalid));
  if (dispatchResult == DispatchEventResult::NotCanceled &&
      unhandledInvalidControls && isConnected() &&
      originalDocument == document())
    unhandledInvalidControls->append(this);
  return false;
}

Project: Chrome

void HTMLFormControlElement::didChangeForm() {
  ListedElement::didChangeForm();
  formOwnerSetNeedsValidityCheck();
  if (formOwner() && isConnected() && canBeSuccessfulSubmitButton())
    formOwner()->invalidateDefaultButtonStyle();
}

Project: Chrome

void HTMLFormControlElement::didMoveToNewDocument(Document& oldDocument) {
  ListedElement::didMoveToNewDocument(oldDocument);
  HTMLElement::didMoveToNewDocument(oldDocument);
}

Project: Chrome

void HTMLFormControlElement::disabledAttributeChanged() {
  setNeedsWillValidateCheck();
  pseudoStateChanged(CSSSelector::PseudoDisabled);
  pseudoStateChanged(CSSSelector::PseudoEnabled);
  if (layoutObject())
    LayoutTheme::theme().controlStateChanged(*layoutObject(),
                                             EnabledControlState);
  if (isDisabledFormControl() && adjustedFocusedElementInTreeScope() == this) {
    document().setNeedsFocusedElementCheck();
  }
}

Project: Chrome

void HTMLFormControlElement::dispatchChangeEvent() {
  dispatchScopedEvent(Event::createBubble(EventTypeNames::change));
}

Project: Chrome

void HTMLFormControlElement::findCustomValidationMessageTextDirection(
    const String& message,
    TextDirection& messageDir,
    String& subMessage,
    TextDirection& subMessageDir) {
  messageDir = determineDirectionality(message);
  if (!subMessage.isEmpty())
    subMessageDir = layoutObject()->style()->direction();
}

Project: Chrome

HTMLFormElement* HTMLFormControlElement::formOwner() const {
  return ListedElement::form();
}

Project: Chrome

Node::InsertionNotificationRequest HTMLFormControlElement::insertedInto(
    ContainerNode* insertionPoint) {
  m_ancestorDisabledState = AncestorDisabledStateUnknown;
  m_dataListAncestorState = Unknown;
  setNeedsWillValidateCheck();
  HTMLElement::insertedInto(insertionPoint);
  ListedElement::insertedInto(insertionPoint);
  fieldSetAncestorsSetNeedsValidityCheck(insertionPoint);

  if (!formOwner() && insertionPoint->isConnected())
    document().didAssociateFormControl(this);

  return InsertionDone;
}

Project: Chrome

bool HTMLFormControlElement::isAutofocusable() const {
  return fastHasAttribute(autofocusAttr) && supportsAutofocus();
}

Project: Chrome

bool HTMLFormControlElement::isDisabledFormControl() const {
  if (fastHasAttribute(disabledAttr))
    return true;

  if (m_ancestorDisabledState == AncestorDisabledStateUnknown)
    updateAncestorDisabledState();
  return m_ancestorDisabledState == AncestorDisabledStateDisabled;
}

Project: Chrome

bool HTMLFormControlElement::isReadOnly() const {
  return fastHasAttribute(HTMLNames::readonlyAttr);
}

Project: Chrome

bool HTMLFormControlElement::isRequired() const {
  return fastHasAttribute(requiredAttr);
}

Project: Chrome

bool HTMLFormControlElement::isValidElement() {
  if (m_validityIsDirty) {
    m_isValid = !willValidate() || valid();
    m_validityIsDirty = false;
  } else {
    DCHECK_EQ(m_isValid, (!willValidate() || valid()));
  }
  return m_isValid;
}

Project: Chrome

bool HTMLFormControlElement::matchesEnabledPseudoClass() const {
  return !isDisabledFormControl();
}

Project: Chrome

bool HTMLFormControlElement::matchesValidityPseudoClasses() const {
  return willValidate();
}

Project: Chrome

void HTMLFormControlElement::parseAttribute(const QualifiedName& name,
                                            const AtomicString& oldValue,
                                            const AtomicString& value) {
  if (name == formAttr) {
    formAttributeChanged();
    UseCounter::count(document(), UseCounter::FormAttribute);
  } else if (name == disabledAttr) {
    if (oldValue.isNull() != value.isNull())
      disabledAttributeChanged();
  } else if (name == readonlyAttr) {
    if (oldValue.isNull() != value.isNull()) {
      setNeedsWillValidateCheck();
      pseudoStateChanged(CSSSelector::PseudoReadOnly);
      pseudoStateChanged(CSSSelector::PseudoReadWrite);
      if (layoutObject())
        LayoutTheme::theme().controlStateChanged(*layoutObject(),
                                                 ReadOnlyControlState);
    }
  } else if (name == requiredAttr) {
    if (oldValue.isNull() != value.isNull())
      requiredAttributeChanged();
    UseCounter::count(document(), UseCounter::RequiredAttribute);
  } else if (name == autofocusAttr) {
    HTMLElement::parseAttribute(name, oldValue, value);
    UseCounter::count(document(), UseCounter::AutoFocusAttribute);
  } else {
    HTMLElement::parseAttribute(name, oldValue, value);
  }
}

Project: Chrome

void HTMLFormControlElement::removedFrom(ContainerNode* insertionPoint) {
  fieldSetAncestorsSetNeedsValidityCheck(insertionPoint);
  hideVisibleValidationMessage();
  m_hasValidationMessage = false;
  m_ancestorDisabledState = AncestorDisabledStateUnknown;
  m_dataListAncestorState = Unknown;
  setNeedsWillValidateCheck();
  HTMLElement::removedFrom(insertionPoint);
  ListedElement::removedFrom(insertionPoint);
}

Project: Chrome

bool HTMLFormControlElement::reportValidity() {
  HeapVector<Member<HTMLFormControlElement>> unhandledInvalidControls;
  bool isValid = checkValidity(&unhandledInvalidControls,
                               CheckValidityDispatchInvalidEvent);
  if (isValid || unhandledInvalidControls.isEmpty())
    return isValid;
  DCHECK_EQ(unhandledInvalidControls.size(), 1u);
  DCHECK_EQ(unhandledInvalidControls[0].get(), this);
  document().updateStyleAndLayoutIgnorePendingStylesheets();
  if (isFocusable()) {
    showValidationMessage();
    return false;
  }
  if (document().frame()) {
    String message(
        "An invalid form control with name='%name' is not focusable.");
    message.replace("%name", name());
    document().addConsoleMessage(ConsoleMessage::create(
        RenderingMessageSource, ErrorMessageLevel, message));
  }
  return false;
}

Project: Chrome

void HTMLFormControlElement::setCustomValidity(const String& error) {
  ListedElement::setCustomValidity(error);
  setNeedsValidityCheck();
}

Project: Chrome

void HTMLFormControlElement::setNeedsValidityCheck() {
  if (!m_validityIsDirty) {
    m_validityIsDirty = true;
    formOwnerSetNeedsValidityCheck();
    fieldSetAncestorsSetNeedsValidityCheck(parentNode());
    pseudoStateChanged(CSSSelector::PseudoValid);
    pseudoStateChanged(CSSSelector::PseudoInvalid);
    pseudoStateChanged(CSSSelector::PseudoInRange);
    pseudoStateChanged(CSSSelector::PseudoOutOfRange);
  }

  if (isValidationMessageVisible()) {
    updateVisibleValidationMessage();
  }
}

Project: Chrome

static bool shouldAutofocusOnAttach(const HTMLFormControlElement* element) {
  if (!element->isAutofocusable())
    return false;
  if (element->document().isSandboxed(SandboxAutomaticFeatures)) {
    element->document().addConsoleMessage(ConsoleMessage::create(
        SecurityMessageSource, ErrorMessageLevel,
        "Blocked autofocusing on a form control because the form's frame is "
        "sandboxed and the 'allow-scripts' permission is not set."));
    return false;
  }

  return true;
}

Project: Chrome

void HTMLFormControlElement::showValidationMessage() {
  scrollIntoViewIfNeeded(false);
  focus();
  updateVisibleValidationMessage();
}

Project: Chrome

void HTMLFormControlElement::willCallDefaultEventHandler(const Event& event) {
  if (!m_wasFocusedByMouse)
    return;
  if (!event.isKeyboardEvent() || event.type() != EventTypeNames::keydown)
    return;

  bool oldShouldHaveFocusAppearance = shouldHaveFocusAppearance();
  m_wasFocusedByMouse = false;

  if (oldShouldHaveFocusAppearance != shouldHaveFocusAppearance() &&
      layoutObject())
    LayoutTheme::theme().controlStateChanged(*layoutObject(),
                                             FocusControlState);
}

Project: Chrome

void HTMLFormControlElement::willChangeForm() {
  ListedElement::willChangeForm();
  formOwnerSetNeedsValidityCheck();
  if (formOwner() && canBeSuccessfulSubmitButton())
    formOwner()->invalidateDefaultButtonStyle();
}

Project: Chrome

bool HTMLFormControlElement::willValidate() const {
  if (!m_willValidateInitialized || m_dataListAncestorState == Unknown) {
    const_cast<HTMLFormControlElement*>(this)->setNeedsWillValidateCheck();
  } else {
    DCHECK_EQ(m_willValidate, recalcWillValidate());
  }
  return m_willValidate;
}

Project: Chrome

HTMLFormControlElement::~HTMLFormControlElement() {}