diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index fe58bc35e3eaa78ecbe417f021c6b60185ece80c..e8df9bb6d76cb278c89c7832643d3f83b392553d 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -1133,6 +1133,10 @@ each locale. -->
                desc="Title of the description column in the search engines editor">
         Name
       </message>
+      <message name="IDS_SEARCH_ENGINES_EDITOR_URL_COLUMN"
+               desc="Title of the URL column in the search engines editor">
+        URL
+      </message>
       <message name="IDS_SEARCH_ENGINES_EDITOR_DESCRIPTION_LABEL"
                desc="Prefix before the search engine description text field">
         Name:
@@ -6893,6 +6897,15 @@ Keep your key file in a safe place. You will need it to create new versions of y
                desc="The label of a link that brings up the keyword editor (aka search engine editor)">
         Manage
       </message>
+      <message name="IDS_SEARCH_ENGINE_ADD_NEW_NAME_PLACEHOLDER" desc="Placeholder text for name before the user adds a new search engine" >
+        Add a new search engine
+      </message>
+      <message name="IDS_SEARCH_ENGINE_ADD_NEW_KEYWORD_PLACEHOLDER" desc="Placeholder text for keyword before the user adds a new search engine" >
+        Keyword
+      </message>
+      <message name="IDS_SEARCH_ENGINE_ADD_NEW_URL_PLACEHOLDER" desc="Placeholder text for URL before the user adds a new search engine" >
+        URL with %s in place of query
+      </message>
 
       <if expr="not pp_ifdef('use_titlecase') or os != 'linux2'">
         <message name="IDS_OPTIONS_DEFAULTBROWSER_GROUP_NAME" desc="The title of the default browser group">
diff --git a/chrome/browser/dom_ui/options/search_engine_manager_handler.cc b/chrome/browser/dom_ui/options/search_engine_manager_handler.cc
index 78c8cd36709a71e2189ec0f36222e875b5f93364..1c211f6c18e78d3808bb84ff7948d278dc8ad3eb 100644
--- a/chrome/browser/dom_ui/options/search_engine_manager_handler.cc
+++ b/chrome/browser/dom_ui/options/search_engine_manager_handler.cc
@@ -54,34 +54,22 @@ void SearchEngineManagerHandler::GetLocalizedValues(
       l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_DESCRIPTION_COLUMN));
   localized_strings->SetString("searchEngineTableKeywordHeader",
       l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_KEYWORD_COLUMN));
-  localized_strings->SetString("addSearchEngineButton",
-      l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_NEW_BUTTON));
-  localized_strings->SetString("editSearchEngineButton",
+  localized_strings->SetString("searchEngineTableURLHeader",
       l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_EDIT_BUTTON));
   localized_strings->SetString("makeDefaultSearchEngineButton",
       l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_MAKE_DEFAULT_BUTTON));
-  // Overlay strings.
-  localized_strings->SetString("editSearchEngineTitle",
-      l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_EDIT_WINDOW_TITLE));
-  localized_strings->SetString("editSearchEngineNameLabel",
-      l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_DESCRIPTION_LABEL));
-  localized_strings->SetString("editSearchEngineKeywordLabel",
-      l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_KEYWORD_LABEL));
-  localized_strings->SetString("editSearchEngineURLLabel",
-      l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_URL_LABEL));
+  localized_strings->SetString("searchEngineTableNamePlaceholder",
+      l10n_util::GetStringUTF16(IDS_SEARCH_ENGINE_ADD_NEW_NAME_PLACEHOLDER));
+  localized_strings->SetString("searchEngineTableKeywordPlaceholder",
+      l10n_util::GetStringUTF16(IDS_SEARCH_ENGINE_ADD_NEW_KEYWORD_PLACEHOLDER));
+  localized_strings->SetString("searchEngineTableURLPlaceholder",
+      l10n_util::GetStringUTF16(IDS_SEARCH_ENGINE_ADD_NEW_URL_PLACEHOLDER));
   localized_strings->SetString("editSearchEngineInvalidTitleToolTip",
       l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_INVALID_TITLE_TT));
   localized_strings->SetString("editSearchEngineInvalidKeywordToolTip",
       l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_INVALID_KEYWORD_TT));
   localized_strings->SetString("editSearchEngineInvalidURLToolTip",
       l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_INVALID_URL_TT));
