Commit message: None
CVE: CVE-2017-15427
CWE: CWE-79
Vulnerability description: Insufficient policy enforcement in Omnibox in Google Chrome prior to 63.0.3239.84 allowed a socially engineered user to XSS themselves by dragging and dropping a javascript: URL into the URL bar.

Project: Chrome

void OmniboxViewViews::AddedToWidget() {
  views::Textfield::AddedToWidget();
  scoped_observer_.Add(GetWidget()->GetCompositor());
}

Project: Chrome

void OmniboxViewViews::AppendDropFormats(
    int* formats,
    std::set<ui::Clipboard::FormatType>* format_types) {
  *formats = *formats | ui::OSExchangeData::URL;
}

Project: Chrome

void OmniboxViewViews::DoInsertChar(base::char16 ch) {
  if (insert_char_time_.is_null()) {
    DCHECK_EQ(latency_histogram_state_, NOT_ACTIVE);
    latency_histogram_state_ = CHAR_TYPED;
    insert_char_time_ = base::TimeTicks::Now();
  }
  Textfield::DoInsertChar(ch);
}

Project: Chrome

void OmniboxViewViews::EmphasizeURLComponents() {
  if (!location_bar_view_)
    return;

  bool text_is_url = model()->CurrentTextIsURL();
  GetRenderText()->SetDirectionalityMode(text_is_url
                                             ? gfx::DIRECTIONALITY_FORCE_LTR
                                             : gfx::DIRECTIONALITY_FROM_TEXT);
  SetStyle(gfx::STRIKE, false);
  UpdateTextStyle(text(), model()->client()->GetSchemeClassifier());
}

Project: Chrome

void OmniboxViewViews::EnterKeywordModeForDefaultSearchProvider() {
  model()->EnterKeywordModeForDefaultSearchProvider(
      KeywordModeEntryMethod::KEYBOARD_SHORTCUT);
}

Project: Chrome

void OmniboxViewViews::ExecuteCommand(int command_id, int event_flags) {
  DestroyTouchSelection();
  switch (command_id) {
    case IDS_PASTE_AND_GO:
      model()->PasteAndGo(GetClipboardText());
      return;
    case IDC_EDIT_SEARCH_ENGINES:
      location_bar_view_->command_updater()->ExecuteCommand(command_id);
      return;

    case IDS_APP_PASTE:
      ExecuteTextEditCommand(ui::TextEditCommand::PASTE);
      return;
    default:
      if (Textfield::IsCommandIdEnabled(command_id)) {
        Textfield::ExecuteCommand(command_id, event_flags);
        return;
      }
      OnBeforePossibleChange();
      location_bar_view_->command_updater()->ExecuteCommand(command_id);
      OnAfterPossibleChange(true);
      return;
  }
}

Project: Chrome

void OmniboxViewViews::GetAccessibleNodeData(ui::AXNodeData* node_data) {
  node_data->role = ui::AX_ROLE_TEXT_FIELD;
  node_data->SetName(l10n_util::GetStringUTF8(IDS_ACCNAME_LOCATION));
  node_data->SetValue(GetText());
  node_data->html_attributes.push_back(std::make_pair("type", "url"));

  base::string16::size_type entry_start;
  base::string16::size_type entry_end;
  if (saved_selection_for_focus_change_.IsValid()) {
    entry_start = saved_selection_for_focus_change_.start();
    entry_end = saved_selection_for_focus_change_.end();
  } else {
    GetSelectionBounds(&entry_start, &entry_end);
  }
  node_data->AddIntAttribute(ui::AX_ATTR_TEXT_SEL_START, entry_start);
  node_data->AddIntAttribute(ui::AX_ATTR_TEXT_SEL_END, entry_end);

  if (popup_window_mode_) {
    node_data->AddIntAttribute(ui::AX_ATTR_RESTRICTION,
                               ui::AX_RESTRICTION_READ_ONLY);
  } else {
    node_data->AddState(ui::AX_STATE_EDITABLE);
  }
}

Project: Chrome

gfx::Size OmniboxViewViews::GetMinimumSize() const {
  const int kMinCharacters = 10;
  return gfx::Size(
      GetFontList().GetExpectedTextWidth(kMinCharacters) + GetInsets().width(),
      GetPreferredSize().height());
}

Project: Chrome