-  localized_strings->SetString("editSearchEngineURLExplanation",
-      l10n_util::GetStringUTF16(
-          IDS_SEARCH_ENGINES_EDITOR_URL_DESCRIPTION_LABEL));
-  localized_strings->SetString("editSearchEngineOkayButton",
-      l10n_util::GetStringUTF16(IDS_OK));
-  localized_strings->SetString("editSearchEngineCancelButton",
-      l10n_util::GetStringUTF16(IDS_CANCEL));
 }
 
 void SearchEngineManagerHandler::RegisterMessages() {
@@ -170,6 +158,8 @@ DictionaryValue* SearchEngineManagerHandler::CreateDictionaryForEngine(
   dict->SetString("keyword", table_model->GetText(
     index, IDS_SEARCH_ENGINES_EDITOR_KEYWORD_COLUMN));
   const TemplateURL* template_url = list_controller_->GetTemplateURL(index);
+  dict->SetString("url", WideToUTF16Hack(template_url->url()->DisplayURL()));
+  dict->SetBoolean("urlLocked", template_url->prepopulate_id() > 0);
   GURL icon_url = template_url->GetFavIconURL();
   if (icon_url.is_valid())
     dict->SetString("iconURL", icon_url.spec());
@@ -225,17 +215,6 @@ void SearchEngineManagerHandler::EditSearchEngine(const ListValue* args) {
     edit_url = list_controller_->GetTemplateURL(index);
   edit_controller_.reset(
       new EditSearchEngineController(edit_url, this, dom_ui_->GetProfile()));
-
-  if (edit_url) {
-    DictionaryValue engine_details;
-    engine_details.SetString("name", WideToUTF16Hack(edit_url->short_name()));
-    engine_details.SetString("keyword", WideToUTF16Hack(edit_url->keyword()));
-    engine_details.SetString("url",
-                             WideToUTF16Hack(edit_url->url()->DisplayURL()));
-    engine_details.SetBoolean("urlLocked", edit_url->prepopulate_id() > 0);
-    dom_ui_->CallJavascriptFunction(L"EditSearchEngineOverlay.setEditDetails",
-                                    engine_details);
-  }
 }
 
 void SearchEngineManagerHandler::OnEditedKeyword(
@@ -259,9 +238,11 @@ void SearchEngineManagerHandler::CheckSearchEngineInfoValidity(
   string16 name;
   string16 keyword;
   std::string url;
+  std::string modelIndex;
   if (!args->GetString(ENGINE_NAME, &name) ||
       !args->GetString(ENGINE_KEYWORD, &keyword) ||
-      !args->GetString(ENGINE_URL, &url)) {
+      !args->GetString(ENGINE_URL, &url) ||
+      !args->GetString(3, &modelIndex)) {
     NOTREACHED();
     return;
   }
@@ -270,8 +251,9 @@ void SearchEngineManagerHandler::CheckSearchEngineInfoValidity(
   validity.SetBoolean("name", edit_controller_->IsTitleValid(name));
   validity.SetBoolean("keyword", edit_controller_->IsKeywordValid(keyword));
   validity.SetBoolean("url", edit_controller_->IsURLValid(url));
+  StringValue indexValue(modelIndex);
   dom_ui_->CallJavascriptFunction(
-      L"EditSearchEngineOverlay.validityCheckCallback", validity);
+      L"SearchEngineManager.validityCheckCallback", validity, indexValue);
 }
 
 void SearchEngineManagerHandler::EditCancelled(const ListValue* args) {
diff --git a/chrome/browser/resources/options/edit_search_engine_overlay.css b/chrome/browser/resources/options/edit_search_engine_overlay.css
deleted file mode 100644
index fad104e4106402df2831f7fe7b9d38a9448da43c..0000000000000000000000000000000000000000
--- a/chrome/browser/resources/options/edit_search_engine_overlay.css
+++ /dev/null
@@ -1,36 +0,0 @@
-#editSearchEngineOverlay {
-  width: 500px;
-}
-
-#editSearchEngineOverlay table label {
-  text-align: end;
-}
-
-#editSearchEngineOverlay table,
-#editSearchEngineOverlay table td:nth-child(2) {
-  width: 100%;
-}
-
-#editSearchEngineOverlay table input {
-  width: 100%;
-  box-sizing: border-box;
-}
-
-#editSearchEngineOverlay .action-area {
-  margin-top: 2ex;
-}
-
-.valid-badge, .alert-badge {
-  width: 22px;
-  height: 21px;
-  background-position: 50% 1px;
-  background-repeat: no-repeat;
-}
-
-.valid-badge {
-  background-image: url("../../../../app/resources/input_good.png");
-}
-
-.alert-badge {
-  background-image: url("../../../app/theme/input_alert.png");
-}
diff --git a/chrome/browser/resources/options/edit_search_engine_overlay.html b/chrome/browser/resources/options/edit_search_engine_overlay.html
deleted file mode 100644
index 19c425adcac026d1581ff45332c305f5efd6937f..0000000000000000000000000000000000000000
--- a/chrome/browser/resources/options/edit_search_engine_overlay.html
+++ /dev/null
@@ -1,40 +0,0 @@
-<div class="page hidden" id="editSearchEngineOverlay">
-  <h1 i18n-content="editSearchEngineTitle"></h1>
-
-  <form id="editSearchEngineForm">
-    <table>
-      <tr>
-        <td><label for="editSearchEngineName"><span
-            i18n-content="editSearchEngineNameLabel"></span></label></td>
-        <td><input type="text" id="editSearchEngineName"></td>
-        <td><div id="editSearchEngineNameValidity"
-            class="alert-badge">&nbsp;</div></td>
-      </tr>
-      <tr>
-        <td><label for="editSearchEngineKeyword"><span
-            i18n-content="editSearchEngineKeywordLabel"></span></label></td>
-        <td><input type="text" id="editSearchEngineKeyword"></td>
-        <td><div id="editSearchEngineKeywordValidity"
-            class="alert-badge">&nbsp;</div></td>
-      </tr>
-      <tr>
-        <td><label for="editSearchEngineURL"><span
-            i18n-content="editSearchEngineURLLabel"></span></label></td>
-        <td><input type="url" id="editSearchEngineURL"></td>
-        <td><div id="editSearchEngineURLValidity"
-            class="alert-badge">&nbsp;</div></td>
-      </tr>
-      <tr>
-        <td></td>
-        <td><span i18n-content="editSearchEngineURLExplanation"><span></td>
-        <td></td>
-    </table>
-
-    <div class="action-area button-strip">
-      <button type="reset"
-          i18n-content="editSearchEngineCancelButton"></button>
-      <button type="submit" id="editSearchEngineOkayButton" disabled
-          i18n-content="editSearchEngineOkayButton"></button>
-    </div>
-  </form>
-</div>
diff --git a/chrome/browser/resources/options/edit_search_engine_overlay.js b/chrome/browser/resources/options/edit_search_engine_overlay.js
deleted file mode 100644
index a4f72b5dbb9c8a8b7a4ff81c9b28837eb61e6738..0000000000000000000000000000000000000000
--- a/chrome/browser/resources/options/edit_search_engine_overlay.js
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-cr.define('options', function() {
-  const OptionsPage = options.OptionsPage;
-
-  /**
-   * EditSearchEngineOverlay class
-   * Encapsulated handling of the 'Edit Search Engine' overlay page.
-   * @class
-   * @constructor
-   */
-  function EditSearchEngineOverlay() {
-    OptionsPage.call(this, 'editSearchEngineOverlay',
-                     templateData.editSearchEngineTitle,
-                     'editSearchEngineOverlay');
-  }
-
-  cr.addSingletonGetter(EditSearchEngineOverlay);
-
-  EditSearchEngineOverlay.prototype = {
-    __proto__: OptionsPage.prototype,
-
-    /**
-     * Initializes the page.
-     */
-    initializePage: function() {
-      OptionsPage.prototype.initializePage.call(this);
-
-      var self = this;
-      var editForm = $('editSearchEngineForm');
-      editForm.onreset = function(e) {
-        chrome.send('searchEngineEditCancelled');
-        self.dismissOverlay_();
-      };
-      editForm.onsubmit = function(e) {
-        chrome.send('searchEngineEditCompleted', self.getInputFieldValues_());
-        self.dismissOverlay_();
-        return false;
-      };
-      var fieldIDs = ['editSearchEngineName',
-                      'editSearchEngineKeyword',
-                      'editSearchEngineURL'];
-      for (var i = 0; i < fieldIDs.length; i++) {
-        var field = $(fieldIDs[i]);
-        field.oninput = this.validateFields_.bind(this);
-        field.onkeydown = function(e) {
-          if (e.keyCode == 27)  // Esc
-            editForm.reset();
-        };
-      }
-    },
-
-    /**
-     * Clears any uncommited input, and dismisses the overlay.
-     * @private
-     */
-    dismissOverlay_: function() {
-      this.setEditDetails_();
-      OptionsPage.clearOverlays();
-    },
-
-    /**
-     * Fills the text fields from the given search engine.
-     * @private
-     */
-    setEditDetails_: function(engineDetails) {
-      if (engineDetails) {
-        $('editSearchEngineName').value = engineDetails['name'];
-        $('editSearchEngineKeyword').value = engineDetails['keyword'];
-        var urlField = $('editSearchEngineURL');
-        urlField.value = engineDetails['url'];
-        urlField.disabled = engineDetails['urlLocked'];
-        this.validateFields_();
-      } else {
-        $('editSearchEngineName').value = '';
-        $('editSearchEngineKeyword').value = '';
-        $('editSearchEngineURL').value = '';
-        var invalid = { name: false, keyword: false, url: false };
-        this.updateValidityWithResults_(invalid);
-      }
-    },
-
-    /**
-     * Starts the process of asynchronously validating the user input. Results
-     * will be reported to updateValidityWithResults_.
-     * @private
-     */
-    validateFields_: function() {
-      chrome.send('checkSearchEngineInfoValidity', this.getInputFieldValues_());
-    },
-
-    /**
-     * Sets the validation images and the enabled state of the Add button based
-     * on the current values of the text fields.
-     * @private
-     * @param {Object} The dictionary of validity states.
-     */
-    updateValidityWithResults_: function(validity) {
-      this.setBadgeValidity_($('editSearchEngineNameValidity'),
-                             validity['name'],
-                             'editSearchEngineInvalidTitleToolTip');
-      this.setBadgeValidity_($('editSearchEngineKeywordValidity'),
-                             validity['keyword'],
-                             'editSearchEngineInvalidKeywordToolTip');
-      this.setBadgeValidity_($('editSearchEngineURLValidity'),
-                             validity['url'],
-                             'editSearchEngineInvalidURLToolTip');
-      $('editSearchEngineOkayButton').disabled =
-          !(validity['name'] && validity['keyword'] && validity['url']);
-    },
-
-    /**
-     * Updates the state of the given validity indicator badge.
-     * @private
-     * @param {HTMLElement} The badge element to adjust.
-     * @param {boolean} Whether or not the badge should be set to the valid
-     *     state.
-     * @param {string} The tooltip string id for the invalid state.
-     */
-    setBadgeValidity_: function(element, isValid, tooltip_id) {
-      if (isValid) {
-        element.className = 'valid-badge';
-        element.title = '';
-      } else {
-        element.className = 'alert-badge';
-        element.title = localStrings.getString(tooltip_id);
-      }
-    },
-
-    /**
-     * Returns the input field values as an array suitable for passing to
-     * chrome.send. The order of the array is important.
-     * @private
-     * @return {array} The current input field values.
-     */
-    getInputFieldValues_: function() {
-      return [ $('editSearchEngineName').value,
-               $('editSearchEngineKeyword').value,
-               $('editSearchEngineURL').value ];
-    }
-  };
-
-  EditSearchEngineOverlay.setEditDetails = function(engineDetails) {
-    EditSearchEngineOverlay.getInstance().setEditDetails_(engineDetails);
-  };
-
-  EditSearchEngineOverlay.validityCheckCallback = function(validity) {
-    EditSearchEngineOverlay.getInstance().updateValidityWithResults_(validity);
-  };
-
-  // Export
-  return {
-    EditSearchEngineOverlay: EditSearchEngineOverlay
-  };
-
-});
diff --git a/chrome/browser/resources/options/inline_editable_list.js b/chrome/browser/resources/options/inline_editable_list.js
new file mode 100644
index 0000000000000000000000000000000000000000..ec0f4a472628a9ddb6bce505b682313f4a722479
--- /dev/null
+++ b/chrome/browser/resources/options/inline_editable_list.js
@@ -0,0 +1,210 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+cr.define('options', function() {
+  const DeletableItem = options.DeletableItem;
+  const DeletableItemList = options.DeletableItemList;
+
+  /**
+   * Creates a new list item with support for inline editing.
+   * @constructor
+   * @extends {options.DeletableListItem}
+   */
+  function InlineEditableItem() {
+    var el = cr.doc.createElement('div');
+    InlineEditableItem.decorate(el);
+    return el;
+  }
+
+  /**
+   * Decorates an element as a inline-editable list item. Note that this is
+   * a subclass of DeletableItem.
+   * @param {!HTMLElement} el The element to decorate.
+   */
+  InlineEditableItem.decorate = function(el) {
+    el.__proto__ = InlineEditableItem.prototype;
+    el.decorate();
+  };
+
+  InlineEditableItem.prototype = {
+    __proto__: DeletableItem.prototype,
+
+    /**
+     * Whether or not this item can be edited.
+     * @type {boolean}
+     * @private
+     */
+    editable_: true,
+
+    /**
+     * Whether or not the current edit should be considered cancelled, rather
+     * than committed, when editing ends.
+     * @type {boolean}
+     * @private
+     */
+    editCancelled_: true,
+
+    /** @inheritDoc */
+    decorate: function() {
+      DeletableItem.prototype.decorate.call(this);
+
+      this.addEventListener('keydown', this.handleKeyDown_.bind(this));
+    },
+
+    /** @inheritDoc */
+    selectionChanged: function() {
+      if (this.editable)
+        this.editing = this.selected;
+    },
+
+    /**
+     * Whether the user is currently editing the list item.
+     * @type {boolean}
+     */
+    get editing() {
+      return this.hasAttribute('editing');
+    },
+    set editing(editing) {
+      if (this.editing == editing)
+        return;
+
+      if (editing)
+        this.setAttribute('editing', '');
+      else
+        this.removeAttribute('editing');
+
+      if (editing) {
+        this.editCancelled_ = false;
+
+        cr.dispatchSimpleEvent(this, 'edit', true);
+
+        var focusElement = this.initialFocusElement;
+        // When this is called in response to the selectedChange event,
+        // the list grabs focus immediately afterwards. Thus we must delay
+        // our focus grab.
+        if (focusElement) {
+          window.setTimeout(function() {
+            focusElement.focus();
+            focusElement.select();
+          }, 50);
+        }
+      } else {
+        if (!this.editCancelled_ && this.hasBeenEdited() &&
+            this.currentInputIsValid) {
+          cr.dispatchSimpleEvent(this, 'commitedit', true);
+        } else {
+          cr.dispatchSimpleEvent(this, 'canceledit', true);
+        }
+      }
+    },
+
+    /**
+     * Whether the item is editable.
+     * @type {boolean}
+     */
+    get editable() {
+      return this.editable_;
+    },
+    set editable(editable) {
+      this.editable_ = editable;
+      if (!editable)
+        this.editing = false;
+    },
+
+    /**
+     * The HTML element that should have focus initially when editing starts.
+     * Should be overriden by subclasses.
+     * @type {HTMLElement}
+     */
+    get initialFocusElement() {
+      return null;
+    },
+
+    /**
+     * Whether the input in currently valid to submit. If this returns false
+     * when editing would be submitted, either editing will not be ended,
+     * or it will be cancelled, depending on the context.
+     * Can be overrided by subclasses to perform input validation.
+     */
+    get currentInputIsValid() {
+      return true;
+    },
+
+    /**
+     * Returns true if the item has been changed by an edit.
+     * Can be overrided by subclasses to return false when nothing has changed
+     * to avoid unnecessary commits.
+     */
+    hasBeenEdited: function() {
+      return true;
+    },
+
+    /**
+     * Called a key is pressed. Handles committing and cancelling edits.
+     * @param {Event} e The key down event.
+     * @private
+     */
+    handleKeyDown_: function(e) {
+      if (!this.editing)
+        return;
+
+      var endEdit = false;
+      switch (e.keyIdentifier) {
+        case 'U+001B':  // Esc
+          this.editCancelled_ = true;
+          endEdit = true;
+          break;
+        case 'Enter':
+          if (this.currentInputIsValid)
+            endEdit = true;
+          break;
+      }
+
+      if (endEdit) {
+        // Blurring will trigger the edit to end; see InlineEditableItemList.
+        this.ownerDocument.activeElement.blur();
+        // Make sure that handled keys aren't passed on and double-handled.
+        // (e.g., esc shouldn't both cancel an edit and close a subpage)
+        e.stopPropagation();
+      }
+    },
+  };
+
+  var InlineEditableItemList = cr.ui.define('list');
+
+  InlineEditableItemList.prototype = {
+    __proto__: DeletableItemList.prototype,
+
+    /** @inheritDoc */
+    decorate: function() {
+      DeletableItemList.prototype.decorate.call(this);
+      this.addEventListener('blur', this.handleBlur_.bind(this), true);
+    },
+
+    /**
+     * Called when an element in the list is blurred. Removes selection (thus
+     * ending edit) if focus moves outside the list.
+     * @param {Event} e The blur event.
+     * @private
+     */
+    handleBlur_: function(e) {
+      // When the blur event happens we do not know who is getting focus so we
+      // delay this a bit until we know if the new focus node is outside the
+      // list.
+      var list = this;
+      var doc = e.target.ownerDocument;
+      window.setTimeout(function() {
+        var activeElement = doc.activeElement;
+        if (!list.contains(activeElement))
+          list.selectionModel.unselectAll();
+      }, 50);
+    },
+  };
+
+  // Export
+  return {
+    InlineEditableItem: InlineEditableItem,
+    InlineEditableItemList: InlineEditableItemList,
+  };
+});
diff --git a/chrome/browser/resources/options/options.html b/chrome/browser/resources/options/options.html
index 2885d511b275d06e44c57cc1824b94515af5fcaf..2a17cc2a5849cd3c6bcb6e31c6c2bd767301b148 100644
--- a/chrome/browser/resources/options/options.html
+++ b/chrome/browser/resources/options/options.html
@@ -22,7 +22,6 @@
 <link rel="stylesheet" href="clear_browser_data.css">
 <link rel="stylesheet" href="content_settings.css">
 <link rel="stylesheet" href="cookies_view.css">
-<link rel="stylesheet" href="edit_search_engine_overlay.css">
 <link rel="stylesheet" href="password_manager.css">
 <link rel="stylesheet" href="password_manager_list.css">
 <link rel="stylesheet" href="personal_options.css">
@@ -60,6 +59,7 @@
 <script src="preferences.js"></script>
 <script src="pref_ui.js"></script>
 <script src="deletable_item_list.js"></script>
+<script src="inline_editable_list.js"></script>
 <script src="list_inline_header_selection_controller.js"></script>
 <script src="options_page.js"></script>
 <if expr="pp_ifdef('chromeos')">
@@ -116,7 +116,6 @@
 <script src="content_settings_ui.js"></script>
 <script src="cookies_tree.js"></script>
 <script src="cookies_view.js"></script>
-<script src="edit_search_engine_overlay.js"></script>
 <script src="font_settings.js"></script>
 <script src="font_settings_ui.js"></script>
 <script src="import_data_overlay.js"></script>
@@ -139,7 +138,6 @@
   <include src="alert_overlay.html">
   <include src="autofill_edit_address_overlay.html">
   <include src="autofill_edit_creditcard_overlay.html">
-  <include src="edit_search_engine_overlay.html">
   <include src="import_data_overlay.html">
   <include src="instant_confirm_overlay.html">
   <if expr="pp_ifdef('chromeos')">