bool OmniboxViewViews::HandleAccessibleAction(
    const ui::AXActionData& action_data) {
  if (read_only())
    return Textfield::HandleAccessibleAction(action_data);

  if (action_data.action == ui::AX_ACTION_SET_VALUE) {
    SetUserText(action_data.value, true);
    return true;
  } else if (action_data.action == ui::AX_ACTION_REPLACE_SELECTED_TEXT) {
    model()->SetInputInProgress(true);
    if (saved_selection_for_focus_change_.IsValid()) {
      SelectRange(saved_selection_for_focus_change_);
      saved_selection_for_focus_change_ = gfx::Range::InvalidRange();
    }
    InsertOrReplaceText(action_data.value);
    TextChanged();
    return true;
  }

  return Textfield::HandleAccessibleAction(action_data);
}

Project: Chrome

bool OmniboxViewViews::HandleEarlyTabActions(const ui::KeyEvent& event) {
  if (!views::FocusManager::IsTabTraversalKeyEvent(event))
    return false;

  if (model()->is_keyword_hint() && !event.IsShiftDown())
    return model()->AcceptKeyword(KeywordModeEntryMethod::TAB);

  if (!model()->popup_model()->IsOpen())
    return false;

  if (event.IsShiftDown() &&
      (model()->popup_model()->selected_line_state() ==
          OmniboxPopupModel::KEYWORD))
    model()->ClearKeyword();
  else
    model()->OnUpOrDownKeyPressed(event.IsShiftDown() ? -1 : 1);

  return true;
}

Project: Chrome

bool OmniboxViewViews::HandleKeyEvent(views::Textfield* textfield,
                                      const ui::KeyEvent& event) {
  if (event.type() == ui::ET_KEY_RELEASED) {
    if (event.key_code() == ui::VKEY_CONTROL)
      model()->OnControlKeyChanged(false);

    return false;
  }

  delete_at_end_pressed_ = false;

  if (event.IsUnicodeKeyCode())
    return false;

  const bool shift = event.IsShiftDown();
  const bool control = event.IsControlDown();
  const bool alt = event.IsAltDown() || event.IsAltGrDown();
  switch (event.key_code()) {
    case ui::VKEY_RETURN:
      model()->AcceptInput(alt ? WindowOpenDisposition::NEW_FOREGROUND_TAB
                               : WindowOpenDisposition::CURRENT_TAB,
                           false);
      return true;
    case ui::VKEY_ESCAPE:
      return model()->OnEscapeKeyPressed();
    case ui::VKEY_CONTROL:
      model()->OnControlKeyChanged(true);
      break;
    case ui::VKEY_DELETE:
      if (shift && model()->popup_model()->IsOpen())
        model()->popup_model()->TryDeletingCurrentItem();

      delete_at_end_pressed_ = (!event.IsAltDown() && !HasSelection() &&
                                GetCursorPosition() == text().length());
      break;
    case ui::VKEY_UP:
      if (IsTextEditCommandEnabled(ui::TextEditCommand::MOVE_UP)) {
        ExecuteTextEditCommand(ui::TextEditCommand::MOVE_UP);
        return true;
      }
      break;
    case ui::VKEY_DOWN:
      if (IsTextEditCommandEnabled(ui::TextEditCommand::MOVE_DOWN)) {
        ExecuteTextEditCommand(ui::TextEditCommand::MOVE_DOWN);
        return true;
      }
      break;
    case ui::VKEY_PRIOR:
      if (control || alt || shift)
        return false;
      model()->OnUpOrDownKeyPressed(-1 * model()->result().size());
      return true;
    case ui::VKEY_NEXT:
      if (control || alt || shift)
        return false;
      model()->OnUpOrDownKeyPressed(model()->result().size());
      return true;
    case ui::VKEY_V:
      if (control && !alt &&
          IsTextEditCommandEnabled(ui::TextEditCommand::PASTE)) {
        ExecuteTextEditCommand(ui::TextEditCommand::PASTE);
        return true;
      }
      break;
    case ui::VKEY_INSERT:
      if (shift && !control &&
          IsTextEditCommandEnabled(ui::TextEditCommand::PASTE)) {
        ExecuteTextEditCommand(ui::TextEditCommand::PASTE);
        return true;
      }
      break;
    case ui::VKEY_BACK:
      if (model()->is_keyword_hint() || model()->keyword().empty() ||
          HasSelection() || GetCursorPosition() != 0)
        return false;
      model()->ClearKeyword();
      return true;

    default:
      break;
  }

  return HandleEarlyTabActions(event);
}

Project: Chrome

void OmniboxViewViews::Init() {
  set_controller(this);
  SetTextInputType(ui::TEXT_INPUT_TYPE_URL);
  GetRenderText()->SetElideBehavior(gfx::ELIDE_TAIL);

  if (popup_window_mode_)
    SetReadOnly(true);

  if (location_bar_view_) {
    popup_view_.reset(new OmniboxPopupContentsView(GetFontList(), this, model(),
                                                   location_bar_view_));
  }

  SetBorder(views::CreateEmptyBorder(
      ChromeLayoutProvider::Get()->GetInsetsMetric(INSETS_OMNIBOX)));

#if defined(OS_CHROMEOS)
  chromeos::input_method::InputMethodManager::Get()->
      AddCandidateWindowObserver(this);
#endif
}