diff --git a/chrome/browser/resources/options/options.js b/chrome/browser/resources/options/options.js
index d77347035658fe8b28d391ee03cdbc2c4eba22b5..c5fdf0722fa9ca244df546605e9c17dbfbf99d6e 100644
--- a/chrome/browser/resources/options/options.js
+++ b/chrome/browser/resources/options/options.js
@@ -14,7 +14,6 @@ var ContentSettings = options.ContentSettings;
 var ContentSettingsExceptionsArea =
     options.contentSettings.ContentSettingsExceptionsArea;
 var CookiesView = options.CookiesView;
-var EditSearchEngineOverlay = options.EditSearchEngineOverlay;
 var FontSettings = options.FontSettings;
 var ImportDataOverlay = options.ImportDataOverlay;
 var InstantConfirmOverlay = options.InstantConfirmOverlay;
@@ -124,7 +123,6 @@ function load() {
   OptionsPage.registerOverlay(AlertOverlay.getInstance());
   OptionsPage.registerOverlay(AutoFillEditAddressOverlay.getInstance());
   OptionsPage.registerOverlay(AutoFillEditCreditCardOverlay.getInstance());
-  OptionsPage.registerOverlay(EditSearchEngineOverlay.getInstance());
   OptionsPage.registerOverlay(ImportDataOverlay.getInstance());
   OptionsPage.registerOverlay(InstantConfirmOverlay.getInstance());
 
diff --git a/chrome/browser/resources/options/options_page.css b/chrome/browser/resources/options/options_page.css
index 6e04d759c9768cc6e8396f2944c8a1713641c97c..ce6fd746d074814026b9d67f9e573e1dfec045b4 100644
--- a/chrome/browser/resources/options/options_page.css
+++ b/chrome/browser/resources/options/options_page.css
@@ -417,7 +417,9 @@ list .deletable-item {
 }
 
 list .deletable-item > :first-child {
+  -webkit-box-align: center;
   -webkit-box-flex: 1;
+  -webkit-padding-end: 3px;
   display: -webkit-box;
 }
 
diff --git a/chrome/browser/resources/options/search_engine_manager.css b/chrome/browser/resources/options/search_engine_manager.css
index 1eac1c425293944ab10d371c2857fbe6e354b9e4..0a0523cb48ce88a223cd469ac15176874339f435 100644
--- a/chrome/browser/resources/options/search_engine_manager.css
+++ b/chrome/browser/resources/options/search_engine_manager.css
@@ -18,7 +18,7 @@
   border-top: 1px solid #d9d9d9;
 }
 
-#searchEngineList .heading .name {
+#searchEngineList .heading .name-column {
   font-weight: bold;
 }
 
@@ -31,24 +31,72 @@
   display: -webkit-box;
 }
 
-#searchEngineList .name {
+#searchEngineList .favicon {
+  padding: 1px 7px 0px 7px;
+  height: 16px;
+}
+
+#searchEngineList .name-column {
   box-sizing: border-box;
-  width: 50%;
+  width: 37%;
 }
 
-#searchEngineList .keyword {
-  -webkit-box-flex: 1;
+#searchEngineList .keyword-column {
+  width: 26%;
 }
 
-#searchEngineList > div:not(.heading) .keyword {
+#searchEngineList .url-column {
+  width: 37%;
+}
+
+#searchEngineList > div:not(.heading) .keyword-column,
+#searchEngineList > div:not(.heading) .url-column {
   color: #666666;
 }
 
+#searchEngineList .name-column,
+#searchEngineList .keyword-column,
+#searchEngineList .url-column {
+  -webkit-padding-end: 1ex;
+}
+
 #searchEngineList .default {
   font-weight: bold;
 }
 
-#searchEngineList .name, #searchEngineList .keyword {
+#searchEngineList .default .url-column {
+  font-weight: normal;
+}
+
+#searchEngineList .name-column {
+  display: -webkit-box;
+}
+
+#searchEngineList .name-column :last-child {
+  -webkit-box-flex: 1;
+}
+
+#searchEngineList input {
+  box-sizing: border-box;
+  margin: 0;
+  width: 100%;
+}
+
+#searchEngineList .static-text {
   overflow: hidden;
   text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+#searchEngineList > :not([editing]) [editmode=true] {
+  display: none;
+}
+
+#searchEngineList > [editing] [editmode=false] {
+  display: none;
+}
+
+#searchEngineList input.invalid {
+  /* TODO(stuartmorgan): Replace with actual badging */
+  background-color: pink;
 }
diff --git a/chrome/browser/resources/options/search_engine_manager.html b/chrome/browser/resources/options/search_engine_manager.html
index 58d1ab1f5aa14093da7d48a2794f0ecc7fc8ddf1..4d8946ef00cda0e6cdafeaebd511fd60cb57fde0 100644
--- a/chrome/browser/resources/options/search_engine_manager.html
+++ b/chrome/browser/resources/options/search_engine_manager.html
@@ -5,10 +5,6 @@
       <list id="searchEngineList"></list>
     </div>
     <div>
-      <div><button id="addSearchEngineButton"
-          i18n-content="addSearchEngineButton"></div>
-      <div><button id="editSearchEngineButton" disabled
-          i18n-content="editSearchEngineButton"></div>
       <div><button id="makeDefaultSearchEngineButton" disabled
           i18n-content="makeDefaultSearchEngineButton"></div>
     </div>
diff --git a/chrome/browser/resources/options/search_engine_manager.js b/chrome/browser/resources/options/search_engine_manager.js
index 7a56bad1e6731bc2a54404d966c5ab0e377325e0..9a99f574ead3c52fbb316ded650ddb5066d40586 100644
--- a/chrome/browser/resources/options/search_engine_manager.js
+++ b/chrome/browser/resources/options/search_engine_manager.js
@@ -37,23 +37,19 @@ cr.define('options', function() {
           this.selectionChanged_.bind(this));
 
       var self = this;
-      $('addSearchEngineButton').onclick = function(event) {
-        chrome.send('editSearchEngine', ["-1"]);
-        OptionsPage.showOverlay('editSearchEngineOverlay');
-      };
-      $('editSearchEngineButton').onclick = function(event) {
-        chrome.send('editSearchEngine', [self.selectedModelIndex_]);
-        OptionsPage.showOverlay('editSearchEngineOverlay');
+      // This is a temporary hack to allow the "Make Default" button to
+      // continue working despite the new list behavior of removing selection
+      // on focus loss.
+      // Once drag-and-drop is supported, so items can be moved into the default
+      // section, this button will go away entirely.
+      $('makeDefaultSearchEngineButton').onmousedown = function(event) {
+        self.pendingDefaultEngine_ = self.list_.selectedItem;
       };
       $('makeDefaultSearchEngineButton').onclick = function(event) {
         chrome.send('managerSetDefaultSearchEngine',
-                    [self.selectedModelIndex_]);
+                    [self.pendingDefaultEngine_['modelIndex']]);
+        self.pendingDefaultEngine_ = null;
       };
-
-      // Remove Windows-style accelerators from button labels.
-      // TODO(stuartmorgan): Remove this once the strings are updated.
-      $('addSearchEngineButton').textContent =
-          localStrings.getStringWithoutAccelerator('addSearchEngineButton');
     },
 
     /**
@@ -62,16 +58,11 @@ cr.define('options', function() {
      * @param {Array} engineList List of available search engines.
      */
     updateSearchEngineList_: function(engineList) {
-      this.list_.dataModel = new ArrayDataModel(engineList);
-    },
-
-    /**
-     * Returns the currently selected list item's underlying model index.
-     * @private
-     */
-    get selectedModelIndex_() {
-      var listIndex = this.list_.selectionModel.selectedIndex;
-      return this.list_.dataModel.item(listIndex)['modelIndex'];
+      var model = new ArrayDataModel(engineList);
+      model.push({
+        'modelIndex': '-1'
+      });
+      this.list_.dataModel = model;
     },
 
     /**
@@ -80,11 +71,7 @@ cr.define('options', function() {
      * @param {!cr.Event} e Event with change info.
      */
     selectionChanged_: function(e) {
-      var selectedIndex = this.list_.selectionModel.selectedIndex;
-      var engine = selectedIndex != -1 ?
-          this.list_.dataModel.item(selectedIndex) : null;
-
-      $('editSearchEngineButton').disabled = engine == null;
+      var engine = this.list_.selectedItem || this.pendingDefaultEngine_;
       $('makeDefaultSearchEngineButton').disabled =
           !(engine && engine['canBeDefault']);
     },
@@ -94,6 +81,11 @@ cr.define('options', function() {
     SearchEngineManager.getInstance().updateSearchEngineList_(engineList);
   };
 
+  SearchEngineManager.validityCheckCallback = function(validity, modelIndex) {
+    SearchEngineManager.getInstance().list_.validationComplete(validity,
+                                                               modelIndex);
+  };
+
   // Export
   return {
     SearchEngineManager: SearchEngineManager
diff --git a/chrome/browser/resources/options/search_engine_manager_engine_list.js b/chrome/browser/resources/options/search_engine_manager_engine_list.js
index 1e7d93e3b785b04805604310c19d3b722910b1d3..b375337c3ff3adacb85f3d6d12dd13eceefe2d6f 100644
--- a/chrome/browser/resources/options/search_engine_manager_engine_list.js
+++ b/chrome/browser/resources/options/search_engine_manager_engine_list.js
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 cr.define('options.search_engines', function() {
-  const DeletableItem = options.DeletableItem;
-  const DeletableItemList = options.DeletableItemList;
+  const InlineEditableItemList = options.InlineEditableItemList;
+  const InlineEditableItem = options.InlineEditableItem;
   const ListInlineHeaderSelectionController =
       options.ListInlineHeaderSelectionController;
 
@@ -31,46 +31,290 @@ cr.define('options.search_engines', function() {
   };
 
   SearchEngineListItem.prototype = {
-    __proto__: DeletableItem.prototype,
+    __proto__: InlineEditableItem.prototype,
+
+    /**
+     * Input field for editing the engine name.
+     * @type {HTMLElement}
+     * @private
+     */
+    nameField_: null,
+
+    /**
+     * Input field for editing the engine keyword.
+     * @type {HTMLElement}
+     * @private
+     */
+    keywordField_: null,
+
+    /**
+     * Input field for editing the engine url.
+     * @type {HTMLElement}
+     * @private
+     */
+    urlField_: null,
+
+    /**
+     * Whether or not this is a placeholder for adding an engine.
+     * @type {boolean}
+     * @private
+     */
+    isPlaceholder_: false,
+
+    /**
+     * Whether or not an input validation request is currently outstanding.
+     * @type {boolean}
+     * @private
+     */
+    waitingForValidation_: false,
+
+    /**
+     * Whether or not the current set of input is known to be valid.
+     * @type {boolean}
+     * @private
+     */
+    currentlyValid_: false,
 
     /** @inheritDoc */
     decorate: function() {
-      DeletableItem.prototype.decorate.call(this);
+      InlineEditableItem.prototype.decorate.call(this);
 
       var engine = this.searchEngine_;
 
-      if (engine['heading'])
+      if (engine['modelIndex'] == '-1') {
+        this.isPlaceholder_ = true;
+        engine['name'] = '';
+        engine['keyword'] = '';
+        engine['url'] = '';
+      }
+
+      this.currentlyValid_ = !this.isPlaceholder_;
+
+      if (engine['heading']) {
         this.classList.add('heading');
-      else if (engine['default'])
+        this.editable = false;
+      } else if (engine['default']) {
         this.classList.add('default');
+      }
 
       this.deletable = engine['canBeRemoved'];
 
-      var nameEl = this.ownerDocument.createElement('div');
-      nameEl.className = 'name';
+      var nameText = engine['name'];
+      var keywordText = engine['keyword'];
+      var urlText = engine['url'];
       if (engine['heading']) {
-        nameEl.textContent = engine['heading'];
-      } else {
-        nameEl.textContent = engine['name'];
-        nameEl.classList.add('favicon-cell');
-        nameEl.style.backgroundImage = url('chrome://favicon/iconurl/' +
-                                           engine['iconURL']);
+        nameText = engine['heading'];
+        keywordText = localStrings.getString('searchEngineTableKeywordHeader');
+        urlText = localStrings.getString('searchEngineTableURLHeader');
+      }
+
+      // Construct the name column.
+      var nameColEl = this.ownerDocument.createElement('div');
+      nameColEl.className = 'name-column';
+      this.contentElement.appendChild(nameColEl);
+
+      // For non-heading rows, start with a favicon.
+      if (!engine['heading']) {
+        var faviconDivEl = this.ownerDocument.createElement('div');
+        faviconDivEl.className = 'favicon';
+        var imgEl = this.ownerDocument.createElement('img');
+        imgEl.src = 'chrome://favicon/iconurl/' + engine['iconURL'];
+        faviconDivEl.appendChild(imgEl);
+        nameColEl.appendChild(faviconDivEl);
       }
-      this.contentElement.appendChild(nameEl);
 
-      var keywordEl = this.ownerDocument.createElement('div');
-      keywordEl.className = 'keyword';
-      keywordEl.textContent = engine['heading'] ?
-          localStrings.getString('searchEngineTableKeywordHeader') :
-          engine['keyword'];
+      var nameEl = this.createEditableTextCell_(nameText);
+      nameColEl.appendChild(nameEl);
+
+      // Then the keyword column.
+      var keywordEl = this.createEditableTextCell_(keywordText);
+      keywordEl.className = 'keyword-column';
       this.contentElement.appendChild(keywordEl);
+
+      // And the URL column.
+      var urlEl = this.createEditableTextCell_(urlText);
+      urlEl.className = 'url-column';
+      this.contentElement.appendChild(urlEl);
+
+      // Do final adjustment to the input fields.
+      if (!engine['heading']) {
+        this.nameField_ = nameEl.querySelector('input');
+        this.keywordField_ = keywordEl.querySelector('input');
+        this.urlField_ = urlEl.querySelector('input');
+
+        if (engine['urlLocked'])
+          this.urlField_.disabled = true;
+
+        if (this.isPlaceholder_) {
+          this.nameField_.placeholder =
+              localStrings.getString('searchEngineTableNamePlaceholder');
+          this.keywordField_.placeholder =
+              localStrings.getString('searchEngineTableKeywordPlaceholder');
+          this.urlField_.placeholder =
+              localStrings.getString('searchEngineTableURLPlaceholder');
+        }
+
+        var fields = [ this.nameField_, this.keywordField_, this.urlField_ ];
+          for (var i = 0; i < fields.length; i++) {
+          fields[i].oninput = this.startFieldValidation_.bind(this);
+        }
+      }
+
+      // Listen for edit events.
+      this.addEventListener('edit', this.onEditStarted_.bind(this));
+      this.addEventListener('canceledit', this.onEditCancelled_.bind(this));
+      this.addEventListener('commitedit', this.onEditCommitted_.bind(this));
+    },
+
+    /**
+     * Returns a div containing an <input>, as well as static text if needed.
+     * @param {string} text The text of the cell.
+     * @return {HTMLElement} The HTML element for the cell.
+     * @private
+     */
+    createEditableTextCell_: function(text) {
+      var container = this.ownerDocument.createElement('div');
+
+      if (!this.isPlaceholder_) {
+        var textEl = this.ownerDocument.createElement('div');
+        textEl.className = 'static-text';
+        textEl.textContent = text;
+        textEl.setAttribute('editmode', false);
+        container.appendChild(textEl);
+      }
+
+      var inputEl = this.ownerDocument.createElement('input');
+      inputEl.type = 'text';
+      inputEl.value = text;
+      if (!this.isPlaceholder_) {
+        inputEl.setAttribute('editmode', true);
+        inputEl.staticVersion = textEl;
+      }
+      container.appendChild(inputEl);
+
+      return container;
+    },
+
+    /** @inheritDoc */
+    get initialFocusElement() {
+      return this.nameField_;
+    },
+
+    /** @inheritDoc */
+    get currentInputIsValid() {
+      return !this.waitingForValidation_ && this.currentlyValid_;
+    },
+
+    /** @inheritDoc */
+    hasBeenEdited: function(e) {
+      var engine = this.searchEngine_;
+      return this.nameField_.value != engine['name'] ||
+             this.keywordField_.value != engine['keyword'] ||
+             this.urlField_.value != engine['url'];
+    },
+
+    /**
+     * Called when entering edit mode; starts an edit session in the model.
+     * @param {Event} e The edit event.
+     * @private
+     */
+    onEditStarted_: function(e) {
+      var editIndex = this.searchEngine_['modelIndex'];
+      chrome.send('editSearchEngine', [String(editIndex)]);
+    },
+
+    /**
+     * Called when committing an edit; updates the model.
+     * @param {Event} e The end event.
+     * @private
+     */
+    onEditCommitted_: function(e) {
+      chrome.send('searchEngineEditCompleted', this.getInputFieldValues_());
+      // Update the static version immediately to prevent flickering before
+      // the model update callback updates the UI.
+      var editFields = [ this.nameField_, this.keywordField_, this.urlField_ ];
+      for (var i = 0; i < editFields.length; i++) {
+        var staticLabel = editFields[i].staticVersion;
+        if (staticLabel)
+          staticLabel.textContent = editFields[i].value;
+      }
+    },
+
+    /**
+     * Called when cancelling an edit; informs the model and resets the control
+     * states.
+     * @param {Event} e The cancel event.
+     * @private
+     */
+    onEditCancelled_: function() {
+      chrome.send('searchEngineEditCancelled');
+      var engine = this.searchEngine_;
+      this.nameField_.value = engine['name'];
+      this.keywordField_.value = engine['keyword'];
+      this.urlField_.value = engine['url'];
+
+      var editFields = [ this.nameField_, this.keywordField_, this.urlField_ ];
+      for (var i = 0; i < editFields.length; i++) {
+        editFields[i].classList.remove('invalid');
+      }
+      this.currentlyValid_ = !this.isPlaceholder_;
+    },
+
+    /**
+     * Returns the input field values as an array suitable for passing to
+     * chrome.send. The order of the array is important.
+     * @private
+     * @return {array} The current input field values.
+     */
+    getInputFieldValues_: function() {
+      return [ this.nameField_.value,
+               this.keywordField_.value,
+               this.urlField_.value ];
+    },
+
+    /**
+     * Begins the process of asynchronously validing the input fields.
+     * @private
+     */
+    startFieldValidation_: function() {
+      this.waitingForValidation_ = true;
+      var args = this.getInputFieldValues_();
+      args.push(this.searchEngine_['modelIndex']);
+      chrome.send('checkSearchEngineInfoValidity', args);
+    },
+
+    /**
+     * Callback for the completion of an input validition check.
+     * @param {Object} validity A dictionary of validitation results.
+     */
+    validationComplete: function(validity) {
+      this.waitingForValidation_ = false;
+      // TODO(stuartmorgan): Implement the full validation UI with
+      // checkmark/exclamation mark icons and tooltips.
+      if (validity['name'])
+        this.nameField_.classList.remove('invalid');
+      else
+        this.nameField_.classList.add('invalid');
+
+      if (validity['keyword'])
+        this.keywordField_.classList.remove('invalid');
+      else
+        this.keywordField_.classList.add('invalid');
+
+      if (validity['url'])
+        this.urlField_.classList.remove('invalid');
+      else
+        this.urlField_.classList.add('invalid');
+
+      this.currentlyValid_ = validity['name'] && validity['keyword'] &&
+          validity['url'];
     },
   };
 
   var SearchEngineList = cr.ui.define('list');
 
   SearchEngineList.prototype = {
-    __proto__: DeletableItemList.prototype,
+    __proto__: InlineEditableItemList.prototype,
 
     /** @inheritDoc */
     createItem: function(searchEngine) {
@@ -95,6 +339,20 @@ cr.define('options.search_engines', function() {
     canSelectIndex: function(index) {
       return !this.dataModel.item(index).hasOwnProperty('heading');
     },
+
+    /**
+     * Passes the results of an input validation check to the requesting row
+     * if it's still being edited.
+     * @param {number} modelIndex The model index of the item that was checked.
+     * @param {Object} validity A dictionary of validitation results.
+     */
+    validationComplete: function(validity, modelIndex) {
+      // If it's not still being edited, it no longer matters.
+      var currentSelection = this.selectedItem;
+      var listItem = this.getListItem(currentSelection);
+      if (listItem.editing && currentSelection['modelIndex'] == modelIndex)
+        listItem.validationComplete(validity);
+    },
   };
 
   // Export
diff --git a/chrome/browser/resources/shared/js/cr/ui/list_single_selection_model.js b/chrome/browser/resources/shared/js/cr/ui/list_single_selection_model.js
index f68818c140b47e69fef80f77a5630532d665538f..ff3c8c64a5616cc40c3092206b853c16983adc06 100644
--- a/chrome/browser/resources/shared/js/cr/ui/list_single_selection_model.js
+++ b/chrome/browser/resources/shared/js/cr/ui/list_single_selection_model.js
@@ -140,7 +140,6 @@ cr.define('cr.ui', function() {
         if (this.selectedIndexBefore_ != this.selectedIndex_) {
           var e = new Event('change');
           var indexes = [this.selectedIndexBefore_, this.selectedIndex_];
-          indexes.sort();
           e.changes = indexes.filter(function(index) {
             return index != -1;
           }).map(function(index) {