Project: Chrome

bool OmniboxViewViews::IsCommandIdEnabled(int command_id) const {
  if (command_id == IDS_APP_PASTE)
    return !read_only() && !GetClipboardText().empty();
  if (command_id == IDS_PASTE_AND_GO)
    return !read_only() && model()->CanPasteAndGo(GetClipboardText());
  return Textfield::IsCommandIdEnabled(command_id) ||
         location_bar_view_->command_updater()->IsCommandEnabled(command_id);
}

Project: Chrome

OmniboxViewViews::OmniboxViewViews(OmniboxEditController* controller,
                                   std::unique_ptr<OmniboxClient> client,
                                   CommandUpdater* command_updater,
                                   bool popup_window_mode,
                                   LocationBarView* location_bar,
                                   const gfx::FontList& font_list)
    : OmniboxView(controller, std::move(client)),
      popup_window_mode_(popup_window_mode),
      security_level_(security_state::NONE),
      saved_selection_for_focus_change_(gfx::Range::InvalidRange()),
      ime_composing_before_change_(false),
      delete_at_end_pressed_(false),
      location_bar_view_(location_bar),
      ime_candidate_window_open_(false),
      select_all_on_mouse_release_(false),
      select_all_on_gesture_tap_(false),
      latency_histogram_state_(NOT_ACTIVE),
      scoped_observer_(this) {
  set_id(VIEW_ID_OMNIBOX);
  SetFontList(font_list);
}

Project: Chrome

void OmniboxViewViews::OnAfterCutOrCopy(ui::ClipboardType clipboard_type) {
  ui::Clipboard* cb = ui::Clipboard::GetForCurrentThread();
  base::string16 selected_text;
  cb->ReadText(clipboard_type, &selected_text);
  GURL url;
  bool write_url;
  model()->AdjustTextForCopy(GetSelectedRange().GetMin(), IsSelectAll(),
                             &selected_text, &url, &write_url);
  if (IsSelectAll())
    UMA_HISTOGRAM_COUNTS(OmniboxEditModel::kCutOrCopyAllTextHistogram, 1);

  ui::ScopedClipboardWriter scoped_clipboard_writer(clipboard_type);
  if (write_url)
    scoped_clipboard_writer.WriteURL(selected_text);
  else
    scoped_clipboard_writer.WriteText(selected_text);
}

Project: Chrome

bool OmniboxViewViews::OnAfterPossibleChange(bool allow_keyword_ui_change) {
  State new_state;
  GetState(&new_state);
  OmniboxView::StateChanges state_changes =
      GetStateChanges(state_before_change_, new_state);

  state_changes.text_differs =
      state_changes.text_differs ||
      (ime_composing_before_change_ != IsIMEComposing());

  const bool something_changed = model()->OnAfterPossibleChange(
      state_changes, allow_keyword_ui_change && !IsIMEComposing());

  if (something_changed &&
      (state_changes.text_differs || state_changes.keyword_differs))
    TextChanged();
  else if (state_changes.selection_differs)
    EmphasizeURLComponents();
  else if (delete_at_end_pressed_)
    model()->OnChanged();

  return something_changed;
}

Project: Chrome

void OmniboxViewViews::OnBeforePossibleChange() {
  GetState(&state_before_change_);
  ime_composing_before_change_ = IsIMEComposing();
}

Project: Chrome

void OmniboxViewViews::OnBlur() {
  saved_selection_for_focus_change_ = GetSelectedRange();

  views::Textfield::OnBlur();
  model()->OnWillKillFocus();

  if (!model()->user_input_in_progress() && model()->popup_model() &&
      model()->popup_model()->IsOpen() && text() != model()->PermanentText())
    RevertAll();
  else
    CloseOmniboxPopup();

  model()->OnKillFocus();

  SelectRange(gfx::Range(0));

  gfx::RenderText* render_text = GetRenderText();
  render_text->SetElideBehavior(gfx::ELIDE_TAIL);
  render_text->SetDisplayOffset(0);

  if (location_bar_view_) {
    if (model()->is_keyword_hint())
      location_bar_view_->Layout();

    location_bar_view_->SchedulePaint();
  }
}

Project: Chrome

void OmniboxViewViews::OnCompositingLockStateChanged(
    ui::Compositor* compositor) {}

Project: Chrome

void OmniboxViewViews::OnCompositingShuttingDown(ui::Compositor* compositor) {
  scoped_observer_.RemoveAll();
}

Project: Chrome

void OmniboxViewViews::OnFocus() {
  views::Textfield::OnFocus();
  model()->OnSetFocus(false);

  if (saved_selection_for_focus_change_.IsValid()) {
    SelectRange(saved_selection_for_focus_change_);
    saved_selection_for_focus_change_ = gfx::Range::InvalidRange();
  }

  GetRenderText()->SetElideBehavior(gfx::NO_ELIDE);

  if (model()->is_keyword_hint())
    location_bar_view_->Layout();

#if BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
  if (controller()->GetToolbarModel()->ShouldDisplayURL()) {
    feature_engagement::NewTabTrackerFactory::GetInstance()
        ->GetForProfile(location_bar_view_->profile())
        ->OnOmniboxFocused();
  }
#endif
}

Project: Chrome

bool OmniboxViewViews::OnMouseDragged(const ui::MouseEvent& event) {
  if (HasTextBeingDragged())
    CloseOmniboxPopup();

  bool handled = views::Textfield::OnMouseDragged(event);

  if (HasSelection() ||
      ExceededDragThreshold(event.location() - GetLastClickLocation())) {
    select_all_on_mouse_release_ = false;
  }

  return handled;
}

Project: Chrome

void OmniboxViewViews::OnNativeThemeChanged(const ui::NativeTheme* theme) {
  views::Textfield::OnNativeThemeChanged(theme);
  if (location_bar_view_) {
    SetBackgroundColor(
        location_bar_view_->GetColor(LocationBarView::BACKGROUND));
  }
  EmphasizeURLComponents();
}

Project: Chrome

void OmniboxViewViews::OnPaste() {
  const base::string16 text(GetClipboardText());
  if (!text.empty()) {
    OnBeforePossibleChange();
    model()->OnPaste();
    state_before_change_.text.clear();
    InsertOrReplaceText(text);
    OnAfterPossibleChange(true);
  }
}

Project: Chrome

void OmniboxViewViews::OnTabChanged(const content::WebContents* web_contents) {
  UpdateSecurityLevel();
  const OmniboxState* state = static_cast<OmniboxState*>(
      web_contents->GetUserData(&OmniboxState::kKey));
  model()->RestoreState(state ? &state->model_state : NULL);
  if (state) {
    SelectRange(state->selection);
    saved_selection_for_focus_change_ = state->saved_selection_for_focus_change;
  }

  ClearEditHistory();
}

Project: Chrome

void OmniboxViewViews::OnWriteDragData(ui::OSExchangeData* data) {
  GURL url;
  bool write_url;
  bool is_all_selected = IsSelectAll();
  base::string16 selected_text = GetSelectedText();
  model()->AdjustTextForCopy(GetSelectedRange().GetMin(), is_all_selected,
                             &selected_text, &url, &write_url);
  data->SetString(selected_text);
  if (write_url) {
    gfx::Image favicon;
    base::string16 title = selected_text;
    if (is_all_selected)
      model()->GetDataForURLExport(&url, &title, &favicon);
    button_drag_utils::SetURLAndDragImage(url, title, favicon.AsImageSkia(),
                                          nullptr, *GetWidget(), data);
    data->SetURL(url, title);
  }
}

Project: Chrome

void OmniboxViewViews::RemovedFromWidget() {
  views::Textfield::RemovedFromWidget();
  scoped_observer_.RemoveAll();
}

Project: Chrome

void OmniboxViewViews::ShowImeIfNeeded() {
  GetInputMethod()->ShowImeIfNeeded();
}

Project: Chrome

void OmniboxViewViews::UpdateContextMenu(ui::SimpleMenuModel* menu_contents) {
  int paste_position = menu_contents->GetIndexOfCommandId(IDS_APP_PASTE);
  DCHECK_GE(paste_position, 0);
  menu_contents->InsertItemWithStringIdAt(
      paste_position + 1, IDS_PASTE_AND_GO, IDS_PASTE_AND_GO);

  menu_contents->AddSeparator(ui::NORMAL_SEPARATOR);

  menu_contents->AddItemWithStringId(IDC_EDIT_SEARCH_ENGINES,
      IDS_EDIT_SEARCH_ENGINES);
}

Project: Chrome

void OmniboxViewViews::UpdatePopup() {
  model()->SetInputInProgress(true);
  if (!model()->has_focus())
    return;

  const gfx::Range sel = GetSelectedRange();
  model()->StartAutocomplete(!sel.is_empty(), sel.GetMax() < text().length());
}

Project: Chrome

void OmniboxViewViews::UpdateSecurityLevel() {
  security_level_ = controller()->GetToolbarModel()->GetSecurityLevel(false);
